<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/default.xsl"?>
<fr:tree xmlns:fr="http://www.forester-notes.org" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xml="http://www.w3.org/XML/1998/namespace" root="false" base-url="/">
  <fr:frontmatter>
    <fr:authors>
      <fr:author>
        <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
      </fr:author>
    </fr:authors>
    <fr:date>
      <fr:year>2025</fr:year>
      <fr:month>4</fr:month>
      <fr:day>1</fr:day>
    </fr:date>
    <fr:uri>https://xw.is/0003/</fr:uri>
    <fr:display-uri>0003</fr:display-uri>
    <fr:route>/0003/</fr:route>
    <fr:title text="Git › Sign Git commits with SSH keys"><fr:link href="/git-index/" title="Git" uri="https://xw.is/git-index/" display-uri="git-index" type="local">Git</fr:link> › Sign Git commits with SSH keys</fr:title>
  </fr:frontmatter>
  <fr:mainmatter>
    <html:p><fr:link href="/git/" title="Git distributed version control system" uri="https://xw.is/git/" display-uri="git" type="local">Git</fr:link> can use <fr:link href="/ssh-keygen.1" title="ssh-keygen(1)" uri="https://xw.is/ssh-keygen.1" display-uri="ssh-keygen.1" type="local">ssh-keygen(1)</fr:link> to sign commits and tags. The problem is that if you want to avoid entering passphrases it requires a running <fr:link href="/ssh-agent.1" title="ssh-agent(1)" uri="https://xw.is/ssh-agent.1" display-uri="ssh-agent.1" type="local">ssh-agent(1)</fr:link>. This is the case even if you configured <fr:link href="/openssh/" title="OpenSSH" uri="https://xw.is/openssh/" display-uri="openssh" type="local">ssh</fr:link> to <fr:link href="/0004/" title="macOS › Use the Apple Keychain for ssh key passphrases" uri="https://xw.is/0004/" display-uri="0004" type="local">automatically decrypt keys using the iCloud Keychain</fr:link>, as <fr:link href="/ssh-keygen.1" title="ssh-keygen(1)" uri="https://xw.is/ssh-keygen.1" display-uri="ssh-keygen.1" type="local">ssh-keygen(1)</fr:link> will not read <html:code>~/.ssh/config</html:code>.</html:p>
    <html:p>We're going to make <fr:link href="/git.1" title="git(1)" uri="https://xw.is/git.1" display-uri="git.1" type="local">git(1)</fr:link> run a helper script that temporarily loads the required key into the <fr:link href="/ssh-agent.1" title="ssh-agent(1)" uri="https://xw.is/ssh-agent.1" display-uri="ssh-agent.1" type="local">ssh agent</fr:link>, setting a short timeout.</html:p>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="Create helper script">Create helper script</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>Create this executable file and put it in your <html:code>PATH</html:code>, I'm using <html:code>~/bin/git-config-helper-gpg.ssh.defaultKeyCommand</html:code>.</html:p>
        <html:pre>#/bin/bash

ssh-add -q -t 5 --apple-load-keychain ~/.ssh/id_ed25519
KEY=$(ssh-add -L | head -n 1)
echo key::$KEY</html:pre>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="Configure Git to use SSH for signing">Configure Git to use SSH for signing</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:pre>git config --global gpg.format ssh
git config --global gpg.ssh.defaultKeyCommand ~/bin/git-config-helper-gpg.ssh.defaultKeyComma</html:pre>
        <html:p>This configures <fr:link href="/git.1" title="git(1)" uri="https://xw.is/git.1" display-uri="git.1" type="local">git(1)</fr:link> to use SSH signing (as opposed to GPG) and instructs it to run our helper script when it needs to sign.</html:p>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="Configure Git to verify signatures">Configure Git to verify signatures</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>Record a list of known email-signature pairs in <html:code>.ssh/allowed_signers</html:code> (file name and location is arbitrary):</html:p>
        <html:pre>aram@mgk.ro ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMRc0UWKrFpCv/EOUo2jpEQt+C/pa0tc1rUWKgjbKTp7 aram@edengate.local</html:pre>
        <html:p>Then configure <fr:link href="/git.1" title="git(1)" uri="https://xw.is/git.1" display-uri="git.1" type="local">git(1)</fr:link> to use this file for verification:</html:p>
        <html:pre>git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers</html:pre>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="Sign every commit">Sign every commit</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>Optionally, you might want to automatically sign every commit and tag:</html:p>
        <html:pre>git config --global commit.gpgsign true
git config --global tag.gpgsign true</html:pre>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="How to use">How to use</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>Sign your commits using <html:code>git commit -S</html:code> (or enable autosigning). To check signatures use <html:code>git log --show-signature</html:code>.</html:p>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false">
      <fr:frontmatter>
        <fr:authors>
          <fr:author>
            <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
          </fr:author>
        </fr:authors>
        <fr:date>
          <fr:year>2025</fr:year>
          <fr:month>4</fr:month>
          <fr:day>1</fr:day>
        </fr:date>
        <fr:title text="GitHub">GitHub</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <html:p>GitHub <fr:link href="https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification" type="external">needs to be aware of signing keys</fr:link>. <html:mark>The SSH key <fr:link href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account" type="external">has to be specifically marked as a signing key</fr:link> in order for GitHub to show "verified" status.</html:mark></html:p>
      </fr:mainmatter>
    </fr:tree>
  </fr:mainmatter>
  <fr:backmatter>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="References">References</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Context">Context</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Backlinks">Backlinks</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Related">Related</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://xw.is/git/</fr:uri>
            <fr:display-uri>git</fr:display-uri>
            <fr:route>/git/</fr:route>
            <fr:title text="Git distributed version control system">Git distributed version control system</fr:title>
            <fr:taxon>Software</fr:taxon>
            <fr:meta name="external">https://git-scm.com</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://xw.is/openssh/</fr:uri>
            <fr:display-uri>openssh</fr:display-uri>
            <fr:route>/openssh/</fr:route>
            <fr:title text="OpenSSH">OpenSSH</fr:title>
            <fr:taxon>Software</fr:taxon>
            <fr:meta name="external">https://www.openssh.com</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>Premier connectivity tool for remote login with the <fr:link href="https://www.openssh.com/specs.html" type="external">SSH protocol</fr:link>.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://xw.is/git.1</fr:uri>
            <fr:display-uri>git.1</fr:display-uri>
            <fr:route>/git.1</fr:route>
            <fr:title text="git(1)">git(1)</fr:title>
            <fr:taxon>Manual</fr:taxon>
            <fr:meta name="external">https://git-scm.com/docs/git</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p><fr:link href="/git/" title="Git distributed version control system" uri="https://xw.is/git/" display-uri="git" type="local">Git</fr:link> command line client.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://xw.is/ssh-agent.1</fr:uri>
            <fr:display-uri>ssh-agent.1</fr:display-uri>
            <fr:route>/ssh-agent.1</fr:route>
            <fr:title text="ssh-agent(1)">ssh-agent(1)</fr:title>
            <fr:taxon>Manual</fr:taxon>
            <fr:meta name="external">https://man.openbsd.org/ssh-agent.1</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>OpenSSH authentication agent.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors />
            <fr:uri>https://xw.is/ssh-keygen.1</fr:uri>
            <fr:display-uri>ssh-keygen.1</fr:display-uri>
            <fr:route>/ssh-keygen.1</fr:route>
            <fr:title text="ssh-keygen(1)">ssh-keygen(1)</fr:title>
            <fr:taxon>Manual</fr:taxon>
            <fr:meta name="external">https://man.openbsd.org/ssh-keygen.1</fr:meta>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>OpenSSH authentication key utility.</html:p>
          </fr:mainmatter>
        </fr:tree>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:author>
                <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
              </fr:author>
            </fr:authors>
            <fr:date>
              <fr:year>2022</fr:year>
              <fr:month>7</fr:month>
              <fr:day>28</fr:day>
            </fr:date>
            <fr:uri>https://xw.is/0004/</fr:uri>
            <fr:display-uri>0004</fr:display-uri>
            <fr:route>/0004/</fr:route>
            <fr:title text="macOS › Use the Apple Keychain for ssh key passphrases"><fr:link href="/macos-index/" title="macOS" uri="https://xw.is/macos-index/" display-uri="macos-index" type="local">macOS</fr:link> › Use the Apple Keychain for ssh key passphrases</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>On <fr:link href="/apple-tn2449/" title="OpenSSH updates in macOS 10.12.2" uri="https://xw.is/apple-tn2449/" display-uri="apple-tn2449" type="local">modern macOS systems</fr:link>, you can set up your system such that your encrypted <fr:link href="/openssh/" title="OpenSSH" uri="https://xw.is/openssh/" display-uri="openssh" type="local">ssh</fr:link> keys get automatically decrypted using the passphrase stored in the Apple Keychain (which gets unlocked at login).</html:p>
            <fr:tree show-metadata="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2022</fr:year>
                  <fr:month>7</fr:month>
                  <fr:day>28</fr:day>
                </fr:date>
                <fr:title text="Add your passphrase to the Keychain">Add your passphrase to the Keychain</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Create your keys as usual, then do this once for every key:</html:p>
                <html:pre>ssh-add --apple-use-keychain ~/.ssh/id_ed25519</html:pre>
                <html:p>
                  <html:mark>You must use the full path your key, a relative path will not work!</html:mark>
                </html:p>
                <html:p>Note that we're <fr:link href="/ssh-add.1" title="ssh-add(1)" uri="https://xw.is/ssh-add.1" display-uri="ssh-add.1" type="local">adding</fr:link> the key to the <fr:link href="/ssh-agent.1" title="ssh-agent(1)" uri="https://xw.is/ssh-agent.1" display-uri="ssh-agent.1" type="local">ssh agent</fr:link> here, but we will not be using the ssh agent. Rather, this operation has the side effect that the key will be saved in the keychain.</html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/aram/" title="Aram Hăvărneanu" uri="https://xw.is/aram/" display-uri="aram" type="local">Aram Hăvărneanu</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2022</fr:year>
                  <fr:month>7</fr:month>
                  <fr:day>28</fr:day>
                </fr:date>
                <fr:title text="Configure ssh to use the Keychain">Configure <fr:link href="/openssh/" title="OpenSSH" uri="https://xw.is/openssh/" display-uri="openssh" type="local">ssh</fr:link> to use the Keychain</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>Add this to your <html:code>~/.ssh/config</html:code>:</html:p>
                <html:pre>Host *
   IgnoreUnknown UseKeychain
   UseKeychain yes</html:pre>
                <html:p><html:code>IgnoreUnknown</html:code> is there so that <html:code>UseKeychain</html:code> (an Apple-specific extension) will work with non-Apple ssh implementations. Contrary to Internet misinformation you do not need to use <fr:link href="/ssh-agent.1" title="ssh-agent(1)" uri="https://xw.is/ssh-agent.1" display-uri="ssh-agent.1" type="local">ssh-agent(1)</fr:link>, or enable any special Apple-specific options for the <fr:link href="/ssh-agent.1" title="ssh-agent(1)" uri="https://xw.is/ssh-agent.1" display-uri="ssh-agent.1" type="local">ssh agent</fr:link>. Of course you can do this per-host, or per-key, but then <html:code>IgnoreUnknown</html:code> should come early in your configuration, before any use of <html:code>UseKeychain</html:code>.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Contributions">Contributions</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
  </fr:backmatter>
</fr:tree>
