Wilson Mar bio photo

Wilson Mar


Email me Calendar Skype call

LinkedIn Twitter Gitter Instagram Youtube

Github Stackoverflow Pinterest

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

US (English)   Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Cyrillic Russian   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean


This article is a step-by-step tutorial on how to setup and use GPG signatures for Git to sign commits and tags, for non-repudiation.


“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 the logical ordering of deep-dive concepts presented in a succint way, as a hands-on narrated scenic tour. “PROTIP” flags advice from hard-won experience such as relevant keyboard shortcuts and things to remember, available only here for you.

TL;DR Generic workflow

The workflow aside from the tooling variations (described below):

  1. Install apps and programs locally
  2. Configure emails and set up GitHub to require signing
  3. Generate and list keys
  4. Add public GPG key to GitHub
  5. Sign Git commits and merges
  6. Sign Git Tags
  7. Import key to GPG on another host

BONUS: Since we’re using GPG, here are also notes about signing of whole files using GPG.

Decisions: Variations

There are several variations (decisions) regarding the workflow to use:

  • Operating system of local machine (macOS, Windows, Linux flavors)
  • Install a GUI app and/or Command-line program to sign keys
  • Download installer from publisher web page or run package manager (Homebrew, Chocolately)

  • The secret-keeping service (macOS Keychain, GPG, Yubikey, Keybase.io, employer-specified, etc.)
  • Whether to sign every commit or just git tags per release

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.

Install client utilities

The alternatives:

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

  • JAMF for macOS machines
  • Microsoft InTune for Windows laptops.

Install on macOS GUI app GPG-Suite

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.

    NOTE: Installation is to folder/file “/Applications/GPG Keychain.app”. To remove the app later, simply delete that file.

  3. Pinch 4 fingers together on the Touchpad and scroll around for apps.

  4. Type enough of “GPG Keychain” for the icon to appear for you to click:


    Gen GPG using macOS GPG-Suite

  5. Ignore the two keys already there.

  6. To generate a GPG key pair click “+ New”, then Advanced, select Key Type “RSA {Sign Only)”.


  7. Define a new password in your password vault, then copy and paste that new password in the two fields.
  8. “Create Key”.
  9. “No, Thanks!” when asked to upload your public key. You can do that later.

    Install on macOS CLI utility gnupg2

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

  11. Execute a Bash script to do the following:

    Alternately, manually install brew (Homebrew)

  12. Install a Git client:

    brew install git

    This installs a bunch, including the latest Python (3.9.4).

  13. For information about the brew gpg2 install:

    brew info gnupg2

    The response at time of writing:

    gnupg: stable 2.2.21 (bottled)
    GNU Pretty Good Privacy (PGP) package
    /usr/local/Cellar/gnupg/2.2.21 (134 files, 11.2MB) *
      Poured from bottle on 2020-07-09 at 18:44:27
    From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gnupg.rb
    License: GPL-3.0
    ==> Dependencies
    Build: pkg-config ✔
    Required: adns ✔, gettext ✔, gnutls ✔, libassuan ✔, libgcrypt ✔, libgpg-error ✔, libksba ✔, libusb ✔, npth ✔, pinentry ✔
    ==> Analytics
    install: 55,008 (30 days), 120,808 (90 days), 506,457 (365 days)
    install-on-request: 47,841 (30 days), 104,969 (90 days), 428,775 (365 days)
    build-error: 0 (30 days)
  14. Verify CLI:

    gpg --version
    gpg (GnuPG/MacGPG2) 2.2.24
    libgcrypt 1.8.7
    Copyright (C) 2020 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 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/wilson_mar/.gnupg
    Supported algorithms:
    Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
    Compression: Uncompressed, ZIP, ZLIB, BZIP2
  15. 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

    MacOS GPG Config

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

  16. Update or Create ~/.gnupg/gpg.conf

    code "$HOME/.gnupg/gpg.conf"
    Visual Studio Code should open with lines such as:
  17. 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
  18. Update permissions on your ~/.gnupg Directory:

    chmod 700 ~/.gnupg
  19. Proceed to Configuration

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
  2. 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.

    Email address in GitHub

  3. 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)

  4. Switch to your GitHub Profile Email page


  5. Identify your “no-reply” public email address, such as “john_doe+github@gmail.com”.

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

Require Signed Commit on GitHub

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

Generate and store keys

There are several places you can store GPG keys securely:


  • Azure KeyVault?
  • AWS ?
  • Google Cloud?

Hashicorp Vault

In a Terminal:

  1. Install Hashicorp Vault program on your Mac:

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

    vault --version
    Vault v1.6.0 ('7ce0bd9691998e0443bc77e98b1e2a4ab1e965d4+CHANGES')
  3. If you don’t have a Hashicorp Vault server, follow my instructions to run it 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@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. 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 becuase 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.

    The Suite requires to be installed “brew install pinentry-mac”, activated by then entry in file ~/.gnupg/gpg-agent.conf

    pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac
  3. If you are not using a Yubikey, proceed to Generate GPG key pairs.

    Optional Yubikey smart chip

    This is for those who work on multiple machines but want to use a single physical signing key they plug into each machine.

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

    git-siging-yubikey-100x100.jpg Instead of storing private keys on a laptop’s hard drive (where they can be hacked by 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).

    PROTIP: If you lose your physical dongle, you’ll need to re-generate all keys.

    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.

  4. 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?

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

  6. Insert your YubiKey and run:

    gpgp --card-status

    If you see these messages:

    gpg: selecting openpgp failed: Operation not supported by device
    gpg: OpenPGP card not available: Operation not supported by device   

    BLOG: continue …


    The response is like this:

    Reader ...........: Yubico Yubikey NEO OTP U2F CCID
    Application ID ...: ID
    Version ..........: 2.0
    Manufacturer .....: Yubico
    Serial number ....: serial
    Name of cardholder: [not set]
    Language prefs ...: [not set]
    Sex ..............: unspecified
    URL of public key : [not set]
    Login data .......: [not set]
    Signature PIN ....: not forced

    References on Yubikey on macOS Git:

    • 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. 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:
  2. 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.

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

  4. 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.

  5. Re-enter the key.

  6. 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 62C414BA89BFBE52 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]

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

    “rsa2048” is the encryption algorithm used.

  7. List keys to obtain a KeyID.

    RESPONSE=$( 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]
  8. 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
  9. To set your GPG signing key in Git, substitute 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.

    OPTIONAL: Edit GPG key

    In case you want to fix a typo:

  10. 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:

  11. Specify “adduid” to enter that mode:

    gpg> adduid
  12. 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>"
  13. Type “O” (capital or lowercase O) to save the entry.

    <a name=”CopyPasteGitHub”</a>

    Copy and Paste in GitHub

  14. 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:


  15. 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.

  16. Print the 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.

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

    pbcopy < "$HOME/mygitsigning.pub"

    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 “—–BEGIN PGP PUBLIC KEY BLOCK—–” and “—–END PGP PUBLIC KEY BLOCK—–”.

  18. Switch to the GitHub page opened and click on the input field (so the field border turns blue), then press command+V to paste. Click “Add GPG key”.

    PROTIP: IMPORTANT: If you lost your laptop, immediately remove the SSH and GPG keys associated with that laptop.

    Signing Key

  19. Configure Git to use the program for signing:

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

    git config --global user.signingkey 62C414BA89BFBE52
  21. 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.

  22. 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.

  23. 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
  24. 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.

  25. Confirm:

    echo $GPG_TTY


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

    source ~/.bash_profile

    If using Zsh:

    source ~/.bashrc

Sign Git Commits & merges

  1. Edit some file
  2. Add

    git add .

    This is not recommended by some, but …

  3. 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(+)
  4. After push, switch to an internet browser to see a verified badge next to your commits on GitHub online.


Specif -S to sign a commit and tag:

git commit -m “test signed commit” -S git tag -m “test signed tag” -s test  

Verify the same commit and tag.

git verify-commit HEAD git log -1 –show-signature git verify-tag test </pre>

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

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.*"
  3. 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]


    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.

  4. 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
  5. See Tags in GitHub under the Code tab, after clicking the release link above GitHub’s colorful line:


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. Get the signature, such as “62C414BA89BFBE52”.

  2. To create a signed file:

    gpg --detach-sign --sign-with 62C414BA89BFBE52 -o package.sig package.exe

    --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.

  3. For a user to verify integrity of the file:

    gpg --verify package.sig package.exe

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


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

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. Cloud services comparisons (across vendors)
  19. Cloud regions (across vendors)
  20. AWS Virtual Private Cloud

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

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

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

  31. Digital Ocean
  32. Cloud Foundry

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

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

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

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

  50. Maven on MacOSX

  51. Ansible

  52. MySQL Setup

  53. SonarQube & SonarSource static code scan

  54. API Management Microsoft
  55. API Management Amazon

  56. Scenarios for load

More on Security

This is one of a series on Security in DevSecOps:

  1. Git Signing
  2. Hashicorp Vault

  3. WebGoat known insecure PHP app and vulnerability scanners
  4. Test for OWASP using ZAP on the Broken Web App

  5. Encrypt all the things

  6. AWS Security (certification exam)
  7. AWS IAM (Identity and Access Management)

  8. Cyber Security
  9. Security certifications