Getting yourself set up in macOS to sign keys using a Nitrokey HSM with gpg is non-trivial. Allegedly (at least some) Nitrokeys are supported by scdaemon
(GnuPG’s stand-in abstraction for cryptographic tokens) but it seems that the version of scdaemon
in brew
doesn’t have support.
However there is gnupg-pkcs11-scd
which is a replacement for scdaemon
which uses PKCS #11. Unfortunately it’s a bit of a hassle to set up.
There’s a bunch of things you’ll want to install from brew
: opensc, gnupg, gnupg-pkcs11-scd, pinentry-mac, openssl and engine_pkcs11.
brew install opensc gnupg gnupg-pkcs11-scd pinentry-mac \ openssl engine-pkcs11
gnupg-pkcs11-scd
won’t create keys, so if you’ve not made one already, you need to generate yourself a keypair. Which you can do with pkcs11-tool
:
pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so -l \ --keypairgen --key-type rsa:2048 \ --id 10 --label 'Danielle Madeley'
The --id
can be any hexadecimal id you want. It’s up to you to avoid collisions.
Then you’ll need to generate and sign a self-signed X.509 certificate for this keypair (you’ll need both the PEM form and the DER form):
/usr/local/opt/openssl/bin/openssl << EOF engine -t dynamic \ -pre SO_PATH:/usr/local/lib/engines/engine_pkcs11.so \ -pre ID:pkcs11 \ -pre LIST_ADD:1 \ -pre LOAD \ -pre MODULE_PATH:/usr/local/lib/opensc-pkcs11.so req -engine pkcs11 -new -key 0:10 -keyform engine \ -out cert.pem -text -x509 -days 3640 -subj '/CN=Danielle Madeley/' x509 -in cert.pem -out cert.der -outform der EOF
The flag -key 0:10
identifies the token and key id (see above when you created the key) you’re using. If you want to refer to a different token or key id, you can change these.
And import it back into your HSM:
pkcs11-tool --module /usr/local/lib/opensc-pkcs11.so -l \ --write-object cert.der --type cert \ --id 10 --label 'Danielle Madeley'
You can then configure gnupg-agent
to use gnupg-pkcs11-scd
. Edit the file ~/.gnupg/gpg-agent.conf
:
scdaemon-program /usr/local/bin/gnupg-pkcs11-scd pinentry-program /usr/local/bin/pinentry-mac
And the file ~./gnupg/gnupg-pkcs11-scd.conf
:
providers nitrokey provider-nitrokey-library /usr/local/lib/opensc-pkcs11.so
gnupg-pkcs11-scd
is pretty nifty in that it will throw up a (pin entry) dialog if your token is not available, and is capable of supporting multiple tokens and providers.
Reload gpg-agent:
gpg-agent --server gpg-connect-agent << EOF RELOADAGENT EOF
Check your new agent is working:
gpg --card-status
Get your key handle (grip), which is the 40-character hex string after the phrase KEY-FRIEDNLY
(sic):
gpg-agent --server gpg-connect-agent << EOF SCD LEARN EOF
Import this key into gpg
as an ‘Existing key’, giving the key grip above:
gpg --expert --full-generate-key
You can now use this key as normal, create sub-keys, etc:
gpg -K /Users/danni/.gnupg/pubring.kbx ------------------------------- sec> rsa2048 2017-07-07 [SCE] 1172FC7B4B5755750C65F9A544B80C280F80807C Card serial no. = 4B43 53233131 uid [ultimate] Danielle Madeley <danielle@madeley.id.au> echo -n "Hello World" | gpg --armor --clearsign --textmode
Side note: the curses-based pinentry
doesn’t deal with piping content into stdin, which is why you want pinentry-mac
.
You can also import your certificate into gpgsm
:
gpgsm --import < ca-certificate gpgsm --learn-card
And that’s it, now you can sign your git tags with your super-secret private key, or whatever it is you do. Remember that you can’t exfiltrate the secret keys from your HSM in the clear, so if you need a backup you can create a DKEK backup (see the SmartcardHSM docs), or make sure you’ve generated that revocation certificate, or just decided disaster recovery is for dweebs.