Thinkpad X250 - SmartCards and GPG

This post describes setup and example usages of Smartcard with a Thinkpad's onboard reader and OpenPGP to handle keys for authentication and encryption. 

Your master key will be stored (securely I hope) on a USB drive and rarely used, with your Smart Card containing a couple of subkeys which will be used to sign and authenticate day-to-day.

At the end of the guide you should have a master key securely stored on a USB key, a hard-copy of revocation certificate,  some sub-keys stored on your Smart Card and some knowledge about how to use it to emails, and authenticate via ssh:

There are already very technical guides on how to set this up like the one on jclement.ca which steps 1-5 heavily lean on but even if you're pretty tech-savvy you may end up not 100% understanding exactly what you've actually done. 

I got my Smart Card by becoming a member of the EFSF fellowship or by picking up another OpenPGP smartcard (or a YubiKey if you don't have a reader, here).

Step 1. Securely create and boot from a Debian Live USB image

Download and verify a Debian live image per my previous guide here. We can now flash the USB key we're going to boot from:

    $ sudo dd bs=4M if=./debian-live-8.3.0-amd64-xfce-desktop+nonfree.iso of=/dev/sdb && sync
    272+0 records in
    272+0 records out
    1140850688 bytes (1.1 GB) copied, 214.052 s, 5.3 MB/s
Yes I used a very slow USB flash drive and it was rather painful.

Step 2. Setup the packages

Next couple of sections are pretty much the same steps as the jclement.ca guide - install gnupg2 and libraries we'll use:
    $ sudo apt-get install haveged gnupg2 gnupg-agent libpth20 pinentry-curses libccid pcscd scdaemon libksba8 paperkey opensc jpegoptim xloadimage
Change the configuration file for GnuPG so that it uses a different, stronger set of ciphers by default:
    $ mkdir ~/.gnupg
    $ cat > ~/.gnupg/gpg.conf << !
    no-emit-version
    no-comments
    keyid-format 0xlong
    with-fingerprint
    use-agent
    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
    !

At this point jclement.ca advise that you disconnect from the network - which is probably a good idea since we're about to generate a handful of keys and we don't want anything to leak. 

What we're going to do is create a Master Key - which will be stored on a USB drive - and then use it to create a handful of Sub Keys which will be stored on the Smart Card for day-to-day use. Since the Sub Keys could conceivably be compromised we'll generate a revocation certificate which we can use to notify everyone that they should no longer be trusted - at this point we'd generate a new set of Sub Keys and load them onto our card.

Since there are a number of utilities and technologies you may not be familiar with I'm going to show a diagram at the end of each step in the key-creation process to help visualise what exactly is going on where. Here's a little diagram showing what symbols I'll be using.

Step 3. Creating the Master Key

    $ gpg2 --gen-key
    gpg (GnuPG) 2.0.26; Copyright (C) 2013 Free Software Foundation, Inc.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    gpg: keyring `/home/sean/.gnupg/secring.gpg' created
    gpg: keyring `/home/sean/.gnupg/pubring.gpg' created
    Please select what kind of key you want:
       (1) RSA and RSA (default)
       (2) DSA and Elgamal
       (3) DSA (sign only)
       (4) RSA (sign only)
    Your selection? 4
    RSA keys may be between 1024 and 4096 bits long.
    What keysize do you want? (2048) 4096
    Requested keysize is 4096 bits
    Please specify how long the key should be valid.
             0 = key does not expire
            = key expires in n days
          w = key expires in n weeks
          m = key expires in n months
          y = key expires in n years
    Key is valid for? (0) 0
    Key does not expire at all
    Is this correct? (y/N) y
    
    GnuPG needs to construct a user ID to identify your key.
    
    Real name: Sean McLemon
    Email address: sean.mclemon@gmail.com
    Comment: 
    You selected this USER-ID:
        "Sean McLemon "
    
    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
    You need a Passphrase to protect your secret key.
    
    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    gpg: /home/sean/.gnupg/trustdb.gpg: trustdb created
    gpg: key 0xC87419541EAC16A8 marked as ultimately trusted
    public and secret key created and signed.
    
    gpg: checking the trustdb
    gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
    gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
    pub   4096R/0xC87419541EAC16A8 2016-04-01
          Key fingerprint = D90B 2575 6FD3 D781 A856  9AE3 C874 1954 1EAC 16A8
    uid                 [ultimate] Sean McLemon 
    
    Note that this key cannot be used for encryption.  You may want to use
    the command "--edit-key" to generate a subkey for this purpose.

And add a pic:

    $ gpg2 --edit-key 0xC87419541EAC16A8
    gpg (GnuPG) 2.0.26; Copyright (C) 2013 Free Software Foundation, Inc.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    Secret key is available.
    
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    [ultimate] (1). Sean McLemon 
    
    gpg> addphoto
    
    Pick an image to use for your photo ID.  The image must be a JPEG file.
    Remember that the image is stored within your public key.  If you use a
    very large picture, your key will become very large as well!
    Keeping the image close to 240x288 is a good size to use.
    
    Enter JPEG filename for photo ID: test.jpg
    Is this photo correct (y/N/q)? y
    
    You need a passphrase to unlock the secret key for
    user: "Sean McLemon "
    4096-bit RSA key, ID 0xC87419541EAC16A8, created 2016-04-01
    
    
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    [ultimate] (1). Sean McLemon 
    [ unknown] (2)  [jpeg image of size 746]
    
    gpg> save
So now we've got a Master Key our live distro's temporary filesystem. As I mentioned before this should only ever live on a USB key - to do anything useful we'll need to generate some Sub Keys.

Step 4. Creating the Sub Keys

A bit of a better explanation of Sub Keys is at https://wiki.debian.org/Subkeys. Remember, these are the keys we'll be using day-to-day and will be stored on our Smart Card.

    $ gpg2 --expert --edit-key 0xC87419541EAC16A8
    gpg (GnuPG) 2.0.26; Copyright (C) 2013 Free Software Foundation, Inc.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    Secret key is available.
    
    gpg: checking the trustdb
    gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
    gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    [ultimate] (1). Sean McLemon 
    [ultimate] (2)  [jpeg image of size 746]
    
    gpg> addkey
    Key is protected.
    
    You need a passphrase to unlock the secret key for
    user: "Sean McLemon "
    4096-bit RSA key, ID 0xC87419541EAC16A8, created 2016-04-01
    
    Please select what kind of key you want:
       (3) DSA (sign only)
       (4) RSA (sign only)
       (5) Elgamal (encrypt only)
       (6) RSA (encrypt only)
       (7) DSA (set your own capabilities)
       (8) RSA (set your own capabilities)
    Your selection? 4
    RSA keys may be between 1024 and 4096 bits long.
    What keysize do you want? (2048) 2048
    Requested keysize is 2048 bits
    Please specify how long the key should be valid.
             0 = key does not expire
            = key expires in n days
          w = key expires in n weeks
          m = key expires in n months
          y = key expires in n years
    Key is valid for? (0) 6m
    Key expires at Wed 28 Sep 2016 21:41:58 CEST
    Is this correct? (y/N) y
    Really create? (y/N) y
    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    sub  2048R/0x191900DBF062921B  created: 2016-04-01  expires: 2016-09-28  usage: S   
    [ultimate] (1). Sean McLemon 
    [ultimate] (2)  [jpeg image of size 746]
    
    gpg> addkey
    Key is protected.
    
    You need a passphrase to unlock the secret key for
    user: "Sean McLemon "
    4096-bit RSA key, ID 0xC87419541EAC16A8, created 2016-04-01
    
    Please select what kind of key you want:
       (3) DSA (sign only)
       (4) RSA (sign only)
       (5) Elgamal (encrypt only)
       (6) RSA (encrypt only)
       (7) DSA (set your own capabilities)
       (8) RSA (set your own capabilities)
    Your selection? 6
    RSA keys may be between 1024 and 4096 bits long.
    What keysize do you want? (2048) 2048
    Requested keysize is 2048 bits
    Please specify how long the key should be valid.
             0 = key does not expire
            = key expires in n days
          w = key expires in n weeks
          m = key expires in n months
          y = key expires in n years
    Key is valid for? (0) 6m
    Key expires at Wed 28 Sep 2016 21:42:14 CEST
    Is this correct? (y/N) y
    Really create? (y/N) y
    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    sub  2048R/0x191900DBF062921B  created: 2016-04-01  expires: 2016-09-28  usage: S   
    sub  2048R/0x46BDB50E980A2B9B  created: 2016-04-01  expires: 2016-09-28  usage: E   
    [ultimate] (1). Sean McLemon 
    [ultimate] (2)  [jpeg image of size 746]
    
    gpg> addkey
    Key is protected.
    
    You need a passphrase to unlock the secret key for
    user: "Sean McLemon "
    4096-bit RSA key, ID 0xC87419541EAC16A8, created 2016-04-01
    
    Please select what kind of key you want:
       (3) DSA (sign only)
       (4) RSA (sign only)
       (5) Elgamal (encrypt only)
       (6) RSA (encrypt only)
       (7) DSA (set your own capabilities)
       (8) RSA (set your own capabilities)
    Your selection? 8
    
    Possible actions for a RSA key: Sign Encrypt Authenticate 
    Current allowed actions: Sign Encrypt 
    
       (S) Toggle the sign capability
       (E) Toggle the encrypt capability
       (A) Toggle the authenticate capability
       (Q) Finished
    
    Your selection? s
    
    Possible actions for a RSA key: Sign Encrypt Authenticate 
    Current allowed actions: Encrypt 
    
       (S) Toggle the sign capability
       (E) Toggle the encrypt capability
       (A) Toggle the authenticate capability
       (Q) Finished
    
    Your selection? e
    
    Possible actions for a RSA key: Sign Encrypt Authenticate 
    Current allowed actions: 
    
       (S) Toggle the sign capability
       (E) Toggle the encrypt capability
       (A) Toggle the authenticate capability
       (Q) Finished
    
    Your selection? a
    
    Possible actions for a RSA key: Sign Encrypt Authenticate 
    Current allowed actions: Authenticate 
    
       (S) Toggle the sign capability
       (E) Toggle the encrypt capability
       (A) Toggle the authenticate capability
       (Q) Finished
    
    Your selection? q
    RSA keys may be between 1024 and 4096 bits long.
    What keysize do you want? (2048) 
    Requested keysize is 2048 bits
    Please specify how long the key should be valid.
             0 = key does not expire
            = key expires in n days
          w = key expires in n weeks
          m = key expires in n months
          y = key expires in n years
    Key is valid for? (0) 6m
    Key expires at Wed 28 Sep 2016 21:42:43 CEST
    Is this correct? (y/N) y
    Really create? (y/N) y
    We need to generate a lot of random bytes. It is a good idea to perform
    some other action (type on the keyboard, move the mouse, utilize the
    disks) during the prime generation; this gives the random number
    generator a better chance to gain enough entropy.
    
    pub  4096R/0xC87419541EAC16A8  created: 2016-04-01  expires: never       usage: SC  
                                   trust: ultimate      validity: ultimate
    sub  2048R/0x191900DBF062921B  created: 2016-04-01  expires: 2016-09-28  usage: S   
    sub  2048R/0x46BDB50E980A2B9B  created: 2016-04-01  expires: 2016-09-28  usage: E   
    sub  2048R/0xF1D25AD8AC008AA1  created: 2016-04-01  expires: 2016-09-28  usage: A   
    [ultimate] (1). Sean McLemon 
    [ultimate] (2)  [jpeg image of size 746]
    
    gpg> save

Now we've got the newly created Master and Sub-keys on the local filesystem. 

Step 5. Generate a revocation certificate

    $ gpg2 --gen-revoke 0xC87419541EAC16A8
    
    sec  4096R/0xC87419541EAC16A8 2016-04-01 Sean McLemon 
    
    Create a revocation certificate for this key? (y/N) y
    Please select the reason for the revocation:
      0 = No reason specified
      1 = Key has been compromised
      2 = Key is superseded
      3 = Key is no longer used
      Q = Cancel
    (Probably you want to select 1 here)
    Your decision? 1
    Enter an optional description; end it with an empty line:
    > FSB got hold of my private key
    > 
    Reason for revocation: Key has been compromised
    y
    Is this okay? (y/N) y
    
    You need a passphrase to unlock the secret key for
    user: "Sean McLemon "
    4096-bit RSA key, ID 0xC87419541EAC16A8, created 2016-04-01
    
    ASCII armored output forced.
    Revocation certificate created.
    
    Please move it to a medium which you can hide away; if Mallory gets
    access to this certificate he can use it to make your key unusable.
    It is smart to print this certificate and store it away, just in case
    your media become unreadable.  But have some caution:  The print system of
    your machine might store the data and make it available to others!
    -----BEGIN PGP PUBLIC KEY BLOCK-----
    Comment: A revocation certificate should follow
    
    iQIgBCABCgAKBQJW/s+XAx0CeQAKCRDIdBlUHqwWqEsbEACFj7ZDRgwcvG99F1Hb
    PHzqGPXh5X04nnjpPbnWaKviycvdCQtFT3N3Hg0c1fmgBBDMXHXKcP8dBwrsgmU0
    x1vEFUSmzvaW+s/EZXh8xwfXhGVmBG+H+i5JqfiPXelaKH12/pDPqIIE+jlwFx5O
    a+I3TMg5x5pBzpSWmCNmFgxU4jEJ6SBwsYYDCwGjStS3C8Dojk7RfJug/+wZAhk2
    nuGbHKeL48TmLwVsoqlbut57yUzqJ16wC9u46oOwKUeUnluEhOm8eT8fvQsHwM11
    THx8UshfjY1/2p5oA4e7GOyB93u7mS5+u57dkKiHwDbNKVq++rMnJH5nesgOT3f6
    D/5quHjQNUXwGJsxu3H+ZSxAmmj2q8ooaPZfDxeAqSgjLSu3vTQGK8HtsnJ3Cgu5
    tANW4SrDrqqvmpNzutGkmfLRgN/stSLi+MJz78h87nCDPQkMUmp6fEk2kEnVmj1B
    BEMY5SSrJrIDGwf4CGn4kzHDb9/GlS47x33LcE8g7F1lM1LQ7eNvRNpDU36dmiwj
    luJLtl0tKVW4YBz4Yo7AxmT0QYSCgXHWStt9XB1devps1CJ45dlbPNzAE8rJH2+1
    3YjZfFAgiSFFHMsq0C1M2+mQaqrAyGUVT/lR42Tok0GgwaZK48VWeP+cRZu07NDr
    pxodVfJ7TjcVUF04AEtSxayfbg==
    =ck5O
    -----END PGP PUBLIC KEY BLOCK-----
    
JClement suggests printing this out - if you do so it might want to create a QR code to make it easier to digitize. You might want avoid QR code generator sites since we're aiming for security, but you can generate one easily enough using python qrcode module:
    $ pip install qrcode
    Downloading/unpacking qrcode
      Downloading qrcode-5.2.2-py2.py3-none-any.whl (89kB): 89kB downloaded
    Requirement already satisfied (use --upgrade to upgrade): colorama in /usr/lib/python2.7/dist-packages (from qrcode)
    Requirement already satisfied (use --upgrade to upgrade): six in /usr/lib/python2.7/dist-packages (from qrcode)
    Installing collected packages: qrcode
    Successfully installed qrcode
    Cleaning up...
    $ python
    Python 2.7.9 (default, Mar  1 2015, 12:57:24) 
    [GCC 4.9.2] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import qrcode
    >>> cert = open("revoke_cert.asc")
    >>> cert_text = cert.read()
    >>> cert_qr = qrcode.make(cert_text)
    >>> cert_qr.save("revoke_cert.png")
The certificate itself is pretty sizeable, so the resulting QR code is pretty dense and you'll need a reasonable camera to successfully scan it (my iPhone 6S worked nicely). Using the lines above the resulting image will look a little like this - it's a little huge so I didn't want to include it in-line.

So assuming we've printed out revoke_cert.png and discarded the .asc file, at this point we should have created:

Step 6. Backup GPG and store keys on SD/USB

Now we've generated our keys we can copy them somewhere safe (an SD card, or USB)
    $ tar -czf gnupg.tgz ~/.gnupg
    tar: Removing leading `/' from member names
    $ gpg2 -a --export-secret-key 0xC87419541EAC16A8 >> 0xC87419541EAC16A8.master.key
    $ gpg2 -a --export-secret-subkeys 0xC87419541EAC16A8 >> 0xC87419541EAC16A8.subkeys.key
    $ gpg2 -a --export 0xC87419541EAC16A8 > 0xC87419541EAC16A8.public.key.asc
    $ sudo cp gnupg.tgz 0xC87419541EAC16A8.master.key 0xC87419541EAC16A8.subkeys.key 0xC87419541EAC16A8.public.key.asc /media/SDCARD/

Note - it is important to do this. Once we restart the filesystem of the live CD will no longer exist, and we'll lose all of our keys. then load the subkeys to the card.

Now we can distribute the key:

    $ gpg2 --keyserver hkp://pool.sks-keyservers.net --send-keys 0xC87419541EAC16A8
    gpg: sending key 0xC87419541EAC16A8 to hkp server pool.sks-keyservers.net

Now we can reboot, upload our public key to the internet and here's what we have - a master key stored offline/disconnected, a set of sub-keys on the smartcard we can use for everyday tasks, a printed certificate we can use to revoke our subkeys, and a public key somewhere on the net we can share with anyone we need to communicate securely with.

Step 7. Boot into usual OS + load keys

When you're sufficiently certain you've got a nice backup of the .gnupg directory, and the subkeys loaded to the card we can boot into our normal OS and remove the live USB card. 

We'll need to 

    $ gpg2 --import 0xC87419541EAC16A8.public.key.asc
    gpg: /home/sean/.gnupg/trustdb.gpg: trustdb created
    gpg: key C3969A6B: public key "Sean McLemon " imported
    gpg: Total number processed: 1
    gpg:               imported: 1  (RSA: 1)
Now everything is set - we explore a few situations where you might want to use a smart card.

Example 1. Signing/Encrypting a text file using your card

There's a very good guide produced by the Free Software Foundation @ https://emailselfdefense.fsf.org which you can follow if you want to use EnigMail and Thunderbird - if you want an easy way to sign/encrypt emails you should follow that. However if we just want to sign a message in a text file from the command line, we could do something like the below

    $ echo > msg << EOF
    > paddy schwartz party time
    > EOF
    $ gpg2 --output msg.sig --sign msg

Example 2. Decrypting a file using your card

If someone's sent you a file they'll encrypt it using your public key. You can use the keys stored on your card to decrypt it.

To set this up we'll first encrypt a simple text file using our own public key:

    $ echo "a funky test message" > plaintext.asc
    $ gpg2 --out cyphertext --recipient 0x2F3F79CDC3969A6B --encrypt plaintext.asc
    gpg: 720D24AD: There is no assurance this key belongs to the named user
    
    pub  2048R/720D24AD 2016-03-24 Sean McLemon 
     Primary key fingerprint: 63EB 6DF3 C42E 1AB3 92B5  BF02 2F3F 79CD C396 9A6B
          Subkey fingerprint: 3E3C 084E 622A BB06 EEFB  A739 A3FE 2BC1 720D 24AD

    It is NOT certain that the key belongs to the person named
    in the user ID.  If you *really* know what you are doing,
    you may answer the next question with yes.

    Use this key anyway? (y/N) y

OK now we'll verify that the card isn't connected, and that we cannot decrypt it without the card

    $ gpg2 --card-status
    gpg: selecting openpgp failed: Card not present
    gpg: OpenPGP card not available: Card not present
    $ gpg2 --out plaintext-decrypted.asc --decrypt cyphertext 
    gpg: selecting openpgp failed: Card not present
    gpg: encrypted with 2048-bit RSA key, ID 720D24AD, created 2016-03-24
          "Sean McLemon "
    gpg: public key decryption failed: Operation cancelled
    gpg: decryption failed: No secret key

Now if we insert the smart card and try to decrypt again we'll be prompted for our PIN, and the file will be decrypted successfully:

    $ gpg2 --out plaintext-decrypted.asc --decrypt cyphertext
    gpg: encrypted with 2048-bit RSA key, ID 720D24AD, created 2016-03-24
          "Sean McLemon "
    $ cat plaintext-decrypted.asc 
    a funky test message

Example 3. Authenticating with a remote machine using your card

    $ echo enable-ssh-support >> ~/.gnupg/gpg-agent.conf
    $ sudo emacs /etc/X11/Xsession.options # and comment/remove the line "use-ssh-agent"

Now we'll add our public key to the computer we want to connect to using ssh - in my case it's mokpo.local

    $ ssh-add -L | ssh mokpo.local 'cat >> ~/.ssh/authorized_keys'
and we can now test logging in

Example 4. Revoking your keys using the QR code

If our key is ever compromised and we'd like to revoke we will need to issue a revocation certificate to say that this key shouldn't ever be trusted. We can use the QR code we previously generated + printed out - first scan the QR code and save the results into a file revoke-certificate-qr.txt, and perform the following steps:

$ gpg2 --import revoke-certificate-qr.txt
$ gpg2 --keyserver hkp://pool.sks-keyservers.net --send-keys 0xC87419541EAC16A8

Now the world should know not to trust our old keypair, and we can go back to the start of this article and generate a completely new one - this time being extra careful to keep it safe!