GnuPG

From YobiWiki
Jump to navigation Jump to search

Those are personal notes when I decided in 2016 to generate a new key as transition from my previous one (from 2002!).
Daily subkeys are stored on a Yubikey NEO-n and master key is stored offline.

Resources

The steps I followed and which I describe only very briefly here, more to remind how I combined them, came from those excellent resources:

gpg.conf

First step was to refresh a little bit my gpg.conf.
See https://github.com/ioerror/duraconf/raw/master/configs/gnupg/gpg.conf for commented gpg.conf and https://help.riseup.net/en/security/message-security/openpgp/best-practices for the reasons behind.

no-emit-version
no-comments
keyid-format 0xlong
with-fingerprint
list-options show-uid-validity
verify-options show-uid-validity
use-agent
keyserver hkps://hkps.pool.sks-keyservers.net
keyserver-options ca-cert-file=/home/phil/.gnupg/keyservers/sks-keyservers.netCA.pem
keyserver-options no-try-dns-srv
keyserver-options no-honor-keyserver-url
keyserver-options include-revoked
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed

Some more of my own:

no-greeting
keyserver-options auto-key-retrieve honor-http-proxy
list-options show-policy-urls show-notations show-keyserver-urls show-uid-validity show-unusable-uids show-unusable-subkeys show-sig-expire
verify-options show-photos show-policy-urls show-notations show-keyserver-urls show-uid-validity show-unusable-uids
utf8-strings
ask-cert-level

Offline storage

Digressing a little bit...

I chose an old SDCard to store the master key offline, but it required a little bit of maintenance because it wasn't mounting automatically:

Making sude partition table was ok:

$ sudo fdisk /dev/mmcblk0

Checking FS signatures:

$ sudo wipefs /dev/mmcblk0p1

There was still a mix of FAT and ext2 signatures, so deleting the ext2 signature based on the returned offset:

$ sudo wipefs -o 0x438 /dev/mmcblk0p1

Formatting

$ sudo mkfs.vfat /dev/mmcblk0p1
$ sudo fatlabel /dev/mmcblk0p1 GNUPG

Reinserting it to get it mounted automatically, then

$ cp ~/.gnupg/gpg.conf /media/phil/GNUPG
$ sudo mount --bind /media/phil/GNUPG ~/.gnupg 

Entropy

Creating large keys require large entropy.
I like haveged for that:

$ sudo apt-get install haveged

Creating main key

Idea following https://www.esev.com/blog/post/2015-01-pgp-ssh-key-on-yubikey-neo/ is to keep the main key completely offline so if yubikey is lost, there is still some hope.

$ gpg --expert --gen-key
Please select what kind of key you want:
  (8) RSA (set your own capabilities)
Your selection? 8
Current allowed actions: Sign Certify Encrypt 
Your selection? s
Your selection? e
Current allowed actions: Certify 
Your selection? q
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Key is valid for? (0) 2y
Is this correct? (y/N) y
Real name: Philippe Teuwen
Email address: phil@teuwen.org
Comment: 
You selected this USER-ID:
   "Philippe Teuwen <phil@teuwen.org>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
gpg: key 0x9B554C36544C89BC marked as ultimately trusted
public and secret key created and signed.

Creating revokation certificate

$ gpg --gen-revoke 9B554C36544C89BC > /media/phil/GNUPG/rev-phil_teuwen.org_2016
sec  4096R/0x9B554C36544C89BC 2016-02-04 Philippe Teuwen <phil@teuwen.org>
Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  3 = Key is no longer used
Your decision? 3
Enter an optional description; end it with an empty line:
>Using revocation certificate that was generated when key was created.
>It is very likely that I have lost access to the private key.
> 
Reason for revocation: Key is no longer used
Using revocation certificate that was generated when key was created. It is very likely that I have lost access to the private key.
Is this okay? (y/N) y

Then printing it on paper

Creating Encryption subkey

Idea following https://www.esev.com/blog/post/2015-01-pgp-ssh-key-on-yubikey-neo/ is to create the encryption key out of yubikey and importing it so it can be imported on several yubikey's.

$ gpg --edit-key 9B554C36544C89BC
gpg> addkey
Please select what kind of key you want:
  (6) RSA (encrypt only)
Your selection? 6
What keysize do you want? (2048) 2048
Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y
pub  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03  usage: C   
                               trust: ultimate      validity: ultimate
sub  2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: 2018-02-03  usage: E   
[ultimate] (1). Philippe Teuwen <phil@teuwen.org>
gpg> save

Backup the secret key

The "keytocard / save" operations that we will do later will also remove the secret key from your keyring, so better to backup it first!

$ gpg --export-secret-key 9B554C36544C89BC > /media/phil/GNUPG/9B554C36544C89BC-2016-02-04-47B68B62B62C8F88-secret.pgp
$ gpg --delete-secret-key 9B554C36544C89BC
$ gpg --import < /media/phil/GNUPG/9B554C36544C89BC-2016-02-04-47B68B62B62C8F88-secret.pgp

Yubikey

$ sudo apt-get install yubikey-personalization ykneomgr
$ wget https://raw.githubusercontent.com/Yubico/yubikey-personalization/master/69-yubikey.rules 
$ wget https://raw.githubusercontent.com/Yubico/yubikey-personalization/master/70-yubikey.rules 
$ sudo mv *rules /etc/udev/rules.d/
$ sudo chown root.root /etc/udev/rules.d/*yubikey.rules

Insert yubikey NEO-n

$ ykinfo -a

On a Yubikey where HID has been deactivated, ykinfo will fail, use ykneomgr instead.
If ykneomgr fails, it may be because you used it for gpg via scdaemon.
Even with "card-timeout 1" in .gnupg/scdaemon.conf I experienced problems once I read some encrypted emails, I have to restart pcscd to free the Yubikey and be able to use ykneomgr.

To keep possibility to use all modes simultaneously:

$ ykpersonalize -m86
Firmware version 3.3.0 Touch level 1285 Program sequence 1
The USB mode will be set to: 0x86
Commit? (y/n) [n]: y

If ykpersonalize doesn't work (no HID), see ykneomgr

I wanted to disable HID (as I don't use it but still touch accidentally the key now and then).
But switching to mode 5 (U2F+CCID) didn't disable HID so to get it working I had to switch to mode 1 (CCID) then 5 (U2F+CCID):

ykneomgr -M1
ykneomgr -M5

Creating Signature and Authentication subkeys

Yes default admin PIN is 12345678 and default user PIN is 123456.
We'll change them later.

$ gpg --edit-key 9B554C36544C89BC
gpg> addcardkey
Please select the type of key to generate:
   (1) Signature key
Your selection? 1
Enter Admin PIN: 12345678
Enter PIN: 123456
Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y
pub  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03  usage: C   
                               trust: ultimate      validity: ultimate
sub  2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: 2018-02-03  usage: E   
sub  2048R/0xAEBAADBEE208E2DD  created: 2016-02-04  expires: 2018-02-03  usage: S   
[ultimate] (1). Philippe Teuwen <phil@teuwen.org>
gpg> addcardkey
Please select the type of key to generate:
   (3) Authentication key
Your selection? 3
Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y
pub  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03  usage: C   
                               trust: ultimate      validity: ultimate
sub  2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: 2018-02-03  usage: E   
sub  2048R/0xAEBAADBEE208E2DD  created: 2016-02-04  expires: 2018-02-03  usage: S   
sub  2048R/0xE5151B7FDCA95A14  created: 2016-02-04  expires: 2018-02-03  usage: A   
[ultimate] (1). Philippe Teuwen <phil@teuwen.org>

Ready to import Encryption subkey?

At this point we should import the encryption key to the yubikey but I got some error:

gpg: error writing key to card: not supported

Some say to use keyParser.py but I found out gpg2 works. Gpg could work directly with the card (except for the keytocard obviously) but Gpg2 needs sdaemon for that:

$ sudo apt-get install gnupg2 scdaemon

Yet another quirk: gnupg gave some warnings about locking from FAT but gnupg2 just refuses using FAT, so I add to the gpg.conf of the SD-Card:

# allow linux to write to FAT disks
lock-never

While this was working fine earlier, at some point I experiended:

$ gpg2 --card-status
gpg: OpenPGP card not available: Not supported

The fix was to forbid scdaemon to use the CCID driver but to use PCSC instead. Edit .gnupg/scdaemon.conf:

card-timeout 1
disable-ccid

The "card-timeout" is supposed to free the card when not in use, still I've typically to restart pcscd if scdaemon keeps locking it.

Encryption key to Yubikey

$ gpg2 --edit-key 9B554C36544C89BC
gpg> toggle
gpg> key 1
sec  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03
ssb* 2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: never     
ssb  2048R/0xAEBAADBEE208E2DD  created: 2016-02-04  expires: never     
                     card-no: 0006 03037217
ssb  2048R/0xE5151B7FDCA95A14  created: 2016-02-04  expires: never     
                     card-no: 0006 03037217
(1)  Philippe Teuwen <phil@teuwen.org>
gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2
gpg> save

Adding UID and photo

Choose a 240x288 picture strongly compressed (I chose jpeg quality 20) to obtain a size < 5kb.

$ gpg2 --edit-key 9B554C36544C89BC
gpg> adduid
Real name: Philippe Teuwen
Email address: pteuwen@quarkslab.com
Comment: 
You selected this USER-ID:
    "Philippe Teuwen <pteuwen@quarkslab.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

gpg> addphoto
Enter JPEG filename for photo ID: ~/phil20.jpg
pub  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03  usage: C   
                               trust: ultimate      validity: ultimate
sub  2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: 2018-02-03  usage: E   
sub  2048R/0xAEBAADBEE208E2DD  created: 2016-02-04  expires: 2018-02-03  usage: S   
sub  2048R/0xE5151B7FDCA95A14  created: 2016-02-04  expires: 2018-02-03  usage: A   
[ultimate] (1)  Philippe Teuwen <phil@teuwen.org>
[ unknown] (2). Philippe Teuwen <pteuwen@quarkslab.com>
[ unknown] (3)  [jpeg image of size 4266]

gpg> uid 1
gpg> primary
gpg> save

Almost there

Exporting key

$ gpg --armor --export 9B554C36544C89BC > /media/phil/GNUPG/9B554C36544C89BC.asc

And pushing it to http://www.yobi.be/files/9B554C36544C89BC.asc

Back to the daily .gnupg

$ sudo umount ~/.gnupg

Changing default PINs

$ gpg --card-edit
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
gpg: OpenPGP card no. D2760001240102000006030372170000 detected
1 - change PIN
Your selection? 1
Please enter the PIN
   123456
New PIN
   ...              
New PIN
   ...
PIN changed.     

3 - change Admin PIN
Your selection? 3
gpg: 3 Admin PIN attempts remaining before card is permanently locked
Please enter the Admin PIN
   12345678                
New Admin PIN
   ...                    
New Admin PIN
   ...
PIN changed.     

Q - quit
Your selection? q

gpg/card> forcesig
gpg/card> url
URL to retrieve public key: http://www.yobi.be/files/9B554C36544C89BC.asc

gpg/card> fetch
gpg: requesting key 0xAEBAADBEE208E2DD from http server www.yobi.be

gpg/card> quit
$ gpg --card-status
Application ID ...: D2760001240102000006030372170000
Version ..........: 2.0
Manufacturer .....: unknown
Serial number ....: xxxxxxxxx
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : http://www.yobi.be/files/9B554C36544C89BC.asc
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: 2048R 2048R 2048R
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 2
Signature key ....: 67E1 AAA2 46D7 9037 7671  BAD5 AEBA ADBE E208 E2DD
      created ....: 2016-02-04 10:56:03
Encryption key....: 55A5 1FF1 F45A A846 EC12  A5D4 47B6 8B62 B62C 8F88
      created ....: 2016-02-04 10:26:52
Authentication key: 93FE E3BA 0F4D 4461 19E8  1CBE E515 1B7F DCA9 5A14
      created ....: 2016-02-04 10:57:35
General key info..: 
pub  2048R/0xAEBAADBEE208E2DD 2016-02-04 Philippe Teuwen <phil@teuwen.org>
sec#  4096R/0x9B554C36544C89BC  created: 2016-02-04  expires: 2018-02-03
ssb>  2048R/0x47B68B62B62C8F88  created: 2016-02-04  expires: 2018-02-03
                      card-no: 0006 03037217
ssb>  2048R/0xAEBAADBEE208E2DD  created: 2016-02-04  expires: 2018-02-03
                      card-no: 0006 03037217
ssb>  2048R/0xE5151B7FDCA95A14  created: 2016-02-04  expires: 2018-02-03
                      card-no: 0006 03037217

Changing default key in gpg.conf

default-key  0xF14883379E8DD09F03280E1B9B554C36544C89BC

Last check

$ sudo apt-get install hopenpgp-tools
$ hkt export-pubkeys 'AEBAADBEE208E2DD' | hokey lint

All green \o/

Using Yubikey on another machine

$ gpg --card-edit
gpg/card> fetch

Signing new key with the old one

$ gpg --default-key 9ad7e3db --sign-key 9B554C36544C89BC
Really sign all user IDs? (y/N) y
   (3) I have done very careful checking.
Your selection? (enter `?' for more information): 3
Really sign? (y/N) y
$ gpg --send-key 9B554C36544C89BC

As it's a pool I sent it 10x, rather than waiting them to all sync...

And also for those still using pgp.mit.edu:

$ gpg --keyserver pgp.mit.edu --send-key 9B554C36544C89BC

Transition

Now let's ask some helpful souls who signed my old key.

Stealing and adapting key transition text from https://we.riseup.net/assets/176898/key%20transition

Mass mailing to those who've signed the old one:

$ gpg --list-sigs 9ad7e3db|grep ^sig|sed 's/.*<//;s/>.*//;/^sig/d'|sort|uniq|tr '\n' ','
Date: 2016-02-04

For a number of reasons[0], I've recently set up a new OpenPGP key,
and will be transitioning away from my old one.

The old key will continue to be valid for some time, but I prefer all
future correspondence to come to the new one. I would also like this
new key to be re-integrated into the web of trust. This message is
signed by both keys to certify the transition.

The old key was:

pub 1024D/0x7A135F579AD7E3DB 2002-05-05 [expires: 2017-08-13]
sub 2048g/0x78FC60279A4A59B9 2002-05-05 [expires: 2017-08-13]
sub 4096R/0xF2FD1762608F63B7 2007-08-16 [expires: 2017-08-13]
fingerprint = 440A 3A9E 56E9 D90E 99D7 63A8 7A13 5F57 9AD7 E3DB

And the new key is:
pub 4096R/0x9B554C36544C89BC 2016-02-04 [expires: 2018-02-03]
sub 2048R/0x47B68B62B62C8F88 2016-02-04 [expires: 2018-02-03]
sub 2048R/0xAEBAADBEE208E2DD 2016-02-04 [expires: 2018-02-03]
sub 2048R/0xE5151B7FDCA95A14 2016-02-04 [expires: 2018-02-03]
fingerprint = F148 8337 9E8D D09F 0328 0E1B 9B55 4C36 544C 89BC

To fetch the full key from a public key server, you can simply do:

gpg --keyserver pool.sks-keyservers.net --recv-key F14883379E8DD09F03280E1B9B554C36544C89BC

If you already know my old key, you can now verify that the new key is
signed by the old one:

gpg --check-sigs F14883379E8DD09F03280E1B9B554C36544C89BC

If you don't already know my old key, or you just want to be double
extra paranoid, you can check the fingerprint against the one above:

gpg --fingerprint F14883379E8DD09F03280E1B9B554C36544C89BC

If you are satisfied that you've got the right key, and the UIDs match
what you expect, I'd appreciate it if you would sign my key. You can
do that by issuing the following command:

**
NOTE: if you have previously signed my key but did a local-only
signature (lsign), you will not want to issue the following, instead
you will want to use --lsign-key, and not send the signatures to the
keyserver
**

gpg --sign-key F14883379E8DD09F03280E1B9B554C36544C89BC

I'd like to receive your signatures on my key. You can either send me
an e-mail with the new signatures (if you have a functional MTA on
your system):

gpg --export F14883379E8DD09F03280E1B9B554C36544C89BC | \
gpg --encrypt -r F14883379E8DD09F03280E1B9B554C36544C89BC --armor | \
mail -s 'OpenPGP Signatures' phil@teuwen.org

Additionally, I highly recommend that you implement a mechanism to
keep your key material up-to-date so that you obtain the latest
revocations, and other updates in a timely manner. You can do regular
key updates by using parcimonie[1] to refresh your keyring. Parcimonie
is a daemon that slowly refreshes your keyring from a keyserver over
Tor. It uses a randomized sleep, and fresh tor circuits for each key.
The purpose is to make it hard for an attacker to correlate the key
updates with your keyring.

I also highly recommend checking out the excellent Riseup GPG best
practices doc, from which I stole most of the text for this transition
message ;-)

https://we.riseup.net/debian/openpgp-best-practices

Please let me know if you have any questions, or problems, and sorry
for the inconvenience.

If you're curious, I've briefly documented all the steps using a Yubikey on my wiki [2].

Philippe Teuwen

0. https://www.debian-administration.org/users/dkg/weblog/48
1. https://gaffer.ptitcanardnoir.org/intrigeri/code/parcimonie/
2. http://wiki.yobi.be/wiki/GnuPG

In an email signed with my old key...

Signing keys

To sign keys it requires the SD-Card.

To enable it:

cp -a ~/.gnupg/pubring.gpg /media/phil/GNUPG/
cp -a ~/.gnupg/trustdb.gpg /media/phil/GNUPG/
sudo mount --bind /media/phil/GNUPG ~/.gnupg

To disable it:

sudo umount ~/.gnupg
cp -a /media/phil/GNUPG/pubring.gpg ~/.gnupg/
cp -a /media/phil/GNUPG/trustdb.gpg ~/.gnupg/

Extending expiration time

sudo service pcscd stop
cp -a ~/.gnupg/pubring.gpg /media/phil/GNUPG/
cp -a ~/.gnupg/trustdb.gpg /media/phil/GNUPG/
sudo mount --bind /media/phil/GNUPG ~/.gnupg
gpg --edit-key 9B554C36544C89BC
expire
  3y
key 47B68B62B62C8F88
key C68777A356ACFA91
key C83BE1071CDE1015
expire
  3y
save
sudo umount ~/.gnupg
cp -a /media/phil/GNUPG/pubring.gpg ~/.gnupg/
cp -a /media/phil/GNUPG/trustdb.gpg ~/.gnupg/
gpg --armor --export 9B554C36544C89BC > /media/phil/GNUPG/9B554C36544C89BC.asc
umount /media/phil/GNUPG
sudo service pcscd start
gpg --keyserver keys.gnupg.net --send-key 9B554C36544C89BC
gpg --armor --export 9B554C36544C89BC > /tmp/9B554C36544C89BC.asc

And pushing it to http://www.yobi.be/files/9B554C36544C89BC.asc

OpenSSH

Using the OpenPGP key and the Yubikey for OpenSSH

$ sudo apt-get install monkeysphere

We need to specifying the authentication subkey here!

$ gpgkey2ssh E5151B7FDCA95A14
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeNXjx+2M1F7CuYRMkoHv6iUnXe93JatAjhmh1ciXIrTk/Agc4JEgb9uTxYA3pNe/qXVSrSdAqJu0cUENj30rLvKOliL0MH1TxGDnZ0JSxv0UA/skwapRwiTKgsHHng7gbq1/07eBl0luywLT1E/4sbeZ6cAK9e8JAO9GahiyYnrzt2nXzoVxGYl2AHkHFuCqHEMH/KQuQ8Tba+ZjqpRbjnreuI9tJQ8eWpMjLr2AYuWgAU5GtbWFHJi0WJI/2kYybT7co7Kldoxg8PRvBE/QQPdP811jc06pf4CVgfCGvCWZaslqG5pLy8LneqYciuQuXDCQMlAWniThTPjf5VLhx COMMENT


Paper backup of the secret keys

If you don't trust SD-Card longevity...

apt-get install paperkey
gpg --export-secret-key key_id | paperkey

OpenPGP cards

See https://en.wikipedia.org/wiki/OpenPGP_card for existing cards.

OpenPGP card 3.x JavaCard Applets

  • SmartPGP, an implementation of the OpenPGP card 3.x specification in JavaCard (>=3.0.4) by ANSSI, including elliptic curves and RSA-4096
sudo apt-get install ant
git clone git@github.com:martinpaljak/oracle_javacard_sdks.git
git clone git@github.com:ANSSI-FR/SmartPGP.git
cd SmartPGP
echo JAVACARD_HOME=$(pwd)/../oracle_javacard_sdks/jc304_kit > javacard.properties
ant
# => ./build/fr/anssi/smartpgp/javacard/smartpgp.cap

This requires a JavaCard 3.0.4 compliant card but it might be used with JavaCard 3.0.1 cards without the ECC parts, see this issue and the javacard-3.0.1 branch.

OpenPGP card 2.x JavaCard Applets

See also https://subgraph.com/sgos/documentation/smartcards/index.fr.html

Yubikey and FluffyKaon implementations can be easily compiled from https://github.com/martinpaljak/AppletPlayground:

git clone https://github.com/martinpaljak/AppletPlayground.git
cd AppletPlayground
ant

The author has also a GlobalPlatform tool to inject the CAP into the card.
To compile it you need openjdk-7-jdk which at the moment seems hard to get on my Debian (old dependencies problem), so I'll stick to openjdk-8 and fetch a pre-built version here.
Usage:

java -jar gp.jar --help

Or using JCOP's jcshell:

/card
auth
upload OpenPGPApplet.cap
install D27600012401 D2760001240102000000000000010000
ctrl^d

Testing the newly created OpenPGP card:

gpg --card-status

Adding an OpenPGP card

How to add a card (YubiKey or JavaCard) to an existing key, using our backup SDCard.

First check that the card is accessible:

$ gpg --card-status
Reader ...........: Yubico Yubikey 4 OTP U2F CCID 00 00
Application ID ...: D2760001240102010006069331940000
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: 06933194
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
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

You may have problems getting it detected, especially if another card was used before. Reboot.

Map the SDCard:

$ pkill gpg-agent
# mount --bind /media/phil/GNUPG ~/.gnupg

Import again the keys (mainly the encryption key) moved to previous cards:

$ gpg --import < ~/.gnupg/9B554C36544C89BC-2016-02-04-47B68B62B62C8F88-secret.pgp

Create new sub-keys:

$ gpg --edit-key 9B554C36544C89BC
gpg> addcardkey
Please select the type of key to generate:
   (1) Signature key
# remember default PIN is 123456, default admin PIN is 12345678
What keysize do you want for the Signature key? (2048) 4096
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y
# wait for key to be generated and provide PINs if requested
gpg> addcardkey
Please select the type of key to generate:
   (3) Authentication key
What keysize do you want for the Authentication key? (2048) 4096
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y
gpg> save

Btw the notation "sec#" or "ssb#" means the key or subkey is not accessible (it's on another keyring or card).
The notation "sec>" or "ssb>" means the card or subcard is stored on a card known to the system.
The same convention is used with gpg --list-secret-keys

Now we can try to transfer the encryption key but it will probably fail because it's still associated with the previous card:

$ gpg2 --edit-key 9B554C36544C89BC
ssb  rsa2048/0x47B68B62B62C8F88
     created: 2016-02-04  expires: 2018-02-03  usage: E   
     card-no: 0000 00000001                                  <= still previous card
gpg> toggle
gpg> key 1
ssb* rsa2048/0x47B68B62B62C8F88
     created: 2016-02-04  expires: 2018-02-03  usage: E   
     card-no: 0000 00000001                                  <= still previous card
gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2
gpg: KEYTOCARD failed: Unusable secret key

The only solution I found was to clean the secret keyring, import again and sync card again:

$ pkill gpg-agent
$ mv .gnupg/private-keys-v1.d/ .gnupg/private-keys-v1.d.old
$ gpg --import < ~/.gnupg/9B554C36544C89BC-2016-02-04-47B68B62B62C8F88-secret.pgp
$ gpg --card-status
$ gpg2 --edit-key 9B554C36544C89BC
Secret key is available.
sec  rsa4096/0x9B554C36544C89BC
     created: 2016-02-04  expires: 2018-02-03  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa2048/0x47B68B62B62C8F88
     created: 2016-02-04  expires: 2018-02-03  usage: E   
sub  rsa2048/0xAEBAADBEE208E2DD
     created: 2016-02-04  expires: 2018-02-03  usage: S   
sub  rsa2048/0xE5151B7FDCA95A14
     created: 2016-02-04  expires: 2018-02-03  usage: A   
sub  rsa2048/0xF7B61B5663205C74
     created: 2017-12-02  expires: 2019-12-02  usage: S   
sub  rsa2048/0xBDBF13DADE186465
     created: 2017-12-02  expires: 2019-12-02  usage: A   
ssb  rsa4096/0xC68777A356ACFA91
     created: 2017-12-07  expires: 2019-12-07  usage: S   
     card-no: 0006 06933194
ssb  rsa4096/0xC83BE1071CDE1015
     created: 2017-12-07  expires: 2019-12-07  usage: A   
     card-no: 0006 06933194

We see the encryption is not bound to a card anymore and the two new subkeys are bound to our card. So we can try again:

$ gpg2 --edit-key 9B554C36544C89BC
ssb  rsa2048/0x47B68B62B62C8F88
     created: 2016-02-04  expires: 2018-02-03  usage: E   
gpg> toggle
gpg> key 1
ssb* rsa2048/0x47B68B62B62C8F88
     created: 2016-02-04  expires: 2018-02-03  usage: E   
gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2
gpg> save

Export the public key and unmount the SDCard:

$ gpg --armor --export 9B554C36544C89BC > ~/.gnupg/9B554C36544C89BC_v3.asc
$ pkill gpg-agent
$ sudo umount ~/.gnupg
$ gpg --import < /media/phil/GNUPG/9B554C36544C89BC_v3.asc
$ gpg --card-status
$ gpg --edit-key 9B554C36544C89BC

The encryption key is probably still linked to the previous card.
Again, I didn't see a clean way to do it.
Backup your secret keyring before messing up with it, then, try to find the file corresponding to the encryption key. It's a file smaller than the others, containing a string "shadowed-private-key". Compare it with the other gnupg secret keyring: /media/phil/GNUPG/private-keys-v1.d/ and replace it with the new copy (or just delete it, it should be synced again from the card).

cp /media/phil/GNUPG/private-keys-v1.d/AFE2DF0A021D08C752DE0ED0A8D020E8E57F4296.key ~/.gnupg/private-keys-v1.d

Now edit the key again, the encryption subkey should be associated with the new card.

Edit the card for the final adjustments:

$ gpg --card-edit
gpg/card> admin
gpg/card> passwd
1 - change PIN
Your selection? 1
PIN changed.
3 - change Admin PIN
Your selection? 3
PIN changed.
Your selection? q
gpg/card> forcesig
gpg/card> name
Cardholder's surname: Teuwen
Cardholder's given name: Philippe
gpg/card> url
URL to retrieve public key: http://www.yobi.be/files/9B554C36544C89BC.asc
gpg/card> fetch
gpg: requesting key from 'http://www.yobi.be/files/9B554C36544C89BC.asc'
gpg/card> quit


Push the updated key to various keyservers.

gpg --keyserver hkps.pool.sks-keyservers.net --send-key 9B554C36544C89BC

GnuPG signing parties

Short GnuPG reference card

GnuPG old notes