Skip to content

Instantly share code, notes, and snippets.

@brianoflan
Created November 2, 2017 15:57
Show Gist options
  • Save brianoflan/2d0d80034d9281441d27af5bea9ac49d to your computer and use it in GitHub Desktop.
Save brianoflan/2d0d80034d9281441d27af5bea9ac49d to your computer and use it in GitHub Desktop.
Encrypt and decrypt short strings with the user's default SSH key pair
#!/bin/bash
# Encrypts and decrypts short strings with the user's default SSH key pair.
# USAGE:
# encrypt file: cat some_short_file.txt | encrypt_str
# encrypt variable: echo "$some_variable" | encrypt_str
# decrypt file: cat something.encrypted | decrypt_str
# decrypt variable: echo "$string" | decrypt_str
#
# # Arguments instead of pipes/STDIN:
# encrypt string: encrypt_str 'some short string'
# encrypt variable: encrypt_str "$some_variable"
# decrypt variable: decrypt_str "$some_variable"
#
# # Advanced:
# export my_encrypted_password='Pb59O+X/A...JQl=' # Note the ellipsis.
# decrypt variable: echo "$my_encrypted_password" | decrypt_str
# decrypt string: echo Pb59O+X/A...JQl= | decrypt_str
# # Encrypted strings are base64-encoded jumbles that are usually much
# # longer than the input string (which is always the size of the private key,
# # increased according to the 4/3 base64 ratio).
# DETAILS:
# Using the default ~/.ssh/id_rsa[.pub] key pair to encrypt and decrypt small
# strings is quick and easy, uses a cryptographic resource most people already
# have (their own SSH keys). The length of the plaintext string that can be
# encrypted is limited by the size of the private key and the nature of the
# "openssl rsautl" tool: A string may be 83 characters long (for 1024 bits)
# - or up to 481 characters long (for a 4096 bit private key).
# Not for long strings: Use "openssl smime" with a cert (which can be
# generated from your SSH keypair if you want something convenient).
# DETAILS ABOUT THE INPUT TO "encrypt_str()":
#
# NOTE: About command line arguments: It is best to pass input to encrypt_str
# via STDIN (just pipe it in, eg. "echo 'some short string' | encrypt_str").
# It is possible to pass input to encrypt_str as a command line argument ($1)
# and this case takes precedence over STDIN (eg.
# "echo 'some string' | encrypt_str 'another'"
# will encrypt 'another', not 'some string').
# CAVEAT: If using a command line argument to encrypt a string with whitespace
# or any characters outside the small, usual set of English keyboard
# alphanumerics and simple punctuation, the string should be quoted (and, if
# necessary, escaped) as the first and only argument. All arguments, including
# the first argument and any following ones are smooshing into one single
# string via "$*". This may cause data corruption if there is contiguous
# whitespace in an unquoted/unescaped clump of arguments (whitespace would be
# considered an argument delimiter - and "$*" will truncate any length of
# whitespace into a single \x20 space character). Example:
# "encrypt_str my long string with contiguous spaces"
# will actually encrypt 'my long string with contiguous spaces' (single space
# between each word), not the actual intended input.
# DESIGN DECISION: Regarding the case in which a user insists on using command
# line args instead of STDIN to input a string that includes plural whitespace
# that should be preserved - but is not quoted: this case seems sufficiently
# unlikely that I have kept "$*" instead of just forcing "$1" and throwing an
# error if $2 is defined. This system is designed for short, simple strings
# and STDIN or well-quoted command line arguments are adequate to preserve
# special and unexpected characters.
get_domain() {
local shost=$(hostname -s)
local fqdn=$(hostname -f)
[[ $shost != $fqdn ]] || fqdn=$(host $(hostname) | awk '{print $1}')
echo "$fqdn" | cut -d. -f2-
}
ensure_ssh_priv() {
local d="$HOME/.ssh"
local f="$d/id_rsa"
[[ -f "$f" ]] || {
[[ -d "$d" ]] || mkdir -p "$d"
chmod go-w $(dirname $d)
chmod go-rwx "$d" "$d/"
ssh-keygen -t rsa -b 4096 -f "$f" -N '' -C "$(id -un)@$(get_domain)"
}
}
ensure_ssh_pub() {
local f="$HOME/.ssh/id_rsa"
[[ -f "$f.pub" ]] || {
ensure_ssh_priv 1>&2
ssh-keygen -y -f "$f" > "$f.pub"
}
}
ensure_ssh_pkcs8() {
local f="$HOME/.ssh/id_rsa.pub"
ensure_ssh_pub 1>&2
[[ -f "$f.pkcs8" ]] || {
ssh-keygen -f "$f" -e -m PKCS8 > "$f.pkcs8"
}
}
_encrypt_str() {
local str=$1
ensure_ssh_pkcs8 1>&2
echo "$str" | openssl rsautl -encrypt \
-pubin -ssl -inkey "$HOME/.ssh/id_rsa.pub.pkcs8" \
| base64
}
encrypt_str() {
local result=''
local str="$*"
if [[ -z $str ]] ; then
# read str;
str=''
local l=''
while read l; do
[[ -z $str ]] && str=$l || str=$(echo -e "$str\n$l")
done ;
_encrypt_str "$str"
else
_encrypt_str "$str"
fi ;
}
_base64_d() {
if echo MQo= | base64 -d > /dev/null 2>&1 ; then
if [ -z ${1+x} ] ; then
cat - | base64 -d
else
echo "$1" | base64 -d
fi ;
else
if [ -z ${1+x} ] ; then
cat - | base64 -D
else
echo "$1" | base64 -D
fi ;
fi ;
}
_decrypt_str() {
local result=''
ensure_ssh_priv 1>&2
echo "$str" | _base64_d | openssl rsautl -decrypt -inkey "$HOME/.ssh/id_rsa"
}
decrypt_str() {
local result=''
local str="$*"
if [[ -z $str ]] ; then
# read str;
str=''
local l=''
while read l; do
[[ -z $str ]] && str=$l || str=$(echo -e "$str\n$l")
done ;
_decrypt_str "$str"
else
_decrypt_str "$str"
fi ;
}
#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment