gitintellij-ideagnupg

How to sign git commits from within an IDE like IntelliJ?


Question How do I sign git commits using an IDE like IntelliJ on Windows?

If you're interested, read what I tried here:

I followed mainly Github's guide. I made sure to not forget to restart bash/IntelliJ after you changed config files.

  1. Generate a new GPG key pair
  2. Add the GPG key to my GitHub account
  3. Associate a verified (by GitHub) email with my GPG key where I made sure the email in my .gitconfig is the same.
  4. Tell Git about my GPG key
  5. Sign a commit with GPG and confirmed that it was Verfied on Github.
  6. From that same page, I set commits to be signed by default with git config --global commit.gpgsign true (I'm using git 2.12). I made a new commit and verified it was signed with git verify-commit HEAD
  7. When I try to commit in IntelliJ, I get gpg: cannot open tty 'no tty' so I found IntelliJ fails to commit changes when attempting to sign commit (GPG) and added no-tty to my C:\Users\username\.gnupg\gpg.conf file and restarted.

  8. Then I get the error gpg: Sorry, no terminal at all requested - can't get input which seems reasonable because I just added the option that has something to do with no terminal. Like gpg: Sorry, no terminal at all requested - can't get input says the solution is to remove the no-tty which I hope doesn't apply to my case.

  9. Other answers in the first question suggested to add use-agent as well in the gpg.conf file, which results in the additional error gpg: gpg-agent is not available in this session. Ah wait, maybe I need to setup gpg-agent.

  10. The best guide for Windows that I could find was the Archlinux wiki (yeah, right). It specifies to add to C:/Users/username/.gnupg/gpg-agent.conf the time to live, so I create that file and add default-cache-ttl 34560000 and max-cache-ttl 34560000 as per https://superuser.com/questions/624343/keep-gnupg-credentials-cached-for-entire-user-session

  11. Now let's actually start this gpg-agent, https://superuser.com/questions/1153101/why-does-git-complain-that-no-gpg-agent-is-running made me check that indeed gpg-agent --version was much newer than gpg --version, so I would like to have gpg2 so I could run git config --global gpg.program gpg2.

  12. But I do not have gpg2 available on the command line. I installed Gpg4win (binary releases, at the bottom) and even Gnupg 2 separately but that didn't give me gpg2 on the command line, I noticed I had to folder GNU in my Program Files (x86) which I think I should have. With where gpg I found out it was at least not pointing to the gpg I just downloaded, because that one showed second. So I pointed git to the right one with git config --global gpg.program 'C:\Program Files (x86)\GnuPG\bin\gpg.exe'

  13. Now I have the error gpg: skipped "keyid": secret key not available. The solution in gpg: skipped "N": secret key not available is what I just did, so that doesn't help. Then I realised I set everything up with the other gpg, not with this one. I did alias gpg="'C:\Program Files (x86)\GnuPG\bin\gpg.exe'", checked gpg --version and did the whole thing again. Actually I put the alias line in my .bash_profile so I don't need to run it every time.

  14. When I try gpg --gen-key it hangs immediately. No idea why, I don't think the problem is not enough entropy because the older gpg worked fine but it's possible that the newer version requires more entropy. In any case I couldn't find any windows user with the same problem on the Internet.

  15. It works! When I commit in IntelliJ, it asks for my passphrase with pinentry only once. But now I can't commit from Git Bash, with the no secret key error which makes sense because as I said gpg --list-keys is empty: no key is associated with this gpg.

  16. Intellij IDEA signing GIT commits with GPG is relevant, but the only answer is for MAC and doesn't seem to apply to Windows. It did lead me to:

  17. me.m01.eu hints at adding a new environment variable called GNUPGHOME which points to C:\Users\username\.gnupg. That directory exists, but as mentioned in an answer from git commit signing failed: secret key not available I think my new gpg uses C:\Users\username\AppData\Roaming\gnupg so I added that instead. I checked with printenv GNUPGHOME that I added it correctly (I had to reboot). Didn't change anything though.

  18. Since my keys are I think in C:\Users\username\.gnupg I tried pointing the environment variable to there, but it didn't help, gpg --list-keys was still empty. So I had to find another way of pointing out to gpg where my keys are.

  19. gpg --list-keys --homedir='C:/Users/s156757/.gnupg' did give the correct keys, so I decided to add homedir C:\Users\s156757\.gnupg to my C:\Users\username\AppData\Roaming\gnupg\gpg.conf file which I had to create. Because of this bug. I confirmed gpg --list-keys returned my keys. Still the same error, adding no-ttyand use-agent to this conf file didn't help.

I can now commit from within IntelliJ but not anymore with Git Bash, which results in skipped "keyid": No secret key.

More thoughts

  1. I didn't manage to export keys from gpg to gpg2 with gpg --export [ID] | gpg2 --import && gpg --export-secret-keys [ID] | gpg2 --import
  2. I thought Gpg4win alone maybe could be configured so I don't need to type my password every time. Unfortunately, I couldn't find anywhere a way to make Gpg4win remember my passphrase.
  3. I later thought, if I remember correctly gpg --list-secret-keys has never returned anything for gpg 2. I only later found out that that command is different from gpg --list-keys.

In short this is the main problem: gpg-agent allows passphrase caching but the gpg version of git doesn't match the gpg-agent version so you have to install the right gpg 2 yourself first. But I didn't manage to do that installation in such a way that I could commit from both Git Bash and IntelliJ.


Solution

  • Since git 2.19.1, gpg2 is supported!

    gpg-agent can handle automatic signing now, remembering your passphrase

    This should make it easier to use gpg to sign commits automatically. To be exact, git version 2.19.1 has at least gpg 2.2.9. These instructions were tested on Windows 7, Windows 8.1, Windows 10, Arch Linux and Fedora 29.

    Steps to configure git commit signing

    1. It is recommended that you have set up ssh, see e.g. help.github.com/en/articles/connecting-to-github-with-ssh (also when you don't use GitHub)
    2. Start (on Linux) a terminal or (on Windows) git bash, check with git --version that git is at least 2.19.1 and with gpg --version that you are using gpg2.

    If not, check with where gpg (or which gpg if the where command is not available) that the top-most path is the git one.

    1. Check in the output of gpg --version if the home directory is something like (on Linux) /home/username/.gnupg or (on windows) /c/Users/username/.gnupg. This directory doesn't have to exist, yet.

    If the path is incorrect, try to change it - for example one time on Windows I saw my home was prefixed by the path in which I ran the command, so I put an alias in the .bash_profile with alias gpg="gpg --homedir=/c/Users/username/.gnupg" and restarted bash, then checked again.

    1. The next few steps are from the good Github's guide, preferably you check there if commands are still correct. First, generate a new GPG key pair (but do not install the gnupg tools!): Run gpg --full-generate-key
    1. Every time your want to check what keys you have, use gpg --list-secret-keys --keyid-format LONG. Do it now, and copy the key_id (as I will name your key from now on) from the output sec rsa4096/key_id 2018-10-27 [SC] ....
    2. Print the public key with gpg --armor --export key_id.
    3. Add the GPG key to your GitHub account: go to Settings | SSH and GPG keys and add the public key block (including the header and footer).
    4. Tell Git about your GPG key in Settings > Version Control > Git > Configure GPG Key as shown in Mahozad's answer, please give it an upvote or tell git manually: git config --global user.signingkey key_id.
    5. If you had to use the --homedir option before, you need to make sure that when git starts gpg, the home is also properly changed. Create a file C:\Users\username\start-gpg.sh and put into it gpg --homedir=/c/Users/username/.gnupg "$@". Then run git config --global gpg.program C:\\Users\\username\\start-gpg.sh to tell git to use it.
    6. Restart bash.
    7. Sign a commit with GPG with git commit -S -m "signed commit" and confirm that it is Verified on Github, you should see a little badge when you view your commit.
    8. Set commits to be signed by default with git config --global commit.gpgsign true. Make a new commit in IntelliJ and verify if it was signed with git verify-commit HEAD.

    Steps to make the commit signing happen automatically: three options

    1. I have no passphrase on my key

    You're done.

    2. I want to try gpg-agent

    For me this option didn't work: I still had to provide my passphrase often, though not always. But in theory this works:

    1. Update the cache time, in C:\Users\username\.gnupg\gpg-agent.conf: create file if it doesn't exist, add default-cache-ttl 34560000 and max-cache-ttl 34560000.
    2. Restart gpg-agent using gpgconf --kill gpg-agent
    3. Now you should have to enter your passphrase once when committing and then not anymore. Check that your commit is Verified on GitHub.

    3. Pipe the passphrase in plaintext to gpg

    This means that you never have to provide your passphrase, but for me this means that GitHub puts an Unverified badge on my commit. Question here: The key whose key-id is in the signature did not sign this commit

    1. If you created it already, replace all of the contents of C:\Users\username\start-gpg.sh with the code below. If you didn't, create the file with the contents below, remove the --homedir flag and run git config --global gpg.program C:\\Users\\username\\start-gpg.sh. In any case, restart bash.

    Yes, you're going to place your password in plaintext on your computer! If you know a better way, please leave a comment...

    # Passphrase-file-descriptor is set to 0 (STDIN), to use it --batch must be used
    # The --pinentry-mode loopback is required since gpg 2.1.0 for --passphrase-fd
    # The "$@" makes sure that whatever git wanted gpg to do, is still done
    echo mypassphrase | gpg --homedir=/c/Users/username/.gnupg --passphrase-fd 0 --batch --yes --pinentry-mode loopback "$@"
    
    1. Commit and push and check that you were not asked for a passphrase, and that your commit is still Verified on GitHub.

    Old answer for older git versions without gpg2

    For the reference, here are the full instructions, or rather the steps I did to make it somewhat work. With 'work' I mean that commits are signed automatically, but there are two disadvantages:

    If you get stuck, check the steps in the question to see if I had the same problem.

    1. First few steps are from the good Github's guide: Generate a new GPG key pair

    2. Add the GPG key to your GitHub account

    3. Associate a verified (by GitHub) email with your GPG key and make sure the name and email in your .gitconfig are the same.

    4. Tell Git about your GPG key

    5. Sign a commit with GPG and confirm that it is Verified on Github, you should see a little badge when you view your commit.

    6. Set commits to be signed by default with git config --global commit.gpgsign true. Make a new commit and verify if it was signed with git verify-commit HEAD.

    7. The gpg version that comes with git is too old, so install Gpg4win (binary releases, at the bottom) which should install gpg 2. With where gpg you should see two path, of which probably the second is your new gpg, something like C:\Program Files (x86)\GnuPG\bin\gpg.exe. If not, try to install Gnupg 2 separately from the downloads page.

    8. I put alias gpg="'C:\Program Files (x86)\GnuPG\bin\gpg.exe'" to point the gpg command to my new gpg in my C:\Users\username\.bash_profile, restart Git Bash and check with gpg --version that I'm now using gpg 2.

    9. Add a new environment variable called GNUPGHOME which points to C:\Users\username\.gnupg. Reboot and check with printenv GNUPGHOME that you added it correctly.

    10. Make a script C:\Users\username\gpg-no-tty.sh and put into it echo passphrase | "C:\Program Files (x86)\GnuPG\bin\gpg.exe" --passphrase-fd 0 --batch --no-tty --yes "$@"

    The reason you are putting your passphrase in plaintext here is because the --batch option, which makes it all work, needs the passphrase fed. To me it seems like there should exist a better solution than saving your passphrase in plaintext on your computer, so please leave a comment if you found something better.

    1. Point git to this script with git config --global gpg.program C:\\Users\\username\\gpg-no-tty.sh.

    2. Now test both in Git Bash and IntelliJ that you can commit, and verify that it worked by doing git verify-commit HEAD.