Smart Card / PKCS#11 support
OpenConnect supports the use of X.509 certificates and keys from smart cards (as well as software storage such as GNOME Keyring and SoftHSM) by means of the PKCS#11 standard. Objects from PKCS#11 tokens are specified by a PKCS#11 URI according to RFC 7512.
In order to use a certificate or key with OpenConnect, you must provide a PKCS#11 URI which identifies it sufficiently. That can be as simple as the following example:
- openconnect -c pkcs11:id=%01 vpn.example.com
Identifying the token
In order to use a PKCS#11 token with OpenConnect, first it must be installed appropriately in the system's p11-kit configuration. You shouldn't need to worry about this; it should automatically be the case for properly packaged software on any modern operating system.
Typically, the smart card support is likely to be provided by OpenSC and a distribution's packaging of OpenSC should automatically have registered the OpenSC module with p11-kit by creating a file such as /usr/share/p11-kit/modules/opensc.module.
In order to query the available PKCS#11 modules, and the certificates stored therein, the best tool to use is the p11tool distributed with GnuTLS. In Fedora it's in the gnutls-utils package.
First identify the PKCS#11 modules which are available by using the --list-tokens option:
- p11tool --list-tokens
Token 7: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29 Label: PIV_II (PIV Card Holder pin) Type: Hardware token Manufacturer: piv_II Model: PKCS#15 emulated Serial: 108421384210c3f5 |
This example shows the relatively common PIV SmartCard, in this case in a Yubikey NEO device.
Locating the certificate
Having established that the token is present and registered correctly with p11-kit, the next step is to identify the URI of the certificate you wish to use. You will note that the above output of p11tool --list-tokens gave a PKCS#11 URI for each token. With that, we can now query the objects available within a specific token, using the --list-all-certs option. We can cut and paste the PKCS#11 URI for the token, but be careful to put it within quotes because it contains semicolons:
- p11tool --list-all-certs 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29'
Note that the PKCS#11 URI specifies a list of attributes which must match. Some of these match criteria may be redundant — in this case we've asked it to list the certificates in a token which has a model of "PKCS#15 emulated" and a manufacturer of "piv_II" and serial number 108421384210c3f5 and token label "PIV_II (PIV Card Holder pin)". Since any one of those criteria would probably be sufficient to uniquely identify this token from the other configured tokens in our system, a simpler command line would also work. For example:
- p11tool --list-all-certs pkcs11:manufacturer=piv_II
Object 0: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=Certificate%20for%20PIV%20Authentication;object-type=cert Type: X.509 Certificate Label: Certificate for PIV Authentication ID: 01 Object 1: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%02;object=Certificate%20for%20Digital%20Signature;object-type=cert Type: X.509 Certificate Label: Certificate for Digital Signature ID: 02 Object 2: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%03;object=Certificate%20for%20Key%20Management;object-type=cert Type: X.509 Certificate Label: Certificate for Key Management ID: 03 Object 3: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%04;object=Certificate%20for%20Card%20Authentication;object-type=cert Type: X.509 Certificate Label: Certificate for Card Authentication ID: 04 |
This device has four certificates installed; the URL for each one is given in the output. (Choosing between the certificates on a given device, if there is more than one, is left as an exercise for the user. You may need to try each one.)
Some devices may not even permit you to list the certificates without logging in. In that case add --login to the p11tool command line above, and provide the PIN when requested
For OpenConnect 7.01 we should be able to use the URI seen here in its entirety, and the software will be cunning enough to find the corresponding key:
- openconnect -c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=Certificate%20for%20PIV%20Authentication;object-type=cert' vpn.example.com
Helping OpenConnect find the key
If no explicit -k argument is given to specify the key, OpenConnect will use the contents of the -c argument as the basis for finding both certificate and key.
It will sensibly add object-type=cert or object-type=private for itself, according to which object it is trying to locate each time. But in version 7.00 and earlier, it would not do that if the URI you provide already contained any object-type= element. So the first thing you need to do with older versions of OpenConnect is trim that part of the URI. So the above example might now be:
- openconnect -c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=Certificate%20for%20PIV%20Authentication' vpn.example.com
Additionally, it can sometimes be the case that although the ID (id=) for a certificate should match the ID of its matching key, the label (object=) might not match. Newer versions of OpenConnect (7.01+), on failing to find a key, will strip the label from the search URI and add the ID of the certificate that was found (even if no ID was part of the original search terms provided with the -c option). But older versions don't.
So it can be useful also to remove the object= part of the URI and leave only the id= attribute to specify the individual object, so that you're giving search criteria which are true for both the certificate and the key:
- openconnect -c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01' vpn.example.com
And while we're at it, that's still a massively redundant way of specifying which token to look in, so we can cut that down as we did before just to make it less unwieldy:
- openconnect -c 'pkcs11:manufacturer=piv_II;id=%01' vpn.example.com
Searching for the key manually
If the heuristics for finding the key don't work, you can always provide an explicit PKCS#11 URI for the key with the -k option. You can look for them by using the --list-privkeys option to p11tool. You will almost certainly want to use the --login option too:
- p11tool --list-privkeys --login pkcs11:manufacturer=piv_II
Token 'PIV_II (PIV Card Holder pin)' with URL 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29' requires user PIN Enter PIN: Object 0: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=PIV%20AUTH%20key;object-type=private Type: Private key Label: PIV AUTH key Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_SENSITIVE; ID: 01 Object 1: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%02;object=SIGN%20key;object-type=private Type: Private key Label: SIGN key Flags: CKA_PRIVATE; CKA_SENSITIVE; ID: 02 Object 2: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%03;object=KEY%20MAN%20key;object-type=private Type: Private key Label: KEY MAN key Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_SENSITIVE; ID: 03 Object 3: URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%04;object=CARD%20AUTH%20key;object-type=private Type: Private key Label: CARD AUTH key Flags: CKA_SENSITIVE; ID: 04 |
Here's the full longhand specification of both certificate and key:
- openconnect -c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=Certificate%20for%20PIV%20Authentication;object-type=cert' -k 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=108421384210c3f5;token=PIV_II%20%28PIV%20Card%20Holder%20pin%29;id=%01;object=PIV%20AUTH%20key;object-type=private' vpn.example.com