How to use openssl certificates with an "agent"
If you're like me then you don't like to leave private keys sitting around unencrypted on your filesystem. That means that any of those client certificates that you use for your fun gemini sites like bbs.geminispace.org or station.martinrue.com need to be encrypted.
If you're also like me and browse geminispace with command line tools this becomes a bit of a pain since having to type passwords in all the damn time is annoying.
How do you get around this? Use gpg for your private key encryption! You don't actually have to store the private keys *in* gpg, you just need to encrypt them like you would any sort of confidential data.
If you are using pxc and bash you can create and use a new identity like so:
# Create a new private key and certificate for whatever
openssl req -x509 -newkey ed25519 -keyout >(gpg -r ${GPG_ADDRESS} -e -o ${CERT_OUT_PATH}/${IDENTITY}-key.pem.gpg) -noenc -out ${CERT_OUT_PATH}/${IDENTITY}-cert.pem -sha256 -subj "/CN=${SITE_OF_INTEREST}"
# Use the new certificate with pxc, if you're using bash or a similar shell
pxc -c ${CERT_OUT_PATH}/${IDENTITY}-cert.pem -k <(gpg -d ${CERT_OUT_PATH}/${IDENTITY}-key.pem.gpg) ${SITE_OF_INTEREST}
The "process redirection" i.e. <() and >() mean you can pipe the data around purely within the shell so you never need to let it touch your disk.
bashrc functions for automation
If you'd like a bash function that will quickly switch between identities when using pxc, use switch_pxc() below. It makes an alias 'pxcc' that sets up pxc with the proper arguments.
switch_pxc() {
ID="$1"
CERT="${HOME}/.pxc/identities/${ID}-cert.pem"
KEY="${HOME}/.pxc/identities/${ID}-key.pem.gpg"
if [ ! -f "$CERT" ] || [ ! -f "$KEY" ] ; then
echo "Could not locate $CERT or $KEY"
return
fi
alias pxcc="pxc -c $CERT -k <(gpg -d $KEY)"
# display the new alias
alias pxcc
}
You can use the below function to automatically generate new certificates that can be switched via switch_pxc. It accepts two arguments. The first is the certificate identity, and the optional second argument is the path to an existing private key. If no second argument is given then this function will generate a new key.
PXC_GPG_ID="whateveryouwanthere@mydomain.com"
generate_pxc_cert() {
ID="$1"
EXISTING_KEY="$2"
if [ -n "$EXISTING_KEY" ] ; then
rp="`realpath "$EXISTING_KEY" 2>/dev/null`"
if [ -z "$EXISTING_KEY" ] ; then
echo "Could not find realpath for $EXISTING_KEY"
return
fi
fi
if [ -z "$ID" ] ; then
echo "No ID provided"
return
fi
CERT="${HOME}/.pxc/identities/${ID}-cert.pem"
KEY="${HOME}/.pxc/identities/${ID}-key.pem.gpg"
if [ -f "$CERT" ] || [ -f "$KEY" ] ; then
echo "Cert $CERT or key $KEY already exist. Delete them first if you want to make a new one."
return
fi
if [ -n "$EXISTING_KEY" ] ; then
if [ ! -f "$EXISTING_KEY" ] ; then
echo "Could not find existing key $EXISTING_KEY"
return
fi
if openssl req -x509 -key <(gpg -d "$EXISTING_KEY") -out "$CERT" -sha256 -subj "/CN=${ID}" ; then
echo "Successfully generated certificate $CERT"
# if the key is in ~/.pxc/identities then make paths relative
REL_KEY_NAME=${EXISTING_KEY##${HOME}/.pxc/identities/}
if ln -sf "$REL_KEY_NAME" "$KEY" ; then
echo "Successfully linked $EXISTING_KEY -> $KEY"
ls -lh "$KEY"
else
echo "Could not link $EXISTING_KEY -> $KEY"
fi
else
echo "Could not generate new certificate from existing key $EXISTING_KEY"
fi
else
if [ -z "$PXC_GPG_ID" ] ; then
echo "No PXC_GPG_ID set, cannot generate key"
return
fi
KEYTYPE="${KEYTYPE:-ed25519}"
echo "Generating a new $KEYTYPE key"
if openssl req -x509 -newkey "$KEYTYPE" -keyout >(gpg -e -r "$PXC_GPG_ID" -o "$KEY") \
-out "$CERT" -noenc -sha256 -subj "/CN=${ID}" ; then
echo "Succesfully generated new cert $CERT and new key $KEY"
else
echo "Openssl failed to generate $CERT and $KEY"
fi
fi
if [ -f "$CERT" ] && [ -f "$KEY" ] ; then
echo "Successfully created $CERT and $KEY"
else
echo "Failed to create $CERT and $KEY"
fi
}
Enjoy!