Skip to content

Instantly share code, notes, and snippets.

@erichmachado
Last active November 21, 2019 19:46
Show Gist options
  • Save erichmachado/586eec1efdd9aade2edd945bd53bba91 to your computer and use it in GitHub Desktop.
Save erichmachado/586eec1efdd9aade2edd945bd53bba91 to your computer and use it in GitHub Desktop.
Set Git/GitHub PGP signing key
#!/usr/bin/env sh
# To test the script, set the GNUPGHOME to a temporary directory, e.g:
# $ export GNUPGHOME="$(mktemp -d)"
set -eu -o pipefail
DEPENDENCIES="gpg git"
EXPIRATION="${EXPIRATION:-6m}"
ALGORITHM="${ALGORITHM:-RSA}"
KEY_LENGTH="${KEY_LENGTH:-4096}"
function command_available() {
[ -x "$(command -v "$1")" ]
}
function check_dependency() {
if ! command_available "$1"; then
echo "Please, install '$1' and try again." && exit 1
fi
}
function check_dependencies() {
for dependency in $DEPENDENCIES; do check_dependency $dependency; done
}
function downcase() {
echo "$1" | tr '[:upper:]' '[:lower:]'
}
function display_exported_keys_info() {
gpg --with-colons --import-options show-only --import
}
function list_user_keys() {
gpg --list-keys --with-colons "<$1>" 2> /dev/null || true
}
function filter_trusted_signing_keys() {
awk -v key_length="$1" -F: '{ if($1 == "sub" && $2 ~ /[fu]/ && $3 == key_length && $12 ~ /s/) print $5 }'
}
function filter_trusted_keys_ids() {
awk -v key_length="$1" -F: '{ if($1 == "pub" && $2 ~ /[fu]/ && $3 == key_length) print $5 }'
}
function filter_fingerprints() {
awk -F: '{ if($1 == "fpr") print $10 }'
}
function add_gpg_primary_key() {
gpg --quick-generate-key "$1 <$2>" $(downcase "$3")"$4" cert "$5"
}
function add_gpg_signing_subkey() {
gpg --quick-add-key "$1" $(downcase "$2")"$3" sign "$4"
}
function primary_key_id() {
list_user_keys "$1" | filter_trusted_keys_ids "$2" | tail -1
}
function key_fingerprint() {
export_armored_gpg_key "$1" | display_exported_keys_info | filter_fingerprints
}
function export_armored_gpg_key() {
# Appending ! to the key ID instructs GPG to export only the key without its subkeys
export_armored_gpg_key_and_subkeys "$1!"
}
function export_armored_gpg_key_and_subkeys() {
gpg --armor --export "$1"
}
function get_gpg_signing_key_id() {
export_armored_gpg_key_and_subkeys "$1" | display_exported_keys_info | filter_trusted_signing_keys "$2" | tail -1
}
function get_git_signing_key_id() {
git config --get --global user.signingkey
}
function set_git_signing_key_id() {
git config --replace-all --global user.signingkey "$1"
}
function get_git_commit_signing() {
git config --get --global commit.gpgsign
}
function set_git_commit_signing() {
git config --replace-all --global commit.gpgsign "$1"
}
function main() {
check_dependencies
local email
read -p "Please enter your email: " email
pk_id="$(primary_key_id "$email" "$KEY_LENGTH")"
if [ -z "$pk_id" ]; then
echo "User $email does not have a suitable primary key ($ALGORITHM $KEY_LENGTH). Creating a primary key now..."
local fullname
read -p "Please enter your full name: " fullname
local expiration
read -p "Set the key expiration (e.g: '2y' for 2 years, '60d' for 60 days, 'none' for no expiration; default: '$EXPIRATION'): " expiration
add_gpg_primary_key "$fullname" "$email" "$ALGORITHM" "$KEY_LENGTH" "${expiration:-$EXPIRATION}"
pk_id="$(primary_key_id "$email" "$KEY_LENGTH")"
fi
echo "Primary key ID: $pk_id"
sg_id="$(get_gpg_signing_key_id "$pk_id" "$KEY_LENGTH")"
if [ -z "$sg_id" ]; then
echo "User $email does not have a suitable signing subkey ($ALGORITHM $KEY_LENGTH). Creating a signing subkey now..."
local expiration
read -p "Set the key expiration (e.g: '2y' for 2 years, '60d' for 60 days, 'none' for no expiration; default: '$EXPIRATION'): " expiration
add_gpg_signing_subkey "$(key_fingerprint "$pk_id")" "$ALGORITHM" "$KEY_LENGTH" "${expiration:-$EXPIRATION}"
sg_id="$(get_gpg_signing_key_id "$pk_id" "$KEY_LENGTH")"
fi
echo "Signing key ID: $sg_id"
if [ "$(get_git_commit_signing)" != 'true' ]; then
local gpg_sign
read -p 'Your Git client is not set to sign commits. Do you want to set it to sign all commits by default? [Y/n] ' gpg_sign
case "$(downcase "$gpg_sign")" in
n*) echo 'Git client not set to sign commits.';;
*) set_git_commit_signing true;;
esac
fi
if [ -z "$(get_git_signing_key_id)" ]; then
local set_signing_key
read -p "Your Git client is not set to use a signing key. Do you want to set it to use $sg_id? [Y/n] " set_signing_key
case "$(downcase "$set_signing_key")" in
n*) echo 'Git client signing key not set.';;
*) set_git_signing_key_id "$sg_id";;
esac
elif [ "$(get_git_signing_key_id)" != "$sg_id" ]; then
local set_signing_key
read -p "Your Git client is using a different signing key. Do you want to set it to use $sg_id? [y/N] " set_signing_key
case "$(downcase "$set_signing_key")" in
y*) set_git_signing_key_id "$sg_id";;
*) echo 'Git client signing key not changed.';;
esac
fi
if [ "$(get_git_signing_key_id)" == "$sg_id" ]; then
echo "Git client signing key set to $sg_id. You're all set!\n"
if ! command_available pbcopy; then
echo "Please, copy the signing public key below and paste it to your profile on GitHub:\n"
export_armored_gpg_key "$sg_id"
else
export_armored_gpg_key "$sg_id" | pbcopy
echo 'Signing public key was copied to the pastebin. You should be able to paste it to your profile on GitHub now.'
fi
fi
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment