#!@SHELL@ # # amcrypt-ossl-asym.sh - asymmetric crypto helper using OpenSSL # Usage: amcrypt-ossl-asym.sh [-d] # # Copyright © 2006 Ben Slusky # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # # Keys can be generated with the standard OpenSSL commands, e.g.: # # $ openssl genrsa -aes128 -out backup-privkey.pem 1024 # Generating RSA private key, 1024 bit long modulus # [...] # Enter pass phrase for backup-privkey.pem: # Verifying - Enter pass phrase for backup-privkey.pem: # # $ openssl rsa -in backup-privkey.pem -pubout -out backup-pubkey.pem # Enter pass phrase for backup-privkey.pem: # Writing RSA key # # change these as needed OPENSSL= # whatever's in $PATH CIPHER=aes-256-cbc # see `openssl help` for more ciphers AMANDA_HOME=~backup RANDFILE=$AMANDA_HOME/.rnd export RANDFILE PASSPHRASE=$AMANDA_HOME/.am_passphrase # optional PRIVKEY=$AMANDA_HOME/backup-privkey.pem PUBKEY=$AMANDA_HOME/backup-pubkey.pem # where might openssl be? PATH=/bin:/usr/bin:/usr/local/bin:/usr/ssl/bin:/usr/local/ssl/bin export PATH MAGIC='AmAnDa+OpEnSsL' ME=`basename "$0"` WORKDIR="/tmp/.${ME}.$$" # first things first if [ -z "${OPENSSL:=`which openssl`}" ]; then echo "${ME}: openssl not found" >&2 exit 1 elif [ ! -x "${OPENSSL}" ]; then echo "${ME}: can't execute openssl (${OPENSSL})" >&2 exit 1 fi if [ -n "${PASSPHRASE}" ]; then # check the openssl version. if it's too old, we have to handle # the pass phrase differently. OSSL_VERSION=`eval \"${OPENSSL}\" version |cut -d\ -f2` case "${OSSL_VERSION}" in ''|0.[0-8].*|0.9.[0-6]*|0.9.7|0.9.7[a-c]*) echo "${ME}: ${OPENSSL} is version ${OSSL_VERSION}" >&2 echo "${ME}: Using pass phrase kluge for OpenSSL version >=0.9.7d" >&2 PASS_FROM_STDIN=yes ;; esac fi mkdir -m 700 "${WORKDIR}" if [ $? -ne 0 ]; then echo "${ME}: failed to create temp directory" >&2 exit 1 fi # ignore SIGINT trap "" 2 trap "rm -rf \"${WORKDIR}\"" 0 1 3 15 # we'll need to pad the datastream to a multiple of the cipher block size # prior to encryption and decryption. 96 bytes (= 768 bits) should be good # for any cipher. pad() { perl -pe 'BEGIN { $bs = 96; $/ = \8192 } $nbytes = ($nbytes + length) % $bs; END { print "\0" x ($bs - $nbytes) }' } encrypt() { # generate a random printable cipher key (on one line) echo `"${OPENSSL}" rand -base64 80` >"${WORKDIR}/pass" # encrypt the cipher key using the RSA public key "${OPENSSL}" rsautl -encrypt -in "${WORKDIR}/pass" -out "${WORKDIR}/pass.ciphertext" -pubin -inkey "${PUBKEY}" -pkcs [ $? -eq 0 ] || return 1 # print magic printf %s "${MAGIC}" # print the encrypted cipher key, preceded by size ls -l "${WORKDIR}/pass.ciphertext" | awk '{ printf("%-10d", $5) }' cat "${WORKDIR}/pass.ciphertext" # encrypt data using the cipher key and print pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -e -pass "file:${WORKDIR}/pass" -nosalt [ $? -eq 0 ] || return 1 } decrypt() { # read magic magicsize=`printf %s "${MAGIC}" | wc -c | sed 's/^ *//'` magic=`dd bs=$magicsize count=1 2>/dev/null` if [ "$magic" != "${MAGIC}" ]; then echo "${ME}: bad magic" >&2 return 1 fi # read size of encrypted cipher key n=`dd bs=10 count=1 2>/dev/null` [ $n -gt 0 ] 2>/dev/null if [ $? -ne 0 ]; then echo "${ME}: bad header" >&2 return 1 fi # read the encrypted cipher key dd "of=${WORKDIR}/pass.ciphertext" bs=$n count=1 2>/dev/null # decrypt the cipher key using the RSA private key if [ "${PASS_FROM_STDIN}" = yes ]; then "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" -pkcs < "${PASSPHRASE}" else "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" ${PASSARG} -pkcs 3< "${PASSPHRASE}" fi [ $? -eq 0 ] || return 1 # use the cipher key to decrypt data pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -d -pass "file:${WORKDIR}/pass" -nosalt # N.B.: in the likely event that we're piping to gzip, the above command # may return a spurious error if gzip closes the output stream early. return 0 } if [ "$1" = -d ]; then if [ -z "${PRIVKEY}" ]; then echo "${ME}: must specify private key for decryption" >&2 exit 1 elif [ ! -r "${PRIVKEY}" ]; then echo "${ME}: can't read private key from ${PRIVKEY}" >&2 exit 1 fi if [ -n "${PASSPHRASE}" -a -e "${PASSPHRASE}" -a -r "${PASSPHRASE}" ]; then PASSARG='-passin fd:3' else PASSPHRASE=/dev/null fi decrypt if [ $? -ne 0 ]; then echo "${ME}: decryption failed" >&2 exit 1 fi else if [ -z "${PUBKEY}" ]; then echo "${ME}: must specify public key for encryption" >&2 exit 1 elif [ ! -r "${PUBKEY}" ]; then echo "${ME}: can't read public key from ${PUBKEY}" >&2 exit 1 fi encrypt if [ $? -ne 0 ]; then echo "${ME}: encryption failed" >&2 exit 1 fi fi