Wilson Mar bio photo

Wilson Mar


Calendar YouTube Github


Sign git commits and tags (for non-repudiation) in GitHub using GPG, Vault, Yubikey, Keybase

US (English)   Norsk (Norwegian)   Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Estonian   اَلْعَرَبِيَّةُ (Egypt Arabic)   Napali   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean


“If you … want to verify that commits are actually from a trusted source, Git has a few ways to sign and verify work using GPG.” -git-scm.com/show-ref command


The contribution of this article is a logical ordering of deep-dive concepts presented in a succinct way, as a hands-on narrated scenic tour. “PROTIP” flags advice from hard-won experience such as relevant keyboard shortcuts, recovering from common mistakes, and things to remember, available only here for you.

Sign using SSH

Thanks to https://blog.artis3nal.com/blog/sign-commits-github-ssh/

  1. Create (Edwards-curve DSA) SSH key pair in a Terminal window, overriding default -filename “id_ed25519”:

    cd ~/.ssh
    ssh-keygen -t ed25519 -o -C "$MY_EMAIL" -f "$NEW_KEY_FILE" 
    # no pass phrase

    NOTE: -t rsa -b 4096 are now obsolete.

    Alternately, use ed25519-sk or ecdsa-sk for a hardware security key.

  2. Add your SSH private key to the ssh-agent and store your passphrase in the keychain. If you created your key with a different name, or if you are adding an existing key that has a different name, replace id_ed25519 in the command with the name of your private key file.

    ssh-add --apple-use-keychain ~/.ssh/$NEW_KEY_FILE

    Example response:

    Identity added: /Users/johndoe/.ssh/johndoe-mac22-23-02-19 (johndoe@gmail.com)
  3. Get the Public Key:

    git config --global gpg.format ssh
    PUBLICKEY=$( cat ~/.ssh/$NEW_KEY_FILE.pub )

    Example value of PUBLICKEY:

    ssh-ed25519 AAAAC3ZzaC1lZDI1NTE5AAAAIAndbpxphGOfHN+R1lidpUY04E3ZukHpo2q93C9HvSfK johndoe@gmail.com
  4. Configure the Public Key:

    git config --global user.signingkey "key::$PUBLICKEY"
    git config --global commit.gpgsign true
    git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
    # Extract email address:
    email=$(git config --global --list | grep "user.email" | awk '{split($0, a,"="); print a[2]}')
    echo "$email $publickey" >> ~/.ssh/allowed_signers
  5. Add to SSH Agent:

    #eval "$(ssh-agent -s)"
     ssh-add ~/.ssh/$NEW_KEY_FILE.pub
     # TODO: echo $0
     ssh-add -l
  6. Add the SSH key to your account on GitHub.

  7. Navigate to a GitHub repo folder.
  8. Commit something.
  9. Verify

    git log --show-signature

Decisions: Variations

This workflow can seem complicated because there are several options: tooling variations (described below):

The workflow:

  1. Make decisions about install variations
  2. Install apps and programs locally
  3. Configure emails
  4. Generate keys
  5. list keys to verify
  6. Add public GPG key to GitHub
  7. Set up GitHub to require signing
  8. Sign Git commits and merges
  9. Sign Git Tags
  10. Remove keys from GitHub immediately after losing your laptop

BONUS: Since we have GPG installed, here are also notes about:

Install client utilities

The alternatives on macOS:

The alternatives on Linux:

The alternatives on Windows:

Enterprises would use a centrally administered system to install for all users, such as:

Desired Self-Serve Workflow

Here’s the workflow I would like to see. It’s not so much self-service as a tool for administrators. Anyway…

Before someone starts a job/project, a trusted administrator (the boss) specifies on a “self-service” portal what should be installed on each worker’s laptop, such as the client utilities which should be installed for his/her specific job based on RBAC (Role-Based Access Control) or Attribute-based Access Control (ABAC) policies.

The app generates the certificate pairs, stores them in Vault, installs them on GitHub, and saves the keys on the worker’s laptop. This provides a more trusted chain than each employee generating their own key pair.

Then all a new working developer needs to do is, on a pre-configured laptop, make a change and do a git tag or add and commit with a tag, then push.

Generate and retrieve SSH & GPG from 1Password

Protect your SSH key within 1Password. See https://developer.1password.com/docs/ssh/git-commit-signing/

https://www.darkreading.com/dr-tech/how-to-use-ssh-keys-and-1password-to-sign-git-commits describes use of 1Password with GitHub:

  1. Update to Git 2.34.0 or later
  2. Navigate to https://github.com/settings/keys and select “new SSH key,” followed by selecting “Signing Key.”

    CAUTION: I don’t see “Signing Key” on GitHub.

  3. From there, navigate to the “Key” box and select the 1Password logo, select “Create SSH Key,” fill in a title, and then select “Create and Fill.”

  4. For the last step, select “Add SSH Key,” and the GitHub part of the process is complete.

Once the key is set up in GitHub, proceed to 1Password on your desktop to configure your .gitconfig file to sign with their SSH key.

  1. Select the “Configure” option in the banner displayed on top, where a window will open with a snippet you can add to the .gitconfig file.

  2. Select the “Edit Automatically” option to have 1Password update the .gitconfig file with one click.

  3. Users in need of more advanced configuration can copy the snippet and do things manually.

Email address in GitHub

We want to configure GitHub to use your primary email in the form such as:


The extra “+github” confuses simple attempts.

  1. Be at a browser profile you want to use. (I click on my avatar on Chrome to setup a profile for each email address I use - one for personal Gmail, another for work)

  2. In your browser, open a new tab to view your GitHub Profile Email page:


  3. If your primary email does not have “+github”, type your address with the extra “+github” in the Add email address field, then click “Add”.

  4. If you are using Gmail or another email system that processes “+github” emails, for “Primary email address”, select that as your primary email.

    IMPORTANT PROTIP: The email specified to GPG should match that reply email in GitHub.

    Alternatively, highlight your “no reply” email address under the “Primary email address” heading. Example:


    Press command+C to copy it to your Clipboard.

    In your notes, press command+V to save that for use to Sign Commits (below).

Install on macOS GUI app GPG-Suite

  • https://medium.com/@rwbutler/signing-commits-using-gpg-on-macos-7210362d15

Instead of VIDEO: downloading from website and clicking manually:

  1. Install silently with one command after installing Homebrew:

    brew install --cask gpg-suite

    (its previous name was gpgtools, as in the website gpgtools.com)

  2. Type your password when prompted.

    ==> Caveats
    Cask gpg-suite installs files under /usr/local. The presence of such
    files can cause warnings when running `brew doctor`, which is considered
    to be a bug in Homebrew Cask.
    ==> Downloading https://releases.gpgtools.org/GPG_Suite-2021.1_105.dmg
    ######################################################################## 100.0%
    ==> Installing Cask gpg-suite
    ==> Running installer for gpg-suite; your password may be necessary.
    Package installers may write to any location; options such as `--appdir` are ignored.
    installer: Package name is GPG Suite
    installer: Upgrading at base path /
    installer: The upgrade was successful.
    🍺  gpg-suite was successfully installed!
  3. Verify folder created during installation (include the quotes because the folder contains a space character):

    ls -al "/Applications/GPG Keychain.app/Contents/"
    total 16
    drwxr-xr-x   8 root  admin   256 May 14 18:37 .
    drwxr-xr-x   3 root  admin    96 May 14 18:37 ..
    drwxr-xr-x   3 root  admin    96 May 14 18:37 Frameworks
    -rw-r--r--   1 root  admin  3354 May 14 18:37 Info.plist
    drwxr-xr-x   3 root  admin    96 May 14 18:37 MacOS
    -rw-r--r--   1 root  admin     8 May 14 18:37 PkgInfo
    drwxr-xr-x  43 root  admin  1376 May 14 18:37 Resources
    drwxr-xr-x   3 root  admin    96 May 14 18:37 _CodeSignature

    To remove the app later, simply delete folder “GPG Keychain.app”, which would make certs disappear too. That’s why we will later save the certs to a location off your laptop.

  4. Open the app from Terminal:

    open "/Applications/GPG Keychain.app"

    Alternately, pinch 4 fingers together on the Touchpad and type enough of “GPG Keychain” for the icon to appear for you to click:


    Gen GPG using macOS GPG-Suite

  5. Click “Show secret keys only” (at the lower-right corner) to ignore the Pub (Public) keys there by default.

  6. If there are green boxes marking your email from a previous session, you need to revoke it before creating a new key for that email.

  7. To generate a GPG key pair click + New for the pop-up dialog.
  8. If there is a pop-up to access your contacts, click “Don’t Allow”.
  9. Click “Advanced options” to select Key Type “RSA (sign only)”.


  10. Type your name in title case.
  11. Type your email with the extra “+github” such as “wilsonmar+github@gmail.com”.

  12. Switch to 1Password, LastPass, etc. to define a new password, then copy and paste that new password in the two fields.
  13. Make a note of the expiration date (by default four years from current date). Some prefer no longer than one year so that if your secrets are compromised, thieves only have a year to use it ;)

  14. Click “Create Key”.

  15. Many don’t enter a Passphrase to protect the key. Click OK. You’ll be prompted again if you didn’t enter a Passphrase.

    Git Signing Upload

  16. If you select “No, Thanks!” to upload your public key, you can do that later from the “Key” menu item.
  17. Your new key should now be listed with green icons at the right.
  18. Press Command+Q to quit the GPG Keychain program.

    Also install utility to handle GPG within your Terminal:

    Install on macOS CLI utility gnupg2

  19. Open a Terminal. Be at your home user folder.

  20. If you have not already, manually install brew (Homebrew)

  21. Upgrade or Install a Git client using Homebrew’s gnupg formulae

    brew upgrade git

    If git was not previously installed, install it:

    brew install git

    Notice the response is for a specific version of MacOS (Mojave in this case):

    ==> Upgrading 1 outdated package:
    git 2.25.0_1 -> 2.32.0
    ==> Upgrading git 2.25.0_1 -> 2.32.0
    ==> Downloading https://ghcr.io/v2/homebrew/core/git/manifests/2.32.0
    ######################################################################## 100.0%
    ==> Downloading https://ghcr.io/v2/homebrew/core/git/blobs/sha256:3c613c84fbc741
    ==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
    ######################################################################## 100.0%
    ==> Pouring git--2.32.0.mojave.bottle.tar.gz
    ==> Caveats
    The Tcl/Tk GUIs (e.g. gitk, git-gui) are now in the `git-gui` formula.
    Bash completion has been installed to:
    Emacs Lisp files have been installed to:
    ==> Summary
    🍺  /usr/local/Cellar/git/2.32.0: 1,517 files, 41.5MB
  22. For information about the brew gpg2 install:

    brew info gnupg2

    The response at time of writing:

    gnupg: stable 2.3.6 (bottled)
    GNU Pretty Good Privacy (PGP) package
    Not installed
    From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gnupg.rb
    License: GPL-3.0-or-later
    ==> Dependencies
    Build: pkg-config ✔
    Required: gettext ✔, gnutls ✔, libassuan ✘, libgcrypt ✘, libgpg-error ✘, libksba ✘, libusb ✔, npth ✘, pinentry ✘
    ==> Analytics
    install: 73,341 (30 days), 262,763 (90 days), 992,581 (365 days)
    install-on-request: 69,985 (30 days), 248,160 (90 days), 932,987 (365 days)
    build-error: 11 (30 days)
  23. Install gpg2 :

    brew install gnupg2
  24. Optionally, edit your ~/.bash_profile or ~/.bashrc or ~/.zshrc file to ensure that commands for “gpg” are routed to gpg2:

    alias gpg="gpg2"
    echo -e "\n$(gpg --version | grep gpg)"    # gpg (GnuPG) 2.2.19

    PROTIP: The response shows that the installation is specific to each version of macOS:

    ==> Downloading https://homebrew.bintray.com/bottles/gmp-6.2.0.mojave.bottle.tar.gz
  25. Compare response from:

    brew info gpg

    The version output should be the same.

    Verify GPG install version

  26. Verify CLI noted in Wikipediat/Wikiwand.com/en/GNU_Privacy_Guard which states the source at d2qa dev.gnupg.org/source/gnupg

    gpg --version
    gpg (GnuPG/MacGPG2) 2.2.34
    libgcrypt 1.8.9
    Copyright (C) 2022 g10 Code GmbH
    License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Home: /Users/wilsonmar/.gnupg
    Supported algorithms:
    Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
    Compression: Uncompressed, ZIP, ZLIB, BZIP2

    MacOS GPG Config

    PROTIP: The command above creates folder $HOME/.gnupg.

  27. Update permissions on your ~/.gnupg Directory:

    chmod 700 ~/.gnupg

    No response is expected if the command is successful.

  28. Update or Create ~/.gnupg/gpg.conf using your favorite text editor:

    code $HOME/.gnupg/gpg.conf

    The above would open Visual Studio Code containing lines such as:

  29. If the use-agent is not there, add it. If it’s there, remove the comment character # from “use-agent” to enable it:

    # Uncomment within config (or add this line)
    # This tells gpg to use the gpg-agent
  30. Switch back to the Terminal to obtain keys:

    gpg -k

    Response (if you’re John Doe):

    pub   rsa4096 2021-06-20 [SC] [expires: 2025-06-20]
    uid           [ultimate] John Doe <johndoe+github@gmail.com>
  31. Double-click to highlight the key above, such as:

  32. Press command+S to save and copy the key to your invisible clipboard.

  33. Switch back to your text editor to complete the command to type “defaul-key “ then press command+V to paste the key to end up with something like this:

    default-key 123456789E91004D4C5D88CAE21961814AC0EF1B
  34. Save the file.

  35. Proceed to Configuration section further down by skipping the Linux install immediately below.

Linux installers

Package installers on Linux have other package names:

  • yum install gnupg2 on CentOS/RHEL
  • dnf install gnupg2 on Fedora
  • apt install gnupg on Debian/Ubuntu

  1. On macOS, install gnupg2 for the gpg program:

    In the script, if each utility is found, it is re-installed if the REINSTALL flag is set on, which it is by default.

    if ! command -v gpg >/dev/null; then
       echo "Installing GPG2 for commit signing..."
       brew install gnupg2
       # See https://www.gnupg.org/faq/whats-new-in-2.1.html
       if [[ "${MY_RUNTYPE,,}" == *"upgrade"* ]]; then
          echo "GPG2 upgrading ..."
          gpg --version | grep gpg  # outputs many lines!
          # To avoid response "Error: git not installed" to brew upgrade git
          brew uninstall --ignore-dependencies gpg2
          brew uninstall gnupg2
          # NOTE: This does not remove .gitconfig file entry.
          brew install gnupg2
  2. Proceed to Configuration

Install Windows GUI


  1. Proceed to Configuration

Install CLI on Windows

  1. Install Chocolatey if you havent’s already.

  2. Install with one command using Chocolatey:

    choco install gpg2 gnupg -y

    Alternately, install Gpg4win GUI using Chocolatey:

    choco install gpg4win

  3. Proceed to Configurations (below).


At your local command line terminal:

  1. Did you configure a user name and email in Git? View using this command:

    git config --list | grep user

    Example response:

  2. You may not want to do this if you’re configuring for multiple organizations. But if you are working with only one GitHub account, if you haven’t already, while in a Terminal with the present working directory at your local repository, configure you valid GitHub user name and email. For example:

    git config --global user.name "John Doe"
    git config --global user.email "john_doe@gmail.com"

    IMPORTANT PROTIP: Any name and email can be specified in Git, which means anyone can impersonate someone else to get a malicious commit PR accepted. This is a big reason organizations ask for cryptographically signing commits in GitHub, which requires that the email specified be validated.

Generate and store keys

Skip to List GPG Keys if you don’t want to store keys off your laptop (in the cloud).

There are several places you can store GPG keys:

  • On your local drive (which will be lost if your laptop dies or gets lost)
  • Keybase cloud (below)
  • On a Yubikey physical device you obtain (from your employer or Amazon, etc.)

  • HashiCorp Vault
  • Azure KeyVault?
  • AWS Key Service (AKS)
  • Google Cloud?

HashiCorp Vault

In a Terminal:

  1. Install HashiCorp Vault program on your Mac:

    brew upgrade vault

    If it’s not already installed:

    brew install vault
  2. Confirm viability by displaying the program’s version, such as:

    vault --version

    At time of writing:

    Vault v1.10.4 (6a1dde56c18c4a1be2756b931ce3c872d8ca5a76)
  3. If you don’t have a HashiCorp Vault server, follow my instructions to run it (for development/experimentation) locally at:


  4. In your $HOME folder, create a file named vaultvalues.env.
  5. Grant run access to it:

    chmod +x vaultvalues.env
  6. Use an editor to customize environment variables, starting with the host name and port of your HashiCorp Vault server in VAULT_ADDR:

    # URL of the HashiCorp Vault server:
    export VAULT_ADDR=https://localhost:8200
    # The signing backend endpoint (transit or gpg) and optionally hashing function:
    # to use. Mandatory for signing.
    export VAULT_SIGN_PATH=transit/sign/test/sha2-256
    export VAULT_SIGN_PATH=gpg/sign/test/sha2-256
    # The verify backend endpoint (transit or gpg). Mandatory for verifying.
    export VAULT_VERIFY_PATH=transit/verify/test
    export VAULT_VERIFY_PATH=gpg/verify/test
    # The SNI to present during the TLS handshake (if different from the Vault HTTP
    # host name). Useful when your Vault is exposed through an AWS private link for
    # example. Optional.
    export VAULT_TLS_SERVER_NAME=hostname.to.use.for.sni.com
  7. Run the file to load environment variables:

    cd $HOME
  8. Navigate into each repo and

    git config --local gpg.program "${VAULT_SIGN_PATH}"
  9. Login to Vault:

    # Login to vault:
    vault login  # referencing $VAULT_ADDR

Proceed to Sign Git commits and merges (below)


  • https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work
  • https://docs.github.com/en/github/authenticating-to-github/signing-commits
  • https://withblue.ink/2020/05/17/how-and-why-to-sign-git-commits.html
  • https://medium.com/hashicorp-engineering/securing-github-access-with-hashicorp-vault-c25ab8f5d5ea
  • https://github.com/martinbaillie/vaultsign
  • https://oteemo.com/hashicorp-vault-is-overhyped-and-mozilla-sops-with-kms-and-git-is-massively-underrated/

Keybase cloud

The advantage of using the Keybase app to generate GPG keys is that the keys are stored online at keybase.io, where you’ll be able to retrieve your keys when you don’t have your laptop anymore.

The downside is that it’s possible for Keybase.io to be hacked.

PROTIP: Keybase was acquired by Zoom in 2020. Some are concerned that Zoom will stop support of the product because Zoom only wanted the talent and not fund the free product.

VIDEO explains https://github.com/pstadler/keybase-gpg-github

  1. Go to keybase.io and create an account.

    NOTE: Because Keybase asks for verfification of social media accounts, it may be more comforting for repository owners to know that users went through more hoops to obtain and verify each of their accounts, so the account used is less likely to be a fake. Keybase provides value-added services such as adding encryption around direct messages on Twitter. VIDEO: Keybase also works with the pass utility to manage passwords securely (like Vault).

  2. Install the Keybase app to /Applications/Keybase.app:

    brew install --cask keybase
  3. Sign locally out to the Keybase service:

    keybase login

    It takes a few seconds and returns you to the command prompt.

  4. To avoid error message:

    gpg: WARNING: server 'gpg-agent' is older than us (2.2.20 < 2.2.23)
    gpg: Note: Outdated servers may lack important security fixes.
    gpg: Note: Use the command "gpgconf --kill all" to restart them.

    do this:

    gpgconf --kill gpg-agent
  5. Import public keys using Keybase:

    keybase pgp export | gpg --import

    Example response:

    gpg: key 938BBBDEB75FEA21: public key "Wilson Mar <wilsonmar+github@gmail.com>" imported
    gpg: Total number processed: 1
    gpg:               imported: 1
  6. Get the private key:

    keybase pgp export --secret | gpg --allow-secret-key --import

    If you see this response:

    ▶ ERROR No matching keys found
    gpg: no valid OpenPGP data found.
    gpg: Total number processed: 0

  7. Verify progress:

    gpg --list-secret-keys
  8. Generate a GPG keypair:

    keybase pgp gen --multi

    Example prompts and responses:

    Enter your real name, which will be publicly visible in your new key: Patrick Stadler
    Enter a public email address for your key: patrick.stadler@gmail.com
    Enter another email address (or ≪enter> when done):
    Push an encrypted copy of your new secret key to the Keybase.io server? [Y/n] Y
    ▶ INFO PGP User ID: Patrick Stadler <patrick.stadler@gmail.com> [primary]
    ▶ INFO Generating primary key (4096 bits)
    ▶ INFO Generating encryption subkey (4096 bits)
    ▶ INFO Generated new PGP key:
    ▶ INFO   user: Patrick Stadler <patrick.stadler@gmail.com>
    ▶ INFO   4096-bit RSA key, ID CB86A866E870EE00, created 2016-04-06
    ▶ INFO Exported new key to the local GPG keychain
  9. Verify whether it’s configured with signingkey:

    git config --list | grep user

    Example response:

  10. Skip to List GPG keys (below).

List GPG keys

List keys to verify that you have indeed generated them.

  1. List what keys have been signed, meaning secret keys (more selective than the gpg -k command):

    gpg --list-secret-keys --keyid-format LONG

    --keyid-format LONG requests showing only those keys where both public and private key pair exists. This is because both are required to sign commits and tags. If nothing is returned, there are no keys usable for signing.

    PROTIP: This above command can be used often, so added as Bash shell alias (keyboard shortcut) in https://github.com/wilsonmar/git-utilities/blob/master/aliases.sh so that you can instead just type:


    In the response, the first line lists the location where keys are stored (with your own user name instead of “wilson_mar”):


    PROTIP: File pubring.kbx is the Gnupg program’s “Key Ring” file. See https://kb.iu.edu/d/awiu about keyring management commands.

  2. To list all keys:

    gpg --list-keys

    External (GPG Suite) to openpgp.or

    If you’re working on open-source projects, not for Enterprise internal use, you can install the GPG Suite (UI app) or Keybase.io.

    The Suite can be installed as a Homebrew formula brew install --cask gpg-suite (brew install cask gpgtools no longer exists). The GUI app is installed at “/Applications/GPG Keychain.app”. The first time it runs, this pop-up appears:


    Read about it at GPGTools.org and here.

    pinentry for GPG on MacOS

  3. Edit file (if the file doesn’t exist, create it) to add the path.

    code ~/.gnupg/gpg-agent.conf
  4. On MacOS, install a graphical pinentry application:

    brew install pinentry-mac
  5. Verify where the program was install:

    which pinentry-mac


  6. Edit file ~/.gnupg/gpg-agent.conf to contain:

    default-cache-ttl 600
    max-cache-ttl 7200
    pinetry-program /usr/local/bin/pinetry-mac
  7. If you are not using a Yubikey, proceed to Generate GPG key pairs.

    Optional Yubikey smart chip

    Instead of storing private keys on your laptop’s hard drive (where a hacker can access from any program running on the computer), security-concious people store their private keys in a separate physical smartcard (OpenGPG card) such as a Yubikey device (one of several).

    Day-to-day, it is good for those who work on multiple machines but want to use a single physical signing key they plug into each machine.

    Note the Yubikey was designed to not be able to export private keys (by hackers or you).

    If your laptop’s USB has been locked down, skip this and move on to generate a key.

    git-siging-yubikey-100x100.jpg PROTIP: If you lose your physical dongle, you’ll need to re-generate all keys.

    Also, keys written to a card can only be used in combination with a PIN code, so that even if a YubiKey is stolen, a thief would not be able to authenticate directly.

    Each YubiKey is its own unique cardno.

  8. In Yubikey Setup Docs, identify your Yubikey (I personally have a 5Ci FIPS for use on MacOS USB-C and iPhone USB/Apple Lightning® Interface: OTP):


    Note that these are internet-based SaaS apps rather than local apps (such as 1Password”.

  9. Click on “GitHub” for these instructions to enable a 2FA app (if you haven’t already),

  10. In a Terminal, install software to manage Yubikey (ykman):

    brew install ykman
    brew install yubikey-personalization

    Install of yubikey-personalization issues Warning: ykpers 1.20.0 is already installed and up-to-date.

    QUESTION: How to check for vulnerabilities in the above utilities?

  11. Use a text editor to add inside file ~/.gnupg/gpg.conf “no-tty” so it contains:

  12. Insert your YubiKey and run:

    gpg --card-status

    The response expected is like this:

    Reader ...........: Yubico Yubikey 4 OTP U2F CCID
    Application ID ...: D27312341241342342342342343
    Version ..........: 2.0
    Application type .: OpenPGP
    Version ..........: 3.4
    Manufacturer .....: Yubico
    Serial number ....: 12345678
    Name of cardholder: [not set]
    Language prefs ...: [not set]
    Salutation .......: 
    URL of public key : [not set]
    Login data .......: [not set]
    Signature PIN ....: not forced
    Key attributes ...: rsa2048 rsa2048 rsa2048
    Max. PIN lengths .: 127 127 127
    PIN retry counter : 3 0 3
    Signature counter : 0
    KDF setting ......: off
    Signature key ....: [none]
    Encryption key....: [none]
    Authentication key: [none]
    General key info..: [none]

    Configure Yubikey

  13. Initiate the configuration program:

    gpg --card-edit

    The prompt appears:

  14. Initiate the configuration program:

    gpg/card> admin

    “Admin commands allowed” means you can continue.

  15. Generate:

    gpg/card> generate
  16. Type Y to:

    “Make off-card backup of encryption key? (Y/n) Y


    PROTIP: Please note that the factory settings of the PINs are
    PIN = '123456'     Admin PIN = '12345678'
    You should change them using the command --change-pin
  17. Change the PIN:

  18. Generate for reals:

    gpg/card> generate
  19. Type n to:

    “Make off-card backup of encryption key? (Y/n) n

    BLOG: if you see these messages:

    gpg: selecting openpgp failed: Operation not supported by device
    gpg: OpenPGP card not available: Operation not supported by device   
  20. In the pop-up, type PIN “123456”, which is the default pin.


    Please specify how long the key should be valid.
          0 = key does not expire
       <n>  = key expires in n days
       <n>w = key expires in n weeks
       <n>m = key expires in n months
       <n>y = key expires in n years
  21. Type “1y” (for 1 year).

    Response is the time/date of expiration, such as:

    Key expires at Wed Jun 22 10:40:38 2022 MDT
  22. Type “y” to “Is this correct? (y/N)”


    GnuPG needs to construct a user ID to identify your key.
    Real name: 
  23. Type your real name, such as “John Doe”.
  24. Type your Email address: johndoe+github@gmail.com
  25. Type Comment:

  26. Type “O” (for OK):

    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
  27. In the pop-up “Please enter the Admin PIN”, which is by default “12345678”, as above.

  28. In the pop-up “Please unlick the card”, type “123456”, the default.

    The response should be a message such as:

    gpg: key A1D1DC27394AD9D0 marked as ultimately trusted
    gpg: revocation certificate stored as '/Users/johndoe/.gnupg/openpgp-revocs.d/056B29F5E7827C95C3A83B3BC1D1DC27394AD9D0.rev'
    public and secret key created and signed.
  29. Change the PIN and Admin PIN:

    gpg/card> passwd


    gpg: OpenPGP card no. A1234567890103040006162528900000 detected
    1 - change PIN
    2 - unblock PIN
    3 - change Admin PIN
    4 - set the Reset Code
    Q - quit
    Your selection? _
  30. Type 1 to “change PIN”.
  31. In the pop-up “Please enter the PIN”, type the CURRENT 6-character PIN and press OK.
  32. In the pop-up “New PIN”, retype your PIN and press OK.

    Expected response:

    PIN changed.
    1 - change PIN
    2 - unblock PIN
    3 - change Admin PIN
    4 - set the Reset Code
    Q - quit
    Your selection? _
  33. Type 3 to “change Admin PIN”.
  34. In the pop-up “Please enter the Admin PIN”, type the CURRENT 8-character PIN and press OK.
  35. In the pop-up “New Admin PIN”, retype your PIN and press OK.

    Expected response:

    PIN changed.
    1 - change PIN
    2 - unblock PIN
    3 - change Admin PIN
    4 - set the Reset Code
    Q - quit
    Your selection? _
  36. Type Q to quit out of passwd mode.

  37. List the configuration:

    gpg/card> list
    General key info..: 
    pub  rsa2048/A1D1DC27394AD9D0 2021-06-22 John Doe <johndoe+github@gmail.com>
    sec>  rsa2048/A1D1DC27394AD9D0  created: 2021-06-22  expires: 2022-06-22
                                 card-no: 0006 16252890
    ssb>  rsa2048/123D8F06797B4CD8  created: 2021-06-22  expires: 2022-06-22
                                 card-no: 0006 16252890
    ssb>  rsa2048/12329BE82896FF01  created: 2021-06-22  expires: 2022-06-22
                                 card-no: 0006 16252890

    Two “ssb” (sub keys) are generated by the YubiKey 5 FIPS Series keys (one for each level of FIPS certification (FIPS 140-2 Level 1 and FIPS 140-2 Level 2). Both certificates apply to the same keys.

  38. Tell Git the signing key to use, using the key from the previous command (above):

    git config --global user.signingkey=A1D1DC27394AD9D0
  39. Verify the signingkey within the [user] section:

    cat .git/config
  40. Type “quit” to quit out of the program.

    Sub-keys are listed:

    sub   rsa2048 2021-06-22 [A] [expires: 2022-06-22]
    sub   rsa2048 2021-06-22 [E] [expires: 2022-06-22]
  41. If you lose your Yubikey, use your Master Key to certify a new signing key, then paste a new public key into GitHub.

    References on Yubikey on macOS Git:

    • https://support.yubico.com/hc/en-us/articles/360013790219-Getting-Started-with-the-YubiKey-on-macOS
    • https://github.com/drduh/YubiKey-Guide
    • https://www.isi.edu/~calvin/yubikeyssh.htm
    • https://hugotunius.se/2018/07/13/yubikey-ssh-authentication.html - 13 Jul 2018
    • https://raymondcheng.net/projects/2018/11/25/git-yubikey.html
    • https://evilmartians.com/chronicles/stick-with-security-yubikey-ssh-gnupg-macos

Install GitKraken app and sign

Git UI clients such as GitKraken can generate GPG keys with its UI.

GitKraken provides a GUI for signing.

Generate GPG key pairs

On a macOS Terminal:

Gen GPG on macOS

PROTIP: In highly secure organizations, keys are generated by a security department and provided to workers.

  1. PROTIP: Define permissions to avoid “gpg: agent_genkey failed: Operation cancelled” error:

    chmod o+rw $(tty)
  2. Generate another key:

    gpg --gen-key

    --generate-key is the long form of the parameter.

    The response:

    gpg (GnuPG/MacGPG2) 2.2.24; Copyright (C) 2020 Free Software Foundation, Inc.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
    GnuPG needs to construct a user ID to identify your key.
    Real name:
  3. Enter in the series of prompts:

    Real Name: John Doe
    Email address: john-doe+github@gmail.com
    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? _

    PRITIP: If you want to use different email addresses on different projects, generate one GPG key for each email address.

  4. Type “O” (capital or lowercase O) to save the entry.

  5. In response to “Please enter the passphrase to protect your new key”:


    PROTIP: Save you Passphrase in a secure place (such as in HashiCorp Vault), then copy it to paste in the prompt. This tactic is to ensure that you really can retrieve it when you use the key in a future command.

    REMEMBER: Don’t reuse passwords and passphrases.

  6. Re-enter the key.

  7. Press Enter. Sample long-winded response:

    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    gpg: key 123567A89BFBE52 marked as ultimately trusted
    gpg: directory '/Users/wilson_mar/.gnupg/openpgp-revocs.d' created
    gpg: revocation certificate stored as '/Users/wilson_mar/.gnupg/openpgp-revocs.d/0BB29E3C5216420CC50ACF8D62C414BA89BFBE52.rev'
    public and secret key created and signed.
    pub   rsa2048 2020-03-01 [SC] [expires: 2022-03-01]
    uid                      Wilson Mar <john_doe+github@gmail.com>
    sub   rsa2048 2020-03-01 [E] [expires: 2022-03-01]

    QUESTION: “rsa4096” or “rsa2048” is the encryption algorithm used. Are they obsolete now?

  8. WARNING: Notice the expiry period is two years from date of creation.

    PROTIP: Setup an appointment to create a new key before this one expires at an inconvenient time. Or use a service that rotates automatically for you.

    List GPG Keys

  9. List keys to obtain a KeyID:

    gpg --list-secret-keys --keyid-format LONG

    Parse the RESPONSE:

    gpg: checking the trustdb
    gpg: marginals needed: 3  completes needed: 1  trust model: pgp
    gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
    gpg: next trustdb check due at 2022-03-01
    sec   rsa2048/62C414BA89BFBE52 2020-03-01 [SC] [expires: 2022-03-01]
    uid                 [ultimate] John Doe <john_doe+github@gmail.com>
    ssb   rsa2048/7F2026C2A22F2B37 2020-03-01 [E] [expires: 2022-03-01]

    NOTE: Only the “[ultimate]” key is used. Ignore any marked “[ revoked]”.

  10. Manually highlight and copy the GPG key ID, which is after “rsa2048/” in the sec section, 62C414BA89BFBE52 in the sample above.

    Alternately, use these Bash script lines to parse the key automatically:

    RESPONSE=$( gpg --list-secret-keys --keyid-format LONG | grep sec )
    # secLine="sec rsa2048/62C414BA89BFBE52 2020-03-01 [SC] [expires: 2022-03-01]"
    GPGKeyID=$( echo ${RESPONSE##*/} | cut -d " " -f 1 )
    echo $GPGKeyID
  11. To set your GPG signing key in Git, construct a command by substituting the GPG key ID you’d like to use with the value of $GPGKeyID:

    git config --global user.signingkey 62C414BA89BFBE52  #

    No response is expected from the command.

    The command updated the config file within the repo’s .git folder.

  12. Skip to section “Copy Paste GitHub” (below).

    OPTIONAL: Edit GPG key

    In case you want to fix a typo:

  13. Associate an email (value for field uid) with your GPG key, which Git requires by entering the edit-key mode:

    gpg --edit-key 62C414BA89BFBE52

    This results in this prompt:

  14. Specify “adduid” to enter that mode:

    gpg> adduid
  15. Enter in the series of prompts:

    Real Name: John Doe
    Email address: john-doe+github@gmail.com
    Comment: My Git signing key
    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

    The response is like:

    You selected this USER-ID: 
     "John Doe (My Git signing key) <john_doe+github@gmail.com>"
  16. Type “O” (capital or lowercase O) to save the entry.

    Copy and Paste in GitHub

  17. Prepare for pasting of the key generated in this next step by switching to an internet browser of the GitHub page that will receive the public key. After signing in, click your icon at the upper-right, select Settings, SSH and GPG keys:


  18. Click “New GPG key” for a form to accept the contents of the public GPG key, then press command+Tab to switch back to the Terminal.

  19. Navigate to your .ssh folder:

    cd ~/.ssh
  20. Print a command such as this to emit a public GPG key, in ASCII armor format so that they can be sent in a standard messaging format such as email. (Otherwise, the output is in binary format):

    gpg --armor --export 62C414BA89BFBE52 >$HOME/mygitsigning.pub

    PROTIP: Redirecting the command output to a file makes it easier and less error-prone than manually highlighting and copying.

  21. Copy the file’s contents to your operating system Clipboard:

    pbcopy < "$HOME/mygitsigning.pub"

    Alternately, on Windows, pipe file contents to the clip.exe program built in within C:\Windows\system32 *:

    type mygitsigning.pub | clip

    Alternately, open the file using a text editor, select all file contents, and copy to Clipboard.

    The public key contents should include markers

  22. Switch to the GitHub page opened and click on the input field (so the field border turns blue)
  23. Click “Add GPG key”.
  24. Type a name for the key. For example: “mac M1 16 2022”.
  25. Click on the space under the “Key” heading.
  26. Press command+V to paste what was saved to your invisible Clipboard using the pbcopy command above.

    PROTIP: IMPORTANT: If you lost your laptop, immediately log into GitHub Settings on another laptop to remove the SSH and GPG keys associated with that laptop.

    Require Signed Commits

  27. In GitHub, scroll down the same page to check “Flag unsigned commits as unverified” under the “Vigilent Mode” heading:


    GitHub Admins repos can require that commits be signed by specifying branch protection rules for all branches, for a specific branch, or for any branch that matches a name pattern matching a fnmatch syntax such as release for branches containing the word “release”.

    See https://help.github.com/en/github/administering-a-repository/about-required-commit-signing

    Signing Key

  28. Configure Git to use the program for signing:

    git config --global gpg.program gpg
  29. Configure Git to use your chosen key for signing (“0A46826A” in the example here):

    git config --global user.signingkey 62C414BA89BFBE52
  30. Configure Git to auto-sign ALL Git Tags (called annotations by Git):

    git config --global tag.forceSignAnnotated true

    Sign all commits

    PROTIP: Many say it’s not necessary to sign every commit, just the commit designated by a release.

  31. Configure Git to auto-sign ALL commits on ALL repos:

    git config --global commit.gpgsign true

    PROTIP: It takes a little more time to sign commits.

  32. Each command above adds an entry in file $HOME/.gitconfig created by the Git client:

     name = John Doe
     email = john_doe+github@gmail.com
     signingkey = 62C414BA89BFBE52
     program = gpg2

    git config –global gpg.program gpg2

  33. If you are using Bash, edit you ~/.bash_profile to avoid these error messages:

    error: gpg failed to sign the data
    fatal: failed to write commit object

    If using Zsh, edit your ~/.bashrc file.

    Add lines to the bottom of the Shell invocation file:

    test -r ~/.bash_profile && echo 'export GPG_TTY=$(tty)' >> ~/.bash_profile
    echo 'export GPG_TTY=$(tty)' >> ~/.profile

    GPG_TTY variable is to avoid errors.

    If using Zsh:

    echo 'export GPG_TTY=$(tty)' >> ~/.profile
  34. Confirm:

    echo $GPG_TTY

    Your number could be different in this example:

  35. Activate the setting by restarting your Terminal session. If using Bash:

    source ~/.bash_profile

    If using Zsh:

    source ~/.bashrc

Sign Git Commits & merges

  1. git clone a repo, then edit one of its files.

  2. Configure for the repo (and each additional repo) the private “no reply” email from above so your email is not exposed publicly:

    git config user.email "12345678+johndoe@users.noreply.github.com"
  3. Add the files changed:

    git add .

    This is not recommended by some, but …

  4. To sign a commit, if you didn’t specify signing every time, add command flag capital -S, construct a command replacing “Some message” in the command with your own message:

    GIT_TRACE=1 git commit -a -S -m "Some message"

    A sample response at time of writing:

    03:48:07.999728 exec-cmd.c:139          trace: resolved executable path from Darwin stack: /Library/Developer/CommandLineTools/usr/bin/git
    03:48:08.000435 exec-cmd.c:236          trace: resolved executable dir: /Library/Developer/CommandLineTools/usr/bin
    03:48:08.001587 git.c:418               trace: built-in: git commit -a -S -m 'Some message'
    03:48:08.017126 run-command.c:643       trace: run_command: gpg2 --status-fd=2 -bsau 62C414BA89BFBE52
    03:48:08.153175 run-command.c:643       trace: run_command: git gc --auto
    03:48:08.156446 exec-cmd.c:139          trace: resolved executable path from Darwin stack: /Library/Developer/CommandLineTools/usr/libexec/git-core/git
    03:48:08.157243 exec-cmd.c:236          trace: resolved executable dir: /Library/Developer/CommandLineTools/usr/libexec/git-core
    03:48:08.158689 git.c:418               trace: built-in: git gc --auto
    [master 71ad705] Some message
     1 file changed, 1 insertion(+)


    git commit --amend --no-edit --signoff
  5. After push, switch to an internet browser to see a verified badge next to your commits on GitHub online.


  6. Verify that green checkmark next to your name on GitHub.


View Signed Git Log

  1. See signing info with your latest commit in the git log:

    git log --show-signature -1

    The response would include, for example:

    commit 71ad7059817e609b52b29469e1214a56799b33ef (HEAD -> master)
    gpg: Signature made Mon Mar  2 11:07:39 2020 EST
    gpg:                using RSA key 0BB29E3C5216420CC50ACF8D62C414BA89BFBE51
    gpg: Good signature from "John Doe <john_doe+github@gmail.com>" [ultimate]
    Author: John Doe <johndoe+github@gmail.com>
    Date:   Sun Jun 20 22:54:17 2021 -0600
  2. To verify signatures during a git merge (and abort if signatures are not verifiable):

    git merge --verifya-signatures other_branch

Sign Git Tags

VIDEO: Git tags are committed and pushed by an additional command.

  1. Construct a command to create a Git tag (such as “v1.5.2”) to the current HEAD:

    GIT_TRACE=1 git tag -a -s v1.5.2 -m 'Signed tag 1.5.2'

    -a (annotation) puts the tag in the repository when pushed to GitHub.

    PROTIP: Git tags are like a branch name. in Semantic Versionioning format. See semver.com.

    GIT_TRACE=1 enables tracing. Example output on macOS:

    03:45:46.646487 exec-cmd.c:139          trace: resolved executable path from Darwin stack: /Library/Developer/CommandLineTools/usr/bin/git
    03:45:46.647227 exec-cmd.c:236          trace: resolved executable dir: /Library/Developer/CommandLineTools/usr/bin
    03:45:46.647782 git.c:418               trace: built-in: git tag -a -s v1.5.2 -m 'Signed tag 1.5.2'
    03:45:46.650392 run-command.c:643       trace: run_command: gpg2 --status-fd=2 -bsau 62C414BA89BFBE52

    You are prompted for the GPG key Passphrase.

    Alternately, construct a command to create a Git tag (such as “v1.5.2”) to a previous commit SHA (such as “f3c9f3a”):

    GIT_TRACE-1 git tag v1.5.2 f3c9f3a

    List Git tags

  2. For a list of all version 1 tags:

    git tag -l "v1.*"


    I don’t recommend this, but theoretically you can silence the “you need a Passphrase” prompt by adding in file ~/.gnupg/gpg.conf “batch”. But

    # Connects gpg-agent to the OSX keychain via the brew-installed
    # pinentry program from GPGtools. This is the OSX 'magic sauce',
    # allowing the gpg key's passphrase to be stored in the login
    # keychain, enabling automatic key signing.
    pinentry-program /usr/local/bin/pinentry-mac   

    Push by Tag

    PROTIP: REMEMBER: Tags are push of tags are in addition to content commits.

  3. For convenience (in scripts), push all tags to GitHub:

    git push --tags

    Alternately, specify the new Tag like a branch:

    git push origin v1.5.2

    A sample response:

    Enumerating objects: 1, done.
    Counting objects: 100% (1/1), done.
    Writing objects: 100% (1/1), 540 bytes | 540.00 KiB/s, done.
    Total 1 (delta 0), reused 0 (delta 0)
    To github.com:wilsonmar/git-utilities
            * [new tag]         v1.5.2 -> v1.5.2
  4. See Tags in GitHub under the Code tab, after clicking the release link above GitHub’s colorful line:


Configure VSCode

  1. Within VSCode, press command , (comma) or click the cog icon at the lower-left.
  2. On top of “Search settings” at the top, type “GPG”. No need to press Enter.
  3. Check “Enables commit signing with GPG or X.509.
  4. Click the X to the right of the top “Settings” tab to to dismiss that tab.

Delete Tags

Git tags such as “v1.5.2” are meant to be permanently associated with a particular commit through history.

  1. To delete a Tag locally:

    git tag -d v1.5.2

    Alternately, –delete is the long form of the -d parameter.

    Multiple tags can be specified in one command (separated by spaces).

  2. To delete a Tag in remote (GitHub):

    git push origin -d v1.5.2

    Alternately, the really short form replaces -d with a colon (:):

    git push origin :v1.5.2

    Tags in CI/CD

  3. View the list of tags with their full (40 character) hash using the git show-ref command:

    git show-ref --tags

    PROTIP: The above command was added as Bash shell alias (keyboard shortcut) in https://github.com/wilsonmar/git-utilities/blob/master/aliases.sh so that you can instead just type:


    The response is a list of full hashes with the path, such as:

    d4c1e33d1969c8b35938db498a556de25b8c3aa3 refs/tags/v1.5.2
  4. VIDEO: In CI/CD such as Jenkins, get the first among latest tags using the git ref-list command:

    COMMIT_ID=$( git rev-list --tags --date-order | head -1 )

    The response is simply a full hash, such as:

  5. Extract the Tag based on the hash using the git show-ref command:

    TAG=$( git show-ref --tags | grep "${COMMIT_ID}" | awk -F / '{print $NF}' )

    The variable is used to specify the version in a Docker Build, Push, then Kubernetes apply, such as:

    docker build -t "$DOCKER_ACCOUNT/$DOCKER_REPO:$TAG" .
    sed -e "s/VERSION/$TAG/" /home/centos/deployment.yml >/tmp/deployment.yml
    kubectl apply -f /tmp/deployment.yml
    kubectl get pods -o wide

BONUS: Encrypting whole files using GPG

GPG can also be used for encryption and decryption of whole files, such as an executable (.exe) file for transmission over email, etc (not related to Git).

There are several ways to verify both the integrity of a file during transmission (as hashing can do) but also provide a way for users to trace authorship.

The steps below describes work with a detached signature where a signature is created in a separate file. We can then provide both the package and the signature file from a trusted source. The user can then verify the package against it. This is like with a hash, but instead of a cleartext signature, the signature is in a “.sig” file which has been encrypted using a private key known only to the file’s owner.

BLOG: Users may want this level of verification for security reasons. Especially if the package handles sensitive information.

  1. Navigate to the folder where the file to be encrypted is located. For example:

    cd ~/.aws
  2. Get the signature, such as “62C414BA89BFBE52”.

    gpg --list-secret-keys --keyid-format LONG
  3. Copy the signing key marked “[ultimate]” and not “[revoked]”, such as “62C414BA89BFBE52” in this sample response:

    sec   rsa2048/62C414BA89BFBE52 2020-03-01 [SC] [expires: 2022-03-01]
    uid                 [ultimate] John Doe <john_doe+github@gmail.com>
  4. Plug in your Yubikey if the command response above has a line like this:

        Card serial no. = 0006 16252890
  5. Construct a variable by typing definitions of values to be used in commands:


    PROTIP: Defining variables allows copy and paste of the complex command below, easier than constructing it piecemeal. This also eliminates typos when the same values are specified in several commands.

  6. Construct a command to create a signed file by triple-clicking this command to copy to Clipboard the command to encrypt file “credentials” and output file “credentials.gpg”:

    gpg --detach-sign --sign-with "$SIGNING" -o "${ENCRYPTED_FILE}"  "${SOURCE_FILE}"

    --detach-sign requests a detached signature to be generated.

    --sign-with precedes the GPG key id to be used to perform signing.

    -o specifies the output file. Traditionally we use either a .sig or a .gpg extension.

  7. If you configured a Yubikey, a pop-up is presented:


    Insert your Yubikey and enter its PIN code.

  8. Verify that files were generated:

    ls -al "$ENCRYPTED_FILE"

Standard signing

Standard signing and clear signing both create ciphertext from the cleartext input file:

  • Standard signing is used with encryption.
  • Clear signing wraps the input with a plaintext signature.

  1. To sign a plaintext file with your secret key:

    gpg -s textfile
  2. To encrypt a plain text file with the user_id of the recipient’s public key:

    gpg -e -r recipient_userid textfile
  3. To sign a plaintext file with your secret key and have the output readable to people without running GPG first:

    gpg --clearsign textfile
  4. To sign a plaintext file with your secret key, and then encrypt it with the recipient’s public key:

    gpg -se -r recipient_userid
  5. To decrypt a ciphertext file to a clear text outputfile, also checking the signature integrity of a signed file:

    gpg -o outputfile ciphertextfile

CAUTION: Sending GPG-encrypted messages hides only the contents of the file but NOT meta-data such as the sender/receiver of the message nor the message size (bytes).

It’s up to you to make sure you’re actually talking to the intended person.

Getting Facebook to encrypt notification emails

In 2015, Facebook introduced an option to encrypt notification emails – account recovery emails, in particular. It’s done by you (the user) adding your OpenPGP public key to your Facebook profile.

  1. Generate your public and private keys.
  2. Copy and paste the text block of your PGP public key, starting with: —–BEGIN PGP PUBLIC KEY BLOCK—– and including —–END PGP PUBLIC KEY BLOCK—– at the end. (On a Mac, I exported my public key as a plain-text ASC file from the GPG Keychain application that I was then able to open in TextEdit to copy the text block mentioned above.)

  3. In Facebook, login and navigate to your About page, “Contact and Basic Info” section:


  4. Click “Add a public key”.
  5. Paste.
  6. Check box “Use this public key to encrypt notification emails that Facebook sends you”.
  7. Click “Save Changes”.
  8. In your email program, decrypt the email from Facebook and click the link.

Encrypt emails in transit

There are two ways to encrypt emails in transit using asymetric keys: S/MIME and TLS.

CAUTION: If you use Google’s native Gmail client, since Google does the encryption using your key, Google (and law enforcement) always has access to your clear text body data.

Even if you use an out-of-browser program, due to the nature of the SMTP open protocol used for email, all of your metadata is still known to mail services. OpenPGP addresses message content rather than SMTP or TCP/IP transport metadata.

CAUTION: The downside of encrypting Body data is that Google will not apply its powerful engine to check for spam in the body of incoming messages (one of the main reasons we use Gmail). Since you publicly publish your public key, spammers can send you encrypted email which are not filtered by Google’s spam engine. So before opening ANY message from an unknown source, verify the URL using virustotal.com.


  1. If you’re not the Workspace Administrator, ask for S/MIME to be enabled.

  2. Using a Workspace Admin email address,


    Alternately, sign in to your Google Workspace admin dashboard:

    Then click the “sandwich” icon for the menu and select Apps > Google Workspace > Gmail > User Settings.

  3. Accept the terms of service if that comes up.

  4. Change the “Enable S/MIME encryption” for sending and receiving emails option.

    Here is where I’m personally stuck because I don’t see the options shown in this Computerworld article:

    Google Gmail SMIME menu choice

    Google Gmail SMIME menu choice

    S/MIME makes use of digital signatures to confirm that the sender’s email address was actually the email address used to send it.

  5. Obtain your email’s S/MIME certificate from a Certificate Authority (CA).

    Italian CA Actalis.it provides them free of charge based on just your email (valid for 1 year).

    1. Copy the verification code from the email to paste on their web page.
    2. Copy the password on their web page and save in your 1Password or other secret vault.
    3. Open the email to save the SMIME key and your Private Personal Code (CRP).

    4. Save that verification code, password, and Private Personal Code in your 1Password
    5. Delete the email.

    Certificate authorities provide S/MIME certificate bundles either as a PKCS #12 file (.p12 or .pfx) if they generated the certificate for you or as a PKCS #7 (.p7b) file if you created the private key on your own computer and submitted a Certificate Signing Request (CSR) to the CA.

    CAUTION: Can you trust Actalis.it to keep you certs secret? I don’t know.

  6. Rules can be set up to require outgoing messages be sent with S/MIME (Secure/Multipurpose Internet Mail Extensions) for encryption.

    “Verified” email address indicates that the associated email address is validated by a digital signature.


Don’t expect Google to set up site-wide end-to-end encryption, however. For Google to monetize Gmail, it must be able to scan messages in order to serve targeting ads to users. It’s an advertising business, after all.

  1. In Gmail.com, on a usually paid) Enterprise edition of Gmail:
  2. Click “Compose” to start composing a message.
  3. Add a recipients to the “To” field.
  4. To the right of the Subject:, a lock icon shows the level of encryption supported by your message’s recipients. If there are multiple users with various encryption levels, the icon will show the lowest encryption status:

    • Green (S/MIME enhanced encryption) Encryption on. Suitable for your most sensitive information. S/MIME encrypts all outgoing messages if we have the recipient’s public key. Only the recipient with the corresponding private key can decrypt this message.

    • Gray = “standard” TLS (Transport Layer Security) used for messages exchanged with other email services who don’t support S/MIME. Encryption using TLS ensures that no third party can overhear or tamper with messages When a server and client communicate. For delivery TLS to work, the email delivery services of both the sender and the receiver always have to use TLS. That’s like why we send important messages in sealed envelopes rather than on the back of postcards. Tip: TLS support is not guaranteed. Support is inferred from past communications with the email service.

    • Red (no encryption) No encryption. That mail is not secure. Past messages sent to the recipient’s domain are used to predict whether the message you’re sending won’t be reliably encrypted.

  5. Click “View Details”
  6. Remove addressees or delete confidential information for emails marked red.

  7. To change your S/MIME settings or learn more about your recipient’s level of encryption, click the lock, then View details.

B. Check if a message you received is encrypted

  1. In a Gmail account with S/MIME enabled, open a message.
  2. On an Android device: Tap View details and then View security details.
  3. On an iPhone or iPad: Tap View details.
  4. You’ll see a colored lock icon that shows you what level of encryption was used to send the message.


That mechanism involves an exchange of cryptographic certificates. mTLS (Mutual Transport Layer Security)

Protonmail can be the recipient.

CA certificates trusted by Gmail for S/MIME



This article was the result of consulting several sources of information:

As with all things Git, the canonical documentation is at git-scm. Regarding Git signing: https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work



Explanation of gpg program parameters are at: https://www.gnupg.org/documentation/manuals/gnupg/GPG-Input-and-Output.html

Protect Your Git Repositories From Commit Forgery Using Signing


VIDEO: [Git/GitHub] Signing your commits in GitHub – Getting the verified badge on your commits by Raveesh Agarwal


https://juliansimioni.com/blog/troubleshooting-gpg-git-commit-signing quotes https://wiki.gentoo.org/wiki/GnuPG#Changing_pinentry_for_SSH_logins




VIDEO: [Git/GitHub] Signing your commits in GitHub – Getting the verified badge on your commits</a>

A Git Horror Story: repository integrity with signed commits

https://www.thegeekyway.com/hands-on-guide-on-gpg-keys/ by GeekyShacklebolt


VIDEO: Source Control Tip 19: Signing a commit via GPG


5 Applications of Digital Signatures

More on DevOps

This is one of a series on DevOps:

  1. DevOps_2.0
  2. ci-cd (Continuous Integration and Continuous Delivery)
  3. User Stories for DevOps
  4. Enterprise Software)

  5. Git and GitHub vs File Archival
  6. Git Commands and Statuses
  7. Git Commit, Tag, Push
  8. Git Utilities
  9. Data Security GitHub
  10. GitHub API
  11. TFS vs. GitHub

  12. Choices for DevOps Technologies
  13. Pulumi Infrastructure as Code (IaC)
  14. Java DevOps Workflow
  15. Okta for SSO & MFA

  16. AWS DevOps (CodeCommit, CodePipeline, CodeDeploy)
  17. AWS server deployment options
  18. AWS Load Balancers

  19. Cloud services comparisons (across vendors)
  20. Cloud regions (across vendors)
  21. AWS Virtual Private Cloud

  22. Azure Cloud Onramp (Subscriptions, Portal GUI, CLI)
  23. Azure Certifications
  24. Azure Cloud

  25. Azure Cloud Powershell
  26. Bash Windows using Microsoft’s WSL (Windows Subsystem for Linux)
  27. Azure KSQL (Kusto Query Language) for Azure Monitor, etc.

  28. Azure Networking
  29. Azure Storage
  30. Azure Compute
  31. Azure Monitoring

  32. Digital Ocean
  33. Cloud Foundry

  34. Packer automation to build Vagrant images
  35. Terraform multi-cloud provisioning automation
  36. Hashicorp Vault and Consul to generate and hold secrets

  37. Powershell Ecosystem
  38. Powershell on MacOS
  39. Powershell Desired System Configuration

  40. Jenkins Server Setup
  41. Jenkins Plug-ins
  42. Jenkins Freestyle jobs
  43. Jenkins2 Pipeline jobs using Groovy code in Jenkinsfile

  44. Docker (Glossary, Ecosystem, Certification)
  45. Make Makefile for Docker
  46. Docker Setup and run Bash shell script
  47. Bash coding
  48. Docker Setup
  49. Dockerize apps
  50. Docker Registry

  51. Maven on MacOSX

  52. Ansible
  53. Kubernetes Operators
  54. OPA (Open Policy Agent) in Rego language

  55. MySQL Setup

  56. Threat Modeling
  57. SonarQube & SonarSource static code scan

  58. API Management Microsoft
  59. API Management Amazon

  60. Scenarios for load
  61. Chaos Engineering

More on Security

This is one of a series on Security in DevSecOps:

  1. Security actions for teamwork and SLSA
  2. DevSecOps

  3. Code Signing on macOS
  4. Transport Layer Security

  5. Git Signing
  6. GitHub Data Security
  7. Encrypt all the things

  8. Azure Security-focus Cloud Onramp
  9. Azure Networking

  10. AWS Onboarding
  11. AWS Security (certification exam)
  12. AWS IAM (Identity and Access Management)
  13. AWS Networking

  14. SIEM (Security Information and Event Management)
  15. Intrusion Detection Systems (Goolge/Palo Alto)
  16. Chaos Engineering

  17. SOC2
  18. FedRAMP
  19. CAIQ (Consensus Assessment Initiative Questionnaire) by cloud vendors

  20. AKeyless cloud vault
  21. Hashicorp Vault
  22. Hashicorp Terraform
  23. OPA (Open Policy Agent)

  24. SonarQube
  25. WebGoat known insecure PHP app and vulnerability scanners
  26. Test for OWASP using ZAP on the Broken Web App

  27. Security certifications
  28. Details about Cyber Security

  29. Quantum Supremecy can break encryption in minutes
  30. Pen Testing
  31. Kali Linux

  32. Threat Modeling
  33. WebGoat (deliberately insecure Java app)