3 # amcrypt-ossl-asym.sh - asymmetric crypto helper using OpenSSL
4 # Usage: amcrypt-ossl-asym.sh [-d]
6 # Copyright © 2006 Ben Slusky <sluskyb@paranoiacs.org>
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 # Keys can be generated with the standard OpenSSL commands, e.g.:
25 # $ openssl genrsa -aes128 -out backup-privkey.pem 1024
26 # Generating RSA private key, 1024 bit long modulus
28 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
29 # Verifying - Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
31 # $ openssl rsa -in backup-privkey.pem -pubout -out backup-pubkey.pem
32 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
37 exec_prefix="@exec_prefix@"
39 amlibexecdir="@amlibexecdir@"
40 . "${amlibexecdir}/amanda-sh-lib.sh"
42 # change these as needed
43 OPENSSL= # whatever's in $PATH
44 CIPHER=aes-256-cbc # see `openssl help` for more ciphers
45 AMANDA_HOME=~@CLIENT_LOGIN@
46 RANDFILE=$AMANDA_HOME/.rnd
48 PASSPHRASE=$AMANDA_HOME/.am_passphrase # optional
49 PRIVKEY=$AMANDA_HOME/backup-privkey.pem
50 PUBKEY=$AMANDA_HOME/backup-pubkey.pem
52 # where might openssl be?
53 PATH=/bin:/usr/bin:/usr/local/bin:/usr/ssl/bin:/usr/local/ssl/bin:/opt/csw/bin
55 MAGIC='AmAnDa+OpEnSsL'
57 WORKDIR="/tmp/.${ME}.$$"
61 if [ -z "${OPENSSL:=`which openssl`}" ]; then
62 echo `_ '%s: %s not found' "${ME}" "openssl"` >&2
64 elif [ ! -x "${OPENSSL}" ]; then
65 echo `_ "%s: can't execute %s (%s)" "${ME}" "openssl" "${OPENSSL}"` >&2
69 if [ -n "${PASSPHRASE}" ]; then
70 # check the openssl version. if it's too old, we have to handle
71 # the pass phrase differently.
72 OSSL_VERSION=`eval \"${OPENSSL}\" version |cut -d\ -f2`
73 case "${OSSL_VERSION}" in
74 ''|0.[0-8].*|0.9.[0-6]*|0.9.7|0.9.7[a-c]*)
75 echo `_ '%s: %s is version %s' "${ME}" "${OPENSSL}" "${OSSL_VERSION}"` >&2
76 echo `_ '%s: Using pass phrase kluge for OpenSSL version >=0.9.7d' "${ME}"` >&2
82 mkdir -m 700 "${WORKDIR}"
84 echo `_ '%s: failed to create temp directory' "${ME}"` >&2
89 trap "rm -rf \"${WORKDIR}\"" 0 1 3 15
91 # we'll need to pad the datastream to a multiple of the cipher block size
92 # prior to encryption and decryption. 96 bytes (= 768 bits) should be good
95 perl -pe 'BEGIN { $bs = 96; $/ = \8192 } $nbytes = ($nbytes + length) % $bs; END { print "\0" x ($bs - $nbytes) }'
99 # generate a random printable cipher key (on one line)
100 echo `"${OPENSSL}" rand -base64 80` >"${WORKDIR}/pass"
102 # encrypt the cipher key using the RSA public key
103 "${OPENSSL}" rsautl -encrypt -in "${WORKDIR}/pass" -out "${WORKDIR}/pass.ciphertext" -pubin -inkey "${PUBKEY}" -pkcs
104 [ $? -eq 0 ] || return 1
107 printf "%s" "${MAGIC}"
109 # print the encrypted cipher key, preceded by size
110 ls -l "${WORKDIR}/pass.ciphertext" | awk '{ printf("%-10d", $5) }'
111 cat "${WORKDIR}/pass.ciphertext"
113 # encrypt data using the cipher key and print
114 pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -e -pass "file:${WORKDIR}/pass" -nosalt
115 [ $? -eq 0 ] || return 1
120 magicsize=`printf "%s" "${MAGIC}" | wc -c | sed 's/^ *//'`
121 magic=`dd bs=$magicsize count=1 2>/dev/null`
122 if [ "$magic" != "${MAGIC}" ]; then
123 echo `_ '%s: bad magic' "${ME}"` >&2
127 # read size of encrypted cipher key
128 n=`dd bs=10 count=1 2>/dev/null`
129 [ $n -gt 0 ] 2>/dev/null
130 if [ $? -ne 0 ]; then
131 echo `_ '%s: bad header' "${ME}"` >&2
135 # read the encrypted cipher key
136 dd "of=${WORKDIR}/pass.ciphertext" bs=$n count=1 2>/dev/null
138 # decrypt the cipher key using the RSA private key
139 if [ "${PASS_FROM_STDIN}" = yes ]; then
140 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" -pkcs < "${PASSPHRASE}"
142 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" ${PASSARG} -pkcs 3< "${PASSPHRASE}"
144 [ $? -eq 0 ] || return 1
146 # use the cipher key to decrypt data
147 pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -d -pass "file:${WORKDIR}/pass" -nosalt
149 # N.B.: in the likely event that we're piping to gzip, the above command
150 # may return a spurious error if gzip closes the output stream early.
154 if [ "$1" = -d ]; then
155 if [ -z "${PRIVKEY}" ]; then
156 echo `_ '%s: must specify private key for decryption' "${ME}"` >&2
158 elif [ ! -r "${PRIVKEY}" ]; then
159 echo `_ "%s: can't read private key from %s" "${ME}" "${PRIVKEY}"` >&2
163 if [ -n "${PASSPHRASE}" -a -e "${PASSPHRASE}" -a -r "${PASSPHRASE}" ]; then
164 PASSARG='-passin fd:3'
170 if [ $? -ne 0 ]; then
171 echo `_ '%s: decryption failed' "${ME}"` >&2
175 if [ -z "${PUBKEY}" ]; then
176 echo `_ '%s: must specify public key for encryption' "${ME}"` >&2
178 elif [ ! -r "${PUBKEY}" ]; then
179 echo `_ "%s: can't read public key from %s" "${ME}" "${PUBKEY}"` >&2
184 if [ $? -ne 0 ]; then
185 echo `_ '%s: encryption failed' "${ME}"` >&2