Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save StevenACoffman/bf6f934755f4ac4f4b1d1ddd189f788a to your computer and use it in GitHub Desktop.
Save StevenACoffman/bf6f934755f4ac4f4b1d1ddd189f788a to your computer and use it in GitHub Desktop.
SSH Generation and commands for GIT and GPG

GPG and GIT and SSH ... OH MY!

ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 -C gears@umich.edu -N ''
  • Then you won't be prompted for a passphrase. However, if you did have a passphrase:
ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 -C gears@umich.edu -N 'Really do not use this passphrase'
  • Then you will be prompted to type your passphrase every time you interact with git remote (pull, push, fetch, etc.) This is intolerable.

  • You can avoid typing your passphrase millions of times a day if you use the ssh-agent. ssh-agent is a helper program that keeps track of user's identity keys and their passphrases. The agent can then use the keys to log into other servers without having the user type in a password or passphrase again.

  • But you will need to add it to your keychain:

eval "$(ssh-agent -s)"
ssh-add -K ~/.ssh/id_ed25519

NOTE: I'm not totally clear on why adding a passphrase you never type is more secure then not having a passphrase if you're only using it for github. I guess so the private key cannot be stolen off of your encrypted hard disk and used on another machine? Seems unlikely, but whatevs.

SSH Config

.ssh/config:

# later rules override earlier ones if they are more specific
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519
#  IdentityFile ~/.ssh/id_rsa # Keep any old insecure key files if you want?
Host github.com
  User StevenACoffman
  HostName github.com
  IdentityFile ~/.ssh/id_ed25519
#  IdentityFile ~/.ssh/id_rsa
  StrictHostKeyChecking no
  UseKeychain yes
  AddKeysToAgent yes
  UserKnownHostsFile=/dev/null
# NOTE:The IdentitiesOnly yes is required to prevent the SSH default behavior
# of sending the identity file matching the default filename for each protocol. If you have a file named
# ~/.ssh/id_rsa that will get tried BEFORE your ~/.ssh/id_rsa.github without this option.

Signoff vs. gpg-sign

The difference between signoff and gpg-sign are significant but subtle. Signoff doesn’t require the GPG key and is just the line: "Signed-off by" which will make the Developer Certificate of Origin bots stop bothering you.

Signing Git commits is important because in this age of malicious code and back doors, it helps protect you from an attacker who might otherwise inject malicious code into your codebase. It also helps discourage untrustworthy developers from adding their own back doors to the code, because once it's discovered, the bad code will be traced to them.

Generate a new pgp key: (better to use gpg2 instead of gpg in all below commands)

First thing to do is to install gpg + gpg2 and gpg-agent + pinentry-mac. The first two are for the main GPG actions (we need both for compatibility reasons) and the last two will be used for storing the GPG key passphrase in OS X’s keychain.

brew install gpg2 pinentry-mac
mkdir -p ~/.gnupg/
echo "pinentry-program $(brew --prefix pinentry-mac)/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
alias pinentry='pinentry-mac'

Test it with echo GETPIN | pinentry and verify that it asks you for the PIN (really passphrase).

You may also want to install pinentry-touchid:

brew tap jorgelbg/tap
brew install pinentry-touchid

and then modify the ~/.gnupg/gpg-agent.conf to point to `"$(brew --prefix pinentry-mac)/bin/pinentry-touchid"

Second, we have to generate a new GPG key if you don’t already have one. Make sure you have gpg2 version >= 2.1.21. You can start the wizard to generate a new key with the following command:

gpg2 --full-gen-key --expert

The wizard is going to ask you a few questions, here’s what I’d recommend:

  • Select (10) ECC (sign only)
  • Select (1) Curve 25519
  • Answer remaining options with any response, but use a strong passphrase
  • After the key is created record its fingerprint
  • Copy the newly created key from GPG keychain: gpg --armor --export
  • Go to github account > settings > GPG keys > paste the Ed25519 PGP public key from the previous step
  • click "add key"

Now to configuring gpg-agent. This will prevents us from having to enter our key every time we want to encrypt, decrypt or sign something.

Note: Prior to GPG 2.1 where autostart became the norm (see https://www.gnupg.org/faq/whats-new-in-2.1.html#autostart) you would need to add to the shell.

In order to do so, add to following to your ~/.bash_profile file (or any other location that will be executed during shell startup)

[ -f ~/.gnupg/gpg-agent.env ] && source ~/.gnupg/gpg-agent.env
if [ -S "${GPG_AGENT_INFO%%:*}" ]; then
  export GPG_AGENT_INFO
else
  eval $(gpg-agent --daemon --log-file /tmp/gpg.log --write-env-file ~/.gnupg/gpg-agent.env --pinentry-program $(brew --prefix pinentry-mac))
fi

This will ensure that the gpg-agent is running when you open a shell, and will also configure your current shell to find it.

Next, open the file ~/.gnupg/gpg.conf and uncomment the line where it says use-agent.

Now, everything on the GPG side is setup and we can test it with the following command. It will encrypt "hello world" for <recipient> and then immediately decrypt it again.

$ echo hello world | gpg2 -e -r <identifier> | gpg2 -d

Replace <identifier> with yourself - it will fuzzy match it against the name and email you have specified earlier when we’ve created the key. This will also open the pinentry program again: Enter your key, mark the checkbox Save in Keychain and confirm with OK

References:

maybe you need some random work in your OS to generate a key. so run this command:
find ./* $HOME -type d | xargs grep some_random_string > /dev/null
check current keys:
gpg2 --list-secret-keys --keyid-format LONG
See your gpg public key:
gpg2 --armor --export YOUR_KEY_ID

YOUR_KEY_ID is the hash in front of sec in previous command. (for example sec 4096R/234FAA343232333 => key id is: 234FAA343232333)

Set a gpg key for git:
git config --global user.signingkey your_key_id
Auto-sign all commits globaly (not signoff)
git config --global commit.gpgsign true
git config --global gpg.program gpg2
Manual, explicit sign (and sign-off) on a single commit:
git commit -S -s -a -m "Test a signed commit"
  • -s, --signoff : Add Signed-off-by line by the committer at the end of the commit log message. The meaning of a signoff depends on the project, but it typically certifies that committer has the rights to submit this work under the same license and agrees to a Developer Certificate of Origin (see http://developercertificate.org/ for more information).
  • -S[<keyid>], --gpg-sign[=<keyid>]: GPG-sign commits. The keyid argument is optional and defaults to the committer identity; if specified, it must be stuck to the option without a space.
  • -a, --all: Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.
  • -m <msg>, --message=<msg>: Use the given as the commit message. If multiple -m options are given, their values are concatenated as separate paragraphs. The -m option is mutually exclusive with -c, -C, and -F.
Auto-SignOff (not sign)

If you don't specify a message explicitly git -m "Something" then the editor will get preloaded with a git template:

printf "\nSigned-off-by: Steven A Coffman <gears@umich.edu>\n" > ~/.gitmessage.txt
git config --global commit.template ~/.gitmessage.txt

This might still be useful for resolving merge conflicts, but git -s -m "Something" seems pretty easy to alias.

Generating a secure SSH Key and commands

ssh-keygen options:

  • -t: Specifies the type of key to create, in our case the Ed25519.
    • -t ed25519 : - for greatest security (bits are a fixed size and -b flag will be ignored)
    • -t rsa : - for greatest portability (key needs to be greater than 4096 bits)
    • -t ecdsa : - faster than RSA or DSA (bits can only be 256, 284, or 521)
    • -t dsa : - DEEMED INSECURE - DSA limted to 1024 bit key as specified by FIPS 186-2, No longer allowed by default in OpenSSH 7.0+
    • -t rsa1 : - DEEMED INSECURE - has weaknesses and shouldn't be used (used in protocol 1)
  • -b 4096: bit size, -b flag ignored for ed25519
  • -C "First.Last@somewhere.com": comment, purely informational and can be anything
  • -o: Save the private-key using the new OpenSSH format rather than the PEM format. New format increases resistance to brute-force password cracking but is not supported by OpenSSH prior to 6.5. Actually, this option is implied when you specify the key type as ed25519.
  • -a 100: It’s the numbers of KDF (Key Derivation Function) rounds. Should be no smaller than 64. Higher numbers result in slower passphrase verification, increasing the resistance to brute-force password cracking should the private-key be stolen.
  • -f: Specify the filename of the generated key file. If you want it to be discovered automatically by the SSH agent, it must be stored in the default .ssh directory within your home directory.
  • -N: Provides the new passphrase. Without this option set to empty string, you will be prompted even if it you enter an empty string.

Example usage (in order of preference - security):

  1. ssh-keygen -o -a 500 -t ed25519 -f ~/.ssh/id_ed25519 -C "First.Last@somewhere.com"
  2. ssh-keygen -o -a 500 -C "First.Last@somewhere.com"
  3. ssh-keygen -t ecdsa -a 500 -b 521 -C "First.Last@somewhere.com"
  4. ssh-keygen -t rsa -a 500 -b 4096 -C "First.Last@somewhere.com"

Example usage (in order of preference - usability):

  1. ssh-keygen -t rsa -a 500 -b 4096 -C "First.Last@somewhere.com"
  2. ssh-keygen -t ecdsa -a 500 -b 521 -C "First.Last@somewhere.com"
  3. ssh-keygen -o -a 500 -C "First.Last@somewhere.com"

To verify:

ssh-keygen -l -f ssh/id_ed25519
  Output: 
    256   SHA256:2..............w First.Last@somewhere.com (ED25519)
    ^^^   ^^^^^^^^^^^^^^^^^       ^^^^^^^^^^                  ^^^
     |__ Size    |__ Fingerprint      |__ Comment             |__ Type
To copy public key:
  • Using ssh-copy-id:
    ssh-copy-id username@remote_host
  • Manually, one-line:
    cat ~/.ssh/id_rsa.pub | ssh username@remote_host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
  • Manually, copying public string into auth keys:
    echo public_key_string >> ~/.ssh/authorized_keys

Key Type Reference

OS OpenSSH Type
Ubuntu 12.04 5.9 dsa, rsa,ecdsa
Ubuntu 14.04 6.6 dsa, rsa,ecdsa,ed25519
Ubuntu 16.04 7.2 dsa*,rsa,ecdsa,ed25519
Ubuntu 18.04 7.6 dsa*, rsa**, ecdsa, ed25519
Fedora 23 7.1 dsa*,rsa,ecdsa,ed25519
CentOS 7 6.4 dsa, rsa,ecdsa
Mac OS X 10.11 (El Capitan) 6.9 dsa, rsa,ecdsa,ed25519
macOS 10.12 (Sierra DP) 7.2 dsa*,rsa,ecdsa,ed25519
Cmder 7.1 dsa*,rsa,ecdsa,ed25519
Window 10 (14342) 6.6.1 dsa, rsa,ecdsa,ed25519
PuTTY N/A dsa, rsa,ecdsa1,ed255191

To skip the prompt to ask for passphrase, add: -N ""

Physical Setup:
  • localhost can SSH to Host1 & Host2
  • Host1 & Host2 cannot SSH to each other.

Copying files from one server to another with keys on local server: scp -3 user1@host1:/path/to/file user2@host2:/path/to/destination

Copying files and subfolders recursively from one server to another with keys on local server: scp -3rp user1@host1:/path/to/file user2@host2:/path/to/destination

Generating public key from private key ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub

Resources:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment