Skip to content

Instantly share code, notes, and snippets.

@u8sand
Created September 1, 2022 21:29
Show Gist options
  • Save u8sand/53b48f0266f1bbff6d2ee50d79601de8 to your computer and use it in GitHub Desktop.
Save u8sand/53b48f0266f1bbff6d2ee50d79601de8 to your computer and use it in GitHub Desktop.
Use ssh keys & github for quick and easy crypto.
#!/bin/bash
#
# A helper for using ssh keys & github for encrypting/decrypting messages intended for specific recipients.
# Installation (assumes ~/.local/sbin/ is in your PATH, put it wherever you want):
# 1. Download script
# 2. mv ssh-crypt.sh ~/.local/sbin/ssh-crypt
# 3. chmod +x ~/.local/sbin/ssh-crypt
#
# See `ssh-crypt help` for command information.
_help() {
COMMAND=$1
case "${COMMAND}" in
help)
echo "Usage: $0 ${COMMAND} [COMMAND]"
echo
echo " Get help about one of the subcommands"
;;
pubkey-from-github)
echo "Usage: $0 ${COMMAND} [github-username-1 github-username-2 ...]"
echo
echo " Fetch the public key(s) from a user on github, these can be used to encrypt messages intended for that user."
;;
encrypt)
echo "Usage: $0 ${COMMAND} <pubkey-1 pubkey-2 ...> < file > file.enc.tar"
echo
echo " Encrypts a message or file on stdin, and writes encrypted bundle to stdout."
echo " the bundle can be decrypted with decrypt."
echo
echo "Arguments:"
echo " pubkey-n The public key(s) of your intended recipient(s)"
echo " can often be obtained through github with pubkey-from-github command"
echo " e.g. $0 ${command} \<($0 pubkey-from-github username) < file > file.enc.tar"
echo
echo "Details:"
echo " The bundle contains a symetric decryption key for the message, and"
echo " the key encrypted using all provided public keys."
;;
decrypt)
echo "Usage: $0 ${COMMAND} [privkey] < file.enc.tar > file"
echo
echo " Decrypts an encrypted bundle from stdin (created with encrypt), and writes"
echo " decrypted message/file to stdout."
echo
echo "Arguments:"
echo " privkey The private key of the public key used to encrypt the message."
echo " defaults to ~/.ssh/id_rsa"
;;
encrypt-short)
echo "Usage: $0 ${COMMAND} <pubkey> < file > file.enc"
echo
echo " Encrypts a message on stdin, and writes encrypted message to stdout."
echo
echo "Arguments:"
echo " pubkey The public key of your intended recipient. Since users can have multiple"
echo " public keys on github, you should prefer encrypt unless you have their pubkey."
echo
echo "Details:"
echo " This uses the keys directly without a intermediary key, this means only one public key"
echo " can be used, and the message should not be too large."
;;
decrypt-short)
echo "Usage: $0 ${COMMAND} [privkey] < file.enc.tar > file"
echo
echo " Decrypts an encrypted message from stdin (created with encrypt-short), and writes"
echo " decrypted message to stdout."
echo
echo "Arguments:"
echo " privkey The private key of the public key used to encrypt the message."
echo " defaults to ~/.ssh/id_rsa"
;;
*)
echo "Usage: $0 COMMAND [ARGS]..."
echo
echo " Use ssh keys & github for quick and easy crypto."
echo
echo "Commands:"
echo " help Get help about one of the commands."
echo " pubkey-from-github Fetch the public key of a user on github for message encryption."
echo " encrypt Encryption a message or file, decryptable by your specified recipient(s)."
echo " decrypt Decrypt a message or file sent to you."
echo " encrypt-short Encryption a short message, decryptable by your specified recipient."
echo " decrypt-short Decrypt a short message sent to you."
;;
esac
}
# This ensures the private key is available in pem format
_ensure_privkey_pem() {
privkey="$1"
if [[ -z "${privkey}" ]]; then
privkey="$HOME/.ssh/id_rsa"
fi
if [[ "$(basename ${privkey} .key)" == "$(basename ${privkey})" ]]; then
if [[ ! -e "${privkey}.key" ]]; then
echo "creating openssl compatible key file from ${privkey}..." >> /dev/stderr
cp "${privkey}" "${privkey}.key"
ssh-keygen -f "${privkey}.key" -p -m pem
fi
privkey="${privkey}.key"
fi
echo "${privkey}"
}
# This extracts pubkeys from github for user(s)
pubkey_from_github() {
if [[ -z "$1" ]]; then
echo "Missing username argument" >> /dev/stderr;
return 1;
fi
while [[ ! -z "$1" ]]; do
pubkeys="$(curl -s https://github.com/$1.keys)"
if [[ -z "${pubkeys}" ]]; then
echo "$1 has no public keys" >> /dev/stderr;
return 1;
fi;
if [[ "${pubkeys}" == "Not Found" ]]; then
echo "$1 was not found on github" >> /dev/stderr;
return 1;
fi;
echo "${pubkeys}"
shift;
done
}
# encrypt a file with an intermediary key, store the encrypted key & message in a tarball. Encrypted key
# is encrypted using any/all pubkeys provided, allowing you to send to multiple recipients or simply a user
# with multiple public keys
encrypt_full() {
pubkeys="$1"
if [[ -z "${pubkeys}" ]]; then
echo "Missing pubkeys argument"
return 1
fi
tmpdir="$(mktemp -d)"
openssl rand -base64 -out "${tmpdir}/key" 32
let n=0
echo "${pubkeys}" | while IFS= read -r pubkey; do
echo "encrypting key-${n}" >> /dev/stderr;
ssh-keygen -f <(echo "${pubkey}") -e -m pkcs8 > "${tmpdir}/${n}.pkcs8.pub";
openssl rsautl -encrypt -pubin -inkey "${tmpdir}/${n}.pkcs8.pub" -in "${tmpdir}/key" -out "${tmpdir}/key-${n}.enc";
let n="$n+1";
done
echo "encrypting message" >> /dev/stderr;
openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt -in - -out "${tmpdir}/message.enc" -pass file:"${tmpdir}/key"
echo "bundling..." >> /dev/stderr;
cwd=$(pwd); cd "${tmpdir}"; tar cf - key-*.enc message.enc; cd "${cwd}"
rm -r "${tmpdir}"
}
# decrypt a file which was encrypted with encrypt_full, attempt to decrypt the intermediary key(s)
# the an ssh private key, if successful, use that key to decrypt the message.
decrypt_full() {
privkey="$(_ensure_privkey_pem $1)"
tmpdir="$(mktemp -d)"
tar xf - -C "${tmpdir}"
ls "${tmpdir}"/key-*.enc | while read keyfile_enc; do
openssl rsautl -decrypt -inkey "${privkey}" -in "${keyfile_enc}" -out "${tmpdir}/key" 2> /dev/null;
if [[ "$?" -eq 0 ]]; then
break;
fi;
done
if [[ ! -e "${tmpdir}/key" ]]; then
echo "Failed to decrypt keyfile" >> /dev/stderr;
rm -r "${tmpdir}"
return 1
else
openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 -in "${tmpdir}/message.enc" -out - -pass file:"${tmpdir}/key"
rm -r "${tmpdir}"
fi
}
# encrypt a small snippet with a ssh public key
encrypt_short() {
pubkey=$1
if [[ -z "${pubkey}" ]]; then
echo "Missing pubkey argument" >> /dev/stderr
return 1
fi
openssl rsautl -encrypt -pubin -inkey <(ssh-keygen -f ${pubkey} -e -m pkcs8) -pubin -in - -out - | base64
}
# decrypt a small snippet with a ssh private key
decrypt_short() {
privkey="$(_ensure_privkey_pem $1)"
base64 -d | openssl rsautl -decrypt -inkey ${privkey} -in - -out -
}
COMMAND="$1"
shift
OPT="$1"
if [[ "${OPT}" == "-h" || "${OPT}" == "--help" ]]; then
shift
set -- "${COMMAND}" "$@"
COMMAND="help"
fi
case "${COMMAND}" in
pubkey-from-github)
pubkey_from_github $*
;;
encrypt)
encrypt_full $*
;;
decrypt)
decrypt_full $*
;;
encrypt-short)
encrypt_short $*
;;
decrypt-short)
decrypt_short $*
;;
help)
_help $*
;;
*)
_help
exit 2
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment