YubiKey Authentication With pam_pkcs11.so
YubiKey and PKCS #11
A YubiKey is a
small hardware authentication device
that plugs into a USB port.
Several models
are available, for US$ 20–70.
Public Key Cryptography Standard or PKCS #11
defines a generic programming interface for software to
interact with cryptographic tokens.
The pam_pkcs11.so
library module has been the
common method for specifying YubiKey or Smart Card
authentication through the
PAM mechanism on Linux.
While pam_pkcs11.so
has been dropped from
Red Hat and Oracle Linux major release 8,
it's still available in Debian 11 and there are many
legacy RHEL 7 and Oracle Linux 7 systems out there.
Let's see how to set up YubiKey authentication
with the pam_pkcs11.so
PAM library.
What Are We Testing?
We are checking to see whether the user posesses a YubiKey containing a certificate for them. The certificate must list their user name as the Canonical Name or CN, and it must be signed by a trusted Certificate Authority or CA.
Real-worldPIN study
The user must enter the device PIN, so you could argue that this accomplishes multi-factor authentication on its own — the device is the something you have while the PIN is something you know. The PIN, however, is a maximum of six digits, with a much smaller search space than a typical password or pass phrase.
We create and operate the CA operation, so we trust it
on a human level as a credential issuer.
We can validate certificates issued by the CA by using
database files stored under /etc/pki/nssdb
on the system.
Overview
We need to establish the CA, create the user keys and certificate, load data into the YubiKey device, and record the CA's certificate in the system's database. Here are the steps in more detail:
1
Generate the CA key pairs.
On Linux the file names don't matter.
However, some systematic naming convention
will make things much easier
for you to keep track of what is in which file.
I will put the CA's RSA and EC key pairs into files
RootCA_RSA.key
and
RootCA_EC.key
,
respectively.
2
Create self-signed certificates from the CA public keys.
I will store these in
RootCA_RSA.pem
and
RootCA_EC.pem
.
3
Create user key pairs.
I will store these in
username_RSA.key
and
username_EC.key
,
with username
changed to their
actual user name.
4
Create Certificate Signing Requests or CSRs for the
user public keys.
I will store these in
username_RSA.csr
and
username_EC.csr
.
5
Create certificates for the user public keys,
signing them with the CA key.
I will store these in
username_RSA.crt
and
username_EC.crt
.
6
Create PFX (or PKCS #12) files for the user identities.
Each includes a public key and its corresponding private key.
I will store these in
username_RSA.pfx
and
username_EC.pfx
.
7 Load a PFX key into each YubiKey device.
8 Configure the operating system to map Linux user names to CN strings.
9
Store the CA self-signed certificates in databases
in /etc/pki/nssdb/
.
10
Configure PAM to use pam_pkcs11.so
for
user authentication.
1: Generate CA key pairs
These keys will be read by the YubiKey device, so we are
limited to RSA 1024-bit, RSA 2048-bit, or ECC secp256r1
(also known as NIST P-256) keys.
We will call this "the Root CA", although you very likely
already have a PKI or Public Key Infrastructure operation
in place.
The name makes sense though,
as the CA will be our root of trust for user authentication.
And so it would be a good idea to include the
-aes256
option to encrypt and thus
password-protect the keys.
Let's generate both RSA 2048 and ECC P-256 key pairs.
Without the named_curve
option for the ECC keys,
the elliptic curves will be explicitly defined.
Many tools require the names, rather than definitions,
including ykman
and certutil
.
We will use the first to load and check the YubiKey,
and the second to install the certificates on the server.
See the manual page for the individual openssl
commands for further details.
In this case, the genpkey
manual page.
# openssl genpkey -algorithm RSA \ -pkeyopt rsa_keygen_bits:2048 \ -aes256 \ -out RootCA_RSA.key .....................+++++ .....................................................................+++++ Enter PEM pass phrase: ØØØØØØØØØØØØØØØ Verifying - Enter PEM pass phrase: ØØØØØØØØØØØØØØØ # openssl genpkey -algorithm EC \ -pkeyopt ec_paramgen_curve:P-256 \ -pkeyopt ec_param_enc:named_curve \ -aes256 \ -out RootCA_EC.key Enter PEM pass phrase: ØØØØØØØØØØØØØØØ Verifying - Enter PEM pass phrase: ØØØØØØØØØØØØØØØ # ls -l RootCA_* -rw------- 1 root root 399 Jan 08 10:31 RootCA_EC.key -rw------- 1 root root 1874 Jan 08 10:31 RootCA_RSA.key # head -7 RootCA_* ==> RootCA_EC.key <== -----BEGIN ENCRYPTED PRIVATE KEY----- MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhIGkdgUnNpGQICCAAw DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEGTb0I49WVBNwduOG3NYcs0EgZCv KthpVHx+/8OkIEN8cseccV2/gf3w5qf0DQ4kv1QOqhAGU8Frpxzo5aNjp5BF/95m GlhsFiefzMD5OOX4rcVkrlgvfsKOezUHzseSA+iUt7rosNwASJOyaVlxaH62yvPw 31CLSh1QHQghDBF4JTaMl2fsVhjskxxN94GhSDhkpFanVCromsMCH+gZD/goIGA= -----END ENCRYPTED PRIVATE KEY----- ==> RootCA_RSA.key <== -----BEGIN ENCRYPTED PRIVATE KEY----- MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIzxyLRv9ZKvsCAggA MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAEhtSomyXixnoYqEsuO6LKBIIE 0GTKM352addByzrsbCqKIg0z4LicmQ/IesrRsiJE3K0nCk4epHKBJLhlNixg+94O 58D+wsB62dqR1nu6ujEGKA30CRLZqLBOAde1fu9DZpLgdYkh6lWHuT5t8bzYi86z 0qkNcntp3SDcf7pMkW/OWTvJSSOXIn6jJs3gPB4k8zoDNFDDwoMFddlDDbkZM6pk Ud9IVnWW0WP+ZUfFdr/nAHlHpF+rN7SEH85tHNKjNXKuC/N1KTlDfSGj1F6xQwHC
The resulting *.key
files
say that they contain private keys.
Actually they contain both the private and public key.
They contain an asymmetric key pair, and have been encrypted
with the symmetric AES-256 cipher using a key derived from
the pass phrase.
2: Create self-signed CA certificates
We are going to create a self-signed certificate. It means that the CA is saying "I exist and the following is my public key. Believe this because I signed it using the corresponding private key and so it must be true."
By itself, that's meaningless. We must simply trust that the CA's public key really is what it claims to be, that the cryptography is strong enough, and (toughest of all) that the people operating the CA are skilled, careful, and utterly trustworthy on matters of personal identity.
We could do the signing in an interactive fashion. We're asking for a certificate good for the next 365 days, you can of course change that. If you don't specify that, the default is only 30 days! Here we go with an interactive run:
# openssl req -x509 -new -key RootCA_RSA.key \
-days 365 -out RootCA_RSA.pem
Enter pass phrase for RootCA_RSA.key: ØØØØØØØØØØØØØØØ
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:Indiana
Locality Name (eg, city) [Default City]:West Lafayette
Organization Name (eg, company) [Default Company Ltd]:Cromwell International
Organizational Unit Name (eg, section) []:HQ
Common Name (e.g. server FQDN or YOUR name) []:pki.cromwell-intl.com
Email Address []:
# file RootCA_RSA.pem
RootCA_RSA.pem: PEM certificate
# head RootCA_RSA.pem
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIURskiIsoYK0xJ26tujrjovAssB1kwDQYJKoZIhvcNAQEL
BQAwgYYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdJbmRpYW5hMRcwFQYDVQQHDA5X
ZXN0IExhZmF5ZXR0ZTEfMB0GA1UECgwWQ3JvbXdlbGwgSW50ZXJuYXRpb25hbDEL
MAkGA1UECwwCSFExHjAcBgNVBAMMFXBraS5jcm9td2VsbC1pbnRsLmNvbTAeFw0y
MTAzMjYxMjEzMzRaFw0yMjAzMjYxMjEzMzRaMIGGMQswCQYDVQQGEwJVUzEQMA4G
A1UECAwHSW5kaWFuYTEXMBUGA1UEBwwOV2VzdCBMYWZheWV0dGUxHzAdBgNVBAoM
FkNyb213ZWxsIEludGVybmF0aW9uYWwxCzAJBgNVBAsMAkhRMR4wHAYDVQQDDBVw
a2kuY3JvbXdlbGwtaW50bC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDMztNo49FqdmkFK2rs5ABiSvQXw8zJ6Gua1jNQBoUn+qo1d7vL4jvRPlhZ
You might want to create a script to do this,
with everything but the pass phrase automated.
If so, you must use one -subj
option with a
very long string.
If you use multiple -subj
options, each with a
part of the subject definition, then only the last one is used.
# openssl req -x509 -new -key RootCA_RSA.key \ -subj "/C=US/ST=Indiana/L=West\ Lafayette/O=Cromwell\ International/OU=HQ/CN=pki.cromwell-intl.com/" \ -days 365 -out RootCA_RSA.pem Enter pass phrase for RootCA_RSA.key: ØØØØØØØØØØØØØØØ # openssl req -x509 -new -key RootCA_EC.key \ -subj "/C=US/ST=Indiana/L=West\ Lafayette/O=Cromwell\ International/OU=HQ/CN=pki.cromwell-intl.com/" \ -days 365 -out RootCA_EC.pem Enter pass phrase for RootCA_EC.key: ØØØØØØØØØØØØØØØ # ls -l RootCA_* -rw------- 1 cromwell cromwell 399 Jan 08 10:31 RootCA_EC.key -rw-rw-r-- 1 cromwell cromwell 887 Jan 08 10:31 RootCA_EC.pem -rw------- 1 cromwell cromwell 1874 Jan 08 10:31 RootCA_RSA.key -rw-rw-r-- 1 cromwell cromwell 1424 Jan 08 10:31 RootCA_RSA.pem
You can examine the details.
The -noout
option means "Don't include the encoded
certificate, the literal content of the certificate file."
# openssl x509 -in RootCA_RSA.pem -text -noout | less Certificate: Data: Version: 3 (0x2) Serial Number: 4a:d7:51:58:41:07:d4:73:18:2d:34:3a:69:ee:8d:d7:a1:06:24:55 Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Validity Not Before: Jan 8 10:31:33 2025 GMT Not After : Jan 8 10:31:33 2026 GMT Subject: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:cc:ce:d3:68:e3:d1:6a:76:69:05:2b:6a:ec:e4: 00:62:4a:f4:17:c3:cc:c9:e8:6b:9a:d6:33:50:06: 85:27:fa:aa:35:77:bb:cb:e2:3b:d1:3e:58:59:83: 7e:7a:dd:78:85:c2:8b:ca:76:48:45:0c:0e:97:02: 0b:6d:b5:a0:20:10:7e:24:97:0e:2d:8d:fb:38:42: 62:b2:19:f8:5a:9d:51:d1:a0:72:1d:8c:0e:4d:17: 51:97:8a:70:03:0d:c8:15:85:dc:05:6e:d8:ee:d6: 99:d8:bd:ab:ec:38:52:95:71:2a:b8:0b:08:8f:67: d9:c2:81:9c:5d:fb:80:d9:6e:cc:4e:a4:f8:f4:78: 5e:19:1f:a4:6a:66:a4:44:ce:f3:1f:77:d4:ae:87: fe:c7:31:d3:90:98:76:30:ba:76:ba:85:e7:cc:ba: 86:92:e0:44:2e:d6:fa:18:72:1c:08:fb:37:bb:d7: 4b:1f:2f:fe:41:31:dd:62:ab:16:f4:3b:9e:c5:80: 60:4d:47:89:fb:4d:00:ae:ba:bc:d9:b7:1e:1e:75: 0e:9f:27:1c:6a:0b:2b:e8:3b:77:33:c4:01:1e:b0: b9:fb:b4:9a:d5:22:f2:ad:48:f3:7a:40:dd:db:10: 74:95:55:aa:45:64:c3:15:48:57:05:b5:c8:8c:da: ff:2d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 0F:0D:DA:A4:AA:5E:4A:23:61:A1:19:3D:EF:10:49:6B:40:1C:A4:E3 X509v3 Authority Key Identifier: keyid:0F:0D:DA:A4:AA:5E:4A:23:61:A1:19:3D:EF:10:49:6B:40:1C:A4:E3 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption aa:12:9c:ea:47:ce:7e:34:f4:8b:83:e8:65:70:04:bb:35:04: 36:9f:15:ba:51:d6:40:a0:df:64:28:89:5c:af:69:cf:f4:fd: af:f1:1d:37:f5:3b:14:a1:38:49:2c:fb:7f:c5:ae:c6:60:ea: 1d:ae:8f:1a:da:25:68:e9:42:11:30:bc:4c:c3:b2:b6:c1:d7: 2c:d5:31:9d:62:19:ba:11:4d:15:2f:dd:08:01:db:f9:eb:c3: 75:8d:66:bb:6a:46:6c:46:7d:13:35:3a:cd:8e:db:2a:46:d3: 95:92:ba:2d:c0:dc:ba:77:ec:7d:2f:3f:1f:c8:92:e9:9c:56: 75:c1:14:9b:f7:71:95:f0:e7:29:e9:ee:98:28:85:8c:f6:62: f9:9d:f6:a7:e7:db:81:ff:ad:74:6b:b4:4c:a2:de:6f:09:21: 98:c5:70:73:b7:fd:7f:14:a6:60:88:68:d1:77:4f:0f:91:1e: 98:89:32:26:26:d7:4b:3f:5e:02:fc:1c:87:df:c2:61:cd:17: 28:b6:f7:2e:8f:90:eb:5b:38:b8:b9:ca:b3:67:da:bb:49:ed: c8:c2:d4:39:03:26:30:ff:25:1a:5b:3a:a2:e2:33:aa:86:1f: cf:30:26:81:21:a7:ae:49:ef:fc:4e:1f:ce:82:2f:c7:1a:a4: 51:33:02:a7 # openssl x509 -in RootCA_EC.pem -text -noout | less Certificate: Data: Version: 3 (0x2) Serial Number: 70:08:e6:71:d8:b7:ac:80:a6:e9:60:0e:73:8c:ad:5b:90:13:a2:0a Signature Algorithm: ecdsa-with-SHA256 Issuer: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Validity Not Before: Jan 8 10:31:33 2025 GMT Not After : Jan 8 10:31:33 2026 GMT Subject: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:8b:13:bf:d6:a9:8b:75:6d:39:92:4b:fd:cd:ac: 6d:84:c8:a3:1f:69:2c:c4:98:80:6f:39:8a:69:d7: c7:ea:55:ec:39:e0:85:ca:ea:8c:6a:5e:50:eb:32: 22:fe:6d:64:69:d6:60:e7:02:cb:0f:4d:c2:5d:e6: 72:29:48:0f:8b ASN1 OID: prime256v1 NIST CURVE: P-256 X509v3 extensions: X509v3 Subject Key Identifier: 82:83:91:AD:11:30:F9:FA:BE:7F:9F:99:2A:30:88:C3:CB:D2:FC:8E X509v3 Authority Key Identifier: keyid:82:83:91:AD:11:30:F9:FA:BE:7F:9F:99:2A:30:88:C3:CB:D2:FC:8E X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:92:72:25:c0:60:32:df:eb:95:55:b6:f0:8a: 1c:22:24:ab:55:a3:ab:8c:a4:de:b3:4b:55:07:3c:39:06:92: 78:02:20:5b:6a:b3:e6:71:c0:97:1d:e5:2f:7f:50:8e:2e:67: 92:62:9d:bb:5f:1d:33:74:99:14:96:cc:32:1f:c1:45:84
Advanced topic: If you want to include CSP and CRL fields,
use ca
instead of x509
to create the certificate.
See the openssl-ca
manual page for details.
3: Create user key pairs
An Intro toElliptic-Curve
Cryptography
Now we need to generate a key pair for each user. These keys aren't actually used by the YubiKey device, so we aren't limited to the small set of asymmetric ciphers supported by the device. The YubiKey only reads the certificate "wrapper", it doesn't process the public key within it. We could generate a larger RSA key pair, or use a different curve.
On a RHEL or Oracle Linux system we have limited choices
as they have built openssl
to only support
FIPS 140-2 algorithms.
On Oracle Linux 8 I see:
# openssl version OpenSSL 1.1.1g-fips 21 Apr 2020 # openssl ecparam -list_curves secp224r1 : NIST/SECG curve over a 224 bit prime field secp256k1 : SECG curve over a 256 bit prime field secp384r1 : NIST/SECG curve over a 384 bit prime field secp521r1 : NIST/SECG curve over a 521 bit prime field prime256v1: X9.62/SECG curve over a 256 bit prime fieldNIST: Digital Signature Standard (DSS) Certicom/SECG: SEC 2, Recommended Elliptic Curve Domain Parameters
There are many standards defining elliptic curves, each with their own nomenclature. The five above are what OpenSSL calls them.
prime256v1
is what U.S. NIST calls P-256,
and it's also called secp256r1.
We will see below that we can use either the
prime256v1
or
P-256
parameter (but not secp256r1
)
with the openssl
command.
secp224r1
,
secp256r1
(a.k.a. P-256
and prime256v1
),
secp384r1
, and
secp521r1
have what NIST says are randomly chosen parameters,
hence the "r" in their name.
secp256k1
is a Koblitz curve.
It instead has parameters that exchange a few bits' worth
of security for a significant computational speed advantage.
That's just a few bits out of 256, so
secp256k1
and secp256r1
should have comparable security.
That is, as long as there has been no funny business
with the supposedly "random" parameter selection of all
the secp*r1
curves.
backdoors
In 2013 The New York Times reported that NSA had influenced NIST to include a deliberate weakness the the Dual_EC_DRBG random bit stream generator and its associated elliptic curve.
Once that came out, many cryptographers seem to feel that while there's no specific reason to distrust NIST-defined elliptic curve algorithms, we certainly can't have absolute confidence in them.
In other words, just how "random" are those supposedly random curve parameters? Might they look random but actually embody a backdoor? We have no way of knowing.
SafeCurvesThe best reference I know of is the SafeCurves project. They describe security problems with both secp256k1 and secp256r1.
On a Mint Linux or Debian system we have a much broader list of choices.
# openssl version OpenSSL 1.1.1f-fips 31 Mar 2020 # openssl ecparam -list_curves secp112r1 : SECG/WTLS curve over a 112 bit prime field secp112r2 : SECG curve over a 112 bit prime field secp128r1 : SECG curve over a 128 bit prime field secp128r2 : SECG curve over a 128 bit prime field secp160k1 : SECG curve over a 160 bit prime field secp160r1 : SECG curve over a 160 bit prime field secp160r2 : SECG/WTLS curve over a 160 bit prime field secp192k1 : SECG curve over a 192 bit prime field secp224k1 : SECG curve over a 224 bit prime field secp224r1 : NIST/SECG curve over a 224 bit prime field secp256k1 : SECG curve over a 256 bit prime field secp384r1 : NIST/SECG curve over a 384 bit prime field secp521r1 : NIST/SECG curve over a 521 bit prime field prime192v1: NIST/X9.62/SECG curve over a 192 bit prime field prime192v2: X9.62 curve over a 192 bit prime field prime192v3: X9.62 curve over a 192 bit prime field prime239v1: X9.62 curve over a 239 bit prime field prime239v2: X9.62 curve over a 239 bit prime field prime239v3: X9.62 curve over a 239 bit prime field prime256v1: X9.62/SECG curve over a 256 bit prime field sect113r1 : SECG curve over a 113 bit binary field sect113r2 : SECG curve over a 113 bit binary field sect131r1 : SECG/WTLS curve over a 131 bit binary field sect131r2 : SECG curve over a 131 bit binary field sect163k1 : NIST/SECG/WTLS curve over a 163 bit binary field sect163r1 : SECG curve over a 163 bit binary field sect163r2 : NIST/SECG curve over a 163 bit binary field sect193r1 : SECG curve over a 193 bit binary field sect193r2 : SECG curve over a 193 bit binary field sect233k1 : NIST/SECG/WTLS curve over a 233 bit binary field sect233r1 : NIST/SECG/WTLS curve over a 233 bit binary field sect239k1 : SECG curve over a 239 bit binary field sect283k1 : NIST/SECG curve over a 283 bit binary field sect283r1 : NIST/SECG curve over a 283 bit binary field sect409k1 : NIST/SECG curve over a 409 bit binary field sect409r1 : NIST/SECG curve over a 409 bit binary field sect571k1 : NIST/SECG curve over a 571 bit binary field sect571r1 : NIST/SECG curve over a 571 bit binary field c2pnb163v1: X9.62 curve over a 163 bit binary field c2pnb163v2: X9.62 curve over a 163 bit binary field c2pnb163v3: X9.62 curve over a 163 bit binary field c2pnb176v1: X9.62 curve over a 176 bit binary field c2tnb191v1: X9.62 curve over a 191 bit binary field c2tnb191v2: X9.62 curve over a 191 bit binary field c2tnb191v3: X9.62 curve over a 191 bit binary field c2pnb208w1: X9.62 curve over a 208 bit binary field c2tnb239v1: X9.62 curve over a 239 bit binary field c2tnb239v2: X9.62 curve over a 239 bit binary field c2tnb239v3: X9.62 curve over a 239 bit binary field c2pnb272w1: X9.62 curve over a 272 bit binary field c2pnb304w1: X9.62 curve over a 304 bit binary field c2tnb359v1: X9.62 curve over a 359 bit binary field c2pnb368w1: X9.62 curve over a 368 bit binary field c2tnb431r1: X9.62 curve over a 431 bit binary field wap-wsg-idm-ecid-wtls1: WTLS curve over a 113 bit binary field wap-wsg-idm-ecid-wtls3: NIST/SECG/WTLS curve over a 163 bit binary field wap-wsg-idm-ecid-wtls4: SECG curve over a 113 bit binary field wap-wsg-idm-ecid-wtls5: X9.62 curve over a 163 bit binary field wap-wsg-idm-ecid-wtls6: SECG/WTLS curve over a 112 bit prime field wap-wsg-idm-ecid-wtls7: SECG/WTLS curve over a 160 bit prime field wap-wsg-idm-ecid-wtls8: WTLS curve over a 112 bit prime field wap-wsg-idm-ecid-wtls9: WTLS curve over a 160 bit prime field wap-wsg-idm-ecid-wtls10: NIST/SECG/WTLS curve over a 233 bit binary field wap-wsg-idm-ecid-wtls11: NIST/SECG/WTLS curve over a 233 bit binary field wap-wsg-idm-ecid-wtls12: WTLS curve over a 224 bit prime field Oakley-EC2N-3: IPSec/IKE/Oakley curve #3 over a 155 bit binary field. Not suitable for ECDSA. Questionable extension field! Oakley-EC2N-4: IPSec/IKE/Oakley curve #4 over a 185 bit binary field. Not suitable for ECDSA. Questionable extension field! brainpoolP160r1: RFC 5639 curve over a 160 bit prime field brainpoolP160t1: RFC 5639 curve over a 160 bit prime field brainpoolP192r1: RFC 5639 curve over a 192 bit prime field brainpoolP192t1: RFC 5639 curve over a 192 bit prime field brainpoolP224r1: RFC 5639 curve over a 224 bit prime field brainpoolP224t1: RFC 5639 curve over a 224 bit prime field brainpoolP256r1: RFC 5639 curve over a 256 bit prime field brainpoolP256t1: RFC 5639 curve over a 256 bit prime field brainpoolP320r1: RFC 5639 curve over a 320 bit prime field brainpoolP320t1: RFC 5639 curve over a 320 bit prime field brainpoolP384r1: RFC 5639 curve over a 384 bit prime field brainpoolP384t1: RFC 5639 curve over a 384 bit prime field brainpoolP512r1: RFC 5639 curve over a 512 bit prime field brainpoolP512t1: RFC 5639 curve over a 512 bit prime field SM2 : SM2 curve over a 256 bit prime field
Let's create RSA 4096 and secp384r1 key pairs for the
user bob
.
The file names are, of course, completely arbitrary.
But organized naming may make it easier to keep track
of what is in which file.
# openssl genpkey -algorithm RSA \ -pkeyopt rsa_keygen_bits:4096 \ -out bob_RSA.key ........................................................................++++ ......++++ # openssl genpkey -algorithm EC \ -pkeyopt ec_paramgen_curve:secp384r1 \ -pkeyopt ec_param_enc:named_curve \ -out bob_EC.key # ls -l bob_* -rw------- 1 cromwell cromwell 306 Jan 08 10:31 bob_EC.key -rw------- 1 cromwell cromwell 3272 Jan 08 10:31 bob_RSA.key
4: Create CSRs for the user public keys
The users have key pairs, we need to create certificates with the public keys. But in order to do that, we must first create Certificate Signing Requests or CSRs.
The Canonical Name or CN must be their username.
The file name doesn't matter but the CN does.
The pam_pkcs11.so
module enforces the rule
that the CN must match the user name.
# openssl req -new -key bob_RSA.key \ -subj "/C=US/ST=Indiana/L=West\ Lafayette/O=Cromwell\ International/OU=HQ/CN=bob/" \ -out bob_RSA.csr # openssl req -new -key bob_EC.key \ -subj "/C=US/ST=Indiana/L=West\ Lafayette/O=Cromwell\ International/OU=HQ/CN=bob/" \ -out bob_EC.csr # ls -l bob_* -rw-rw-r-- 1 cromwell cromwell 570 Jan 08 10:31 bob_EC.csr -rw------- 1 cromwell cromwell 306 Jan 08 10:31 bob_EC.key -rw-rw-r-- 1 cromwell cromwell 1716 Jan 08 10:31 bob_RSA.csr -rw------- 1 cromwell cromwell 3272 Jan 08 10:31 bob_RSA.key
5: Create certificates for the user public keys
Now we can use those CSRs to generate the certificates. Here I'm using the same CA RSA key to sign both of the user's RSA and ECC public keys.
# openssl x509 -req -in bob_RSA.csr \ -CA RootCA_RSA.pem -CAkey RootCA_RSA.key \ -CAcreateserial -days 365 -out bob_RSA.crt Signature ok subject=C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = bob Getting CA Private Key Enter pass phrase for RootCA_RSA.key: ØØØØØØØØØØØØØØØ # openssl x509 -req -in bob_EC.csr \ -CA RootCA_RSA.pem -CAkey RootCA_RSA.key \ -CAcreateserial -days 365 -out bob_EC.crt Signature ok subject=C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = bob Getting CA Private Key Enter pass phrase for RootCA_RSA.key: ØØØØØØØØØØØØØØØ # ls -l bob_* -rw-rw-r-- 1 cromwell cromwell 1042 Jan 08 10:31 bob_EC.crt -rw-rw-r-- 1 cromwell cromwell 570 Jan 08 10:31 bob_EC.csr -rw------- 1 cromwell cromwell 306 Jan 08 10:31 bob_EC.key -rw-rw-r-- 1 cromwell cromwell 1623 Jan 08 10:31 bob_RSA.crt -rw-rw-r-- 1 cromwell cromwell 1716 Jan 08 10:31 bob_RSA.csr -rw------- 1 cromwell cromwell 3272 Jan 08 10:31 bob_RSA.key
Again, you can examine the results. I have highlighted some critical parts.
# openssl x509 -in bob_RSA.crt -text -noout | less Certificate: Data: Version: 1 (0x0) Serial Number: 4e:bf:dd:b3:6f:d5:e5:0d:47:44:02:71:1b:68:f2:7f:3e:c4:56:d4 Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Validity Not Before: Jan 8 10:31:33 2025 GMT Not After : Jan 8 10:31:33 2026 GMT Subject: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = bob Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) Modulus: 00:9a:52:cc:bb:e0:89:cc:fe:68:ac:76:a3:5d:84: a6:3c:52:e6:40:77:f8:43:2e:ce:b9:3d:14:4d:0a: 64:27:0f:c9:4d:15:d6:32:de:e5:7d:2c:03:b4:83: da:29:e3:17:05:49:a3:98:81:bd:e5:35:5d:d2:1c: d5:c6:12:16:87:ff:7e:d1:a6:03:12:a7:c1:3d:5a: 2a:44:3e:eb:fb:42:6d:da:8f:ce:d2:d9:c2:2a:1b: 34:ce:e9:6d:db:34:5e:32:82:48:87:56:33:ef:0c: 4f:98:67:53:60:77:3f:3d:26:9f:0f:f1:7f:ca:22: 47:17:7b:de:9a:3b:98:b1:ac:85:6d:b0:ee:75:b9: 45:4f:3e:86:2e:8a:18:81:0e:d0:00:d0:e0:c0:8c: 13:6c:1f:b0:f8:18:e3:0b:b2:21:bd:1c:33:90:64: ce:d6:7f:cc:ab:a4:5d:08:83:67:5d:06:db:52:eb: 86:c2:66:df:12:d6:04:c0:07:41:6e:c5:1b:df:39: 4b:dc:48:ad:43:29:4b:ef:f5:dd:c7:c6:79:25:d4: 97:34:b9:38:57:18:e0:70:fd:80:5f:64:66:20:51: 33:08:09:a9:34:ed:cb:a6:4e:3c:88:b6:51:51:2b: 9d:7c:5b:3c:5f:21:56:46:62:72:95:0b:17:29:79: 3f:d2:22:23:60:3c:c1:8f:a8:a0:3e:f3:ae:12:81: c5:d7:14:2a:23:8c:39:63:5a:a1:b1:dc:83:64:20: ac:f3:20:24:74:23:26:bf:35:d7:5c:ef:bb:f5:11: 12:97:74:95:0d:5f:ea:fd:74:a6:f9:4c:2b:20:d7: 1f:50:9e:af:2d:0b:78:1e:d6:97:46:69:46:13:06: fc:7e:36:36:5c:84:83:69:db:45:fd:1f:a2:ef:cd: c1:73:dd:15:c1:95:c2:1a:c6:c3:b6:44:af:20:7b: 9c:3a:2f:0c:eb:c8:2e:98:8a:a5:6f:5f:1e:d3:42: a2:70:e2:8e:68:af:e5:fd:c8:2a:5f:1c:79:8e:8a: 67:69:8d:4f:87:2e:d1:aa:da:3d:88:4f:f7:bb:7b: 66:25:fa:a3:ae:d7:33:e6:fa:f1:61:05:60:d2:a8: ec:1b:8d:28:7d:f4:3f:aa:19:d8:ce:29:a4:ca:2c: 25:e6:1a:13:04:e8:8e:38:a2:88:de:e2:1b:dd:91: 1a:92:e9:8d:45:64:b5:2c:9d:ca:ab:3c:69:74:31: fc:7f:8b:97:97:99:d8:26:3c:da:1c:1e:0b:ba:97: 51:09:34:fb:a4:b1:cc:81:c7:2f:9e:de:f7:3c:65: 56:42:88:c2:43:d7:b1:a0:72:7e:99:73:63:ec:f2: d5:f2:05 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 6b:e6:3b:d6:3a:78:11:fd:13:44:70:bc:e2:3d:f4:60:86:1e: 2a:8c:2c:56:10:a4:4f:ff:39:0f:8d:38:dc:5f:66:6a:32:ca: 84:e0:20:6b:2f:a4:1e:9e:37:64:c2:9e:c8:65:a7:4d:d4:10: d6:ea:8c:16:fe:91:f5:d9:c2:ff:05:95:e5:7a:84:2c:48:04: de:60:dd:60:12:72:92:b2:4c:f2:01:5b:8b:3d:38:57:fd:5d: cd:d4:2d:38:9e:37:5c:8a:8a:40:42:4c:0d:f6:97:33:90:55: 1a:46:28:f6:c3:59:3b:fa:dd:0b:71:0f:19:ed:0f:9b:8b:2e: f0:a2:e5:5e:cd:de:28:29:82:1f:ff:68:e9:af:67:39:b5:f7: 30:85:da:26:6c:2e:1e:d3:d0:7e:f2:d0:0e:23:49:01:ef:4f: ac:a4:fb:48:f0:8b:3d:6e:e8:ad:4b:56:31:85:d9:8b:39:a8: 9f:d7:fe:18:48:6a:53:e4:50:0b:62:8c:2c:ef:c4:5a:45:08: 4d:2b:49:dd:1c:6e:9c:01:bf:2f:d0:99:94:74:bd:26:e0:c4: a6:59:aa:a0:52:ba:2a:ec:24:2f:7a:36:4c:e4:39:b4:3c:f4: 07:9e:87:32:c6:a5:8b:1d:52:73:cc:45:1b:ce:ee:af:1a:c4: 44:b7:f6:c1 # openssl x509 -in bob_EC.crt -text -noout | less Certificate: Data: Version: 1 (0x0) Serial Number: 4e:bf:dd:b3:6f:d5:e5:0d:47:44:02:71:1b:68:f2:7f:3e:c4:56:d5 Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = pki.cromwell-intl.com Validity Not Before: Jan 8 10:31:33 2025 GMT Not After : Jan 8 10:31:33 2026 GMT Subject: C = US, ST = Indiana, L = West Lafayette, O = Cromwell International, OU = HQ, CN = bob Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (384 bit) pub: 04:2c:e4:bc:27:a7:7b:9b:7f:f9:6b:b0:b1:db:fe: 8a:d3:7e:bc:a4:63:8c:55:94:6b:e8:e5:b8:13:4e: ef:00:86:d7:41:9a:d2:6d:b6:b6:3c:8e:e8:c7:6a: 80:1e:ed:08:c7:ad:8a:9a:23:7b:22:a3:6f:ef:7d: 93:c0:39:57:7a:f8:b7:8b:33:d5:9c:cb:73:0b:12: de:3f:6d:5e:79:37:e8:c0:fa:87:b8:4a:18:4d:08: 82:f8:71:85:27:0d:b5 ASN1 OID: secp384r1 NIST CURVE: P-384 Signature Algorithm: sha256WithRSAEncryption 02:7c:96:37:6d:24:17:e4:20:ce:78:ed:a8:f9:c0:82:af:4d: b1:7c:f9:a7:24:46:7f:88:2b:04:b6:f6:9e:78:36:1c:c3:88: 1a:6e:ca:83:79:09:95:d1:94:bf:4d:9f:2a:57:84:11:8e:52: e4:40:5b:84:1f:16:34:86:80:3d:7a:15:3c:86:35:91:37:96: b3:b2:2b:49:7d:76:50:d5:c7:bf:ab:d0:f3:2e:fe:fc:92:58: e2:3f:bd:bf:27:69:6a:23:11:7e:cb:30:79:00:d8:ee:d7:a6: 46:22:48:cc:93:3c:55:3d:67:bf:bf:71:df:a8:4a:1a:b2:01: a5:96:52:78:2c:c9:d0:eb:ba:73:e4:12:b5:77:bc:58:c4:8d: 12:e8:0e:9f:c6:00:e1:86:02:8b:6e:c3:37:c5:1e:e7:31:bb: f7:d5:89:19:fb:b8:4e:c0:6c:10:bc:c0:ae:56:fb:35:75:6d: 7a:d9:f5:b8:55:c4:0a:49:f4:29:ae:04:2b:19:1d:e8:67:fa: 97:0a:c7:35:c3:5c:79:78:5a:0c:25:89:54:0a:14:23:11:59: 9d:d8:f7:0c:a4:e9:45:27:d2:2f:71:8d:d0:b8:b6:57:7c:04: 66:4f:29:cc:76:22:f0:27:23:b8:18:61:d9:1f:84:37:57:7b: 6f:12:14:2a
6: Create PFX / PKCS #12 files for the user identities
The PFX or PKCS #12 file contains both the user's private key and their public key within the certificate just created by the CA.
We do an "export" operation here to create the file, which we later "import" into the YubiKey. You will be asked for a "Export Password", you can enter the same string twice to protect the PFX file and the user's private key which it contains. Or if you simply press Enter twice, there will be no such password.
# openssl pkcs12 -export \ -in bob_RSA.crt \ -inkey bob_RSA.key \ -out bob_RSA.pfx Enter Export Password: ØØØØØØØØØØØØ Verifying - Enter Export Password: ØØØØØØØØØØØØ # openssl pkcs12 -export \ -in bob_EC.crt \ -inkey bob_EC.key \ -out bob_EC.pfx Enter Export Password: ØØØØØØØØØØØØ Verifying - Enter Export Password: ØØØØØØØØØØØØ # ls -l bob_* -rw-rw-r-- 1 cromwell cromwell 1042 Jan 08 10:31 bob_EC.crt -rw-rw-r-- 1 cromwell cromwell 570 Jan 08 10:31 bob_EC.csr -rw------- 1 cromwell cromwell 306 Jan 08 10:31 bob_EC.key -rw------- 1 cromwell cromwell 1298 Jan 08 10:31 bob_EC.pfx -rw-rw-r-- 1 cromwell cromwell 1623 Jan 08 10:31 bob_RSA.crt -rw-rw-r-- 1 cromwell cromwell 1716 Jan 08 10:31 bob_RSA.csr -rw------- 1 cromwell cromwell 3272 Jan 08 10:31 bob_RSA.key -rw------- 1 cromwell cromwell 3909 Jan 08 10:31 bob_RSA.pfx
And now we're done with the openssl
commands!
7: Load PFX files into YubiKey devices
We select one of the user's PFX files and load it into slot 9A of the YubiKey.
First, let's make sure the YubiKey is detected.
# lsusb Bus 001 Device 002: ID 1050:0407 Yubico.com Yubikey 4/5 OTP+U2F+CCID Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Now we'll see if there's anything already loaded into the device.
We will use the ykman
command from the
yubikey-manager
package.
It does not come with a manual.
Yubico has the
missing ykman
manual.
You can run ykman -h
to see a list of applications.
Then ykman piv -h
for a list of piv
commands.
Then ykman piv import-certificate -h
for details on the import-certificate
command,
and so on.
Let's see what, if anything, is stored in the PIV slots:
# ykman piv info PIV version: 4.3.7 PIN tries remaining: 3 CHUID: 3019d4e739da739ced39ce739d836858210842108421c84210c3eb34109f8401173d5f226c14c650c5efa2731f350832303330303130313e00fe00 CCC: f015a000000116ff0217b5e9fa5cc2252ff4070b8ac930f10121f20121f300f40100f50110f600f700fa00fb00fc00fd00fe00
If there was something shown in PIV slot 9A, we could delete it. However, unmentioned means empty. This device is ready for us to load a certificate. Let's do that, and then check what happened.
# ykman piv import-certificate 9a bob_EC.pfx
Enter a management key [blank to use default key]:
Enter password to decrypt certificate: ØØØØØØØØØØØØ
# ykman piv info
PIV version: 4.3.7
PIN tries remaining: 3
CHUID: 3019d4e739da739ced39ce739d836858210842108421c84210c3eb3410358a9d4cb4d43e2606ed79d43b6bcd9e350832303330303130313e00fe00
CCC: f015a000000116ff0217b5e9fa5cc2252ff4070b8ac930f10121f20121f300f40100f50110f600f700fa00fb00fc00fd00fe00
Slot 9a:
Algorithm: ECCP384
Subject CN: bob
Issuer CN: pki.cromwell-intl.com
Serial: 449580035331568342259526991763565597446497392341
Fingerprint: abfd843520daf2bba712e9b180f8769c521edabb8fb8ddffd5ed9719e0d0cc60
Not before: Jan 8 10:31:33 2025 GMT
Not after: Jan 8 10:31:33 2026 GMT
The YubiKey has interpreted the certificate "wrapper",
the X.509 data structure, and found
the subject's key algorithm,
the subject CN, the issuer CN (the CA),
and dates of inception and expiration.
It outputs the serial number in base 10, not the base 16
used by openssl
.
The YubiKey reports an ECCP384 (P-384 or secp384r1)
elliptic curve key, although it can't use that key.
8: Configure mappings between user names and CN strings
Now we get into Linux server administration.
We need to create /etc/pam_pkcs11/cn_map
to tell the pam_pkcs11.so
PAM module how to
map from certificate CN field to Linux user name.
The user name and CN string don't have to be identical. But if they aren't, it will be very difficult to keep track of how your CN strings and user names match up.
We'll use the "minimum UID of an ordinary user" concept
defined in /etc/login.defs
to decide whether
to configure the user.
If you want to include root
,
then you'll manually add a line after running this script:
#!/bin/bash UID_MIN=$(awk '/\<UID_MIN\>/ {print $2}' /etc/login.defs) rm -f /etc/pam_pkcs11/cn_map for login in $( awk -F: -v u=$UID_MIN '$3 >= u {print $1}' /etc/passwd ) do echo " adding $login" echo "$login -> $login" >> /etc/pam_pkcs11/cn_map done
9: Store the CA certificates in system databases
We will use the certutil
command,
see its manual page for details.
# certutil -A -d dbm:/etc/pki/nssdb -n "Linux Root CA RSA" -t "CT,C,C" -i RootCA_RSA.pem # certutil -A -d dbm:/etc/pki/nssdb -n "Linux Root CA EC" -t "CT,C,C" -i RootCA_EC.pem # certutil -A -d sql:/etc/pki/nssdb -n "Linux Root CA RSA" -t "CT,C,C" -i RootCA_RSA.pem # certutil -A -d sql:/etc/pki/nssdb -n "Linux Root CA EC" -t "CT,C,C" -i RootCA_EC.pem
-A |
Add a certificate to a database. |
-d location |
Use this database. Specifying it as sql:directory
uses the newer SQLite database in
the files cert9.db ,
key4.db , and
pkcs11.txt .Specifying it as dbm:directory
uses the legacy database in
the files cert8.db ,
key3.db , and
secmod.db .SQLite is better for reporting, but some authentication tasks need the legacy database. |
-n nickname |
Assign this nickname to the certificate. |
-t attributes |
Specify the trust attributes. |
-i filename |
Read the certificate from this file. |
Verify that the certificates are in the database, we can find them by nickname, and we can validate them.
# certutil -L -d sql:/etc/pki/nssdb Certificate Nickname Trust Attributes SSL,S/MIME,JAR/XPI Linux Root CA RSA CT,C,C Linux Root CA EC CT,C,C # certutil -O -d sql:/etc/pki/nssdb -n "Linux Root CA RSA" "Linux Root CA RSA" [CN=factory.mfew.army.mil,OU=HQ,O=Cromwell Intl,L=West Lafayette,ST=Indiana,C=US] # certutil -V -d sql:/etc/pki/nssdb -n "Linux Root CA RSA" -u A certutil: certificate is valid # certutil -O -d sql:/etc/pki/nssdb -n "Linux Root CA EC" "Linux Root CA EC" [CN=factory.mfew.army.mil,OU=HQ,O=Cromwell Intl,L=West Lafayette,ST=Indiana,C=US] # certutil -V -d sql:/etc/pki/nssdb -n "Linux Root CA EC" -u A certutil: certificate is valid
That much should be adequate, but just in case:
# mkdir /etc/pam_pkcs11/cacerts # cp -a RootCA_*pem /etc/pam_pkcs11/cacerts # ./cacertdir_rehash /etc/pam_pkcs11/cacerts ‘fc8c95cc.0’ -> ‘RootCA_EC.pem’ ‘fc8c95cc.1’ -> ‘RootCA_RSA.pem’ # ls -l /etc/pam_pkcs11/cacerts/ total 8 lrwxrwxrwx. 1 root root 13 Mar 26 15:04 fc8c95cc.0 -> RootCA_EC.pem lrwxrwxrwx. 1 root root 14 Mar 26 15:04 fc8c95cc.1 -> RootCA_RSA.pem -r--r--r--. 1 root root 843 Jun 6 2020 RootCA_EC.pem -r--r--r--. 1 root root 1379 Jun 6 2020 RootCA_RSA.pem
The functional part of the cacertdir_rehash
script looks like this:
#!/bin/bash # Actual script starts with tests: called with one # parameter, the name of an existing directory. cd $1 # Remove existing symbolic links. find * -type l -exec rm {} \; for i in * do if [ -r $i -a ! -d $i ] then hash=$(openssl x509 -hash -noout -in $i 2> /dev/null) if [ -z "$hash" ] then continue fi suffix=0 while [ -e $hash.$suffix ] do suffix=$((suffix + 1)) done ln -sfv $i $hash.$suffix fi done
The file /etc/pam_pkcs11/pam_pkcs11.conf
sets a cert_policy
in three stanzas:
[... lines deleted ...] cert_policy = ca, certificate; [... lines deleted ...] cert_policy = ca, certificate; [... lines deleted ...] cert_policy = ca, certificate; [... lines deleted ...]
Option ca
verifies the validity of the
certificate in the device, as belonging to the user
and signed by the trusted Root CA.
source code pam_pkcs11.c
documentation
Option certificate
attempts to digitally
sign a random string using the user's private key
embedded in the *.pfx file stored in the device.
Reading the debug output (shown toward the bottom of this
page) and following the source code, the ca
verification of the certificate clearly succeeds first,
and then it's a failure to create a new signature
of a random string.
As we're already doing two-factor authentication,
I haven't pursued this further.
I simply remove the certificate
option.
Meanwhile the US DoD STIG requires the ocsp_on
option.
So:
# sed -i 's/cert_policy =.*/cert_policy = ca, ocsp_on/' /etc/pam_pkcs11/pam_pkcs11.conf # grep cert_policy /etc/pam_pkcs11/pam_pkcs11.conf cert_policy = ca, ocsp_on cert_policy = ca, ocsp_on cert_policy = ca, ocsp_on
While we must not use the authconfig
or
authconfig-gtk
program, in order to keep
things in sync we should also:
# sed -i 's/SMARTCARD=no/SMARTCARD=yes/' /etc/sysconfig/authconfig
Let's make sure that the SELinux security labeling is OK:
# restorecon -r -v /etc
Finally, we need to make sure the pcscd
daemon
is running, to detect that the YubiKey has been plugged in.
# systemctl enable pcscd # systemctl restart pcscd
10: Configure PAM to authenticate users with pam_pkcs11.so
On RHEL and Oracle Linux and their derivatives,
the file /etc/pam.d/system-auth
is included
in many PAM service files.
On Oracle Linux and RHEL 7 and 8 it contains the following:
#%PAM-1.0 # This file is auto-generated. # User changes will be destroyed the next time authselect is run. auth required pam_env.so auth sufficient pam_unix.so try_first_pass nullok auth required pam_deny.so account required pam_unix.so password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= password sufficient pam_unix.so try_first_pass use_authtok nullok sha512 shadow password required pam_deny.so session optional pam_keyinit.so revoke session required pam_limits.so -session optional pam_systemd.so session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid session required pam_unix.so
To require both YubiKey and password, simply
insert a line.
Add the word debug
to the end of the line
to see a lot of debug output.
Once you have things working, you will want to remove
the debug
option.
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authselect is run.
auth required pam_env.so
auth required pam_pkcs11.so wait_for_card debug
auth sufficient pam_unix.so try_first_pass nullok
auth required pam_deny.so
account required pam_unix.so
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so try_first_pass use_authtok nullok sha512 shadow
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
-session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
To require the YubiKey only for text console login,
graphical display manager login, and various screensavers,
insert a pam_succeed_if.so
line.
The success=N
means
"If this succeeds, skip over the next N rules."
If you want to require the YubiKey for
su
,
sudo
,
or other PAM-aware services, just add them to the list.
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authselect is run.
auth required pam_env.so
auth [success=1 default=ignore] pam_succeed_if.so service notin login:gdm:xdm:kdm:xscreensaver:gnome-screensaver:kscreensaver quiet use_uid
auth required pam_pkcs11.so wait_for_card debug
auth sufficient pam_unix.so try_first_pass nullok
auth required pam_deny.so
account required pam_unix.so
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so try_first_pass use_authtok nullok sha512 shadow
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
-session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
On Debian and Ubuntu, you will do similar things in the
/etc/pam.d/common-auth
file.
and Compliance
See my page on
compliance through Linux PAM
to see what else you must add with
pam_faillock.so
,
pam_lastlog.so
,
pam_pwhistory.so
,
pam_pwquality.so
, and
pam_sss.so
to meet the U.S. DoD STIG requirements.
Example output
With the debug
option on the
pam_pkcs11.so
module
I see the following on the screen.
Red Hat Enterprise Linux Server 7 Kernel 3.10.0 on an x86_64 localhost login: bob DEBUG:pam_config.c:230: Using config file /etc/pam_pkcs11/pam_pkcs11.conf DEBUG:pkcs11_lib.c:182: Initializing NSS ... DEBUG:pkcs11_lib.c:192: Initializing NSS ... database=/etc/pki/nssdb DEBUG:pkcs11_lib.c:212: ... NSS Complete DEBUG:pam_pkcs11.c:272: Is it a screen saver? DEBUG:pam_pkcs11.c:287: explicit username = [bob] DEBUG:pam_pkcs11.c:315: loading pkcs #11 module... DEBUG:pkcs11_lib.c:237: Looking up module in list DEBUG:pkcs11_lib.c:240: modlist = 0x2384ef0 next = 0x238f260 DEBUG:pkcs11_lib.c:241: dllName= <null> DEBUG:pkcs11_lib.c:240: modlist= 0x238f260 next = 0x0 DEBUG:pkcs11_lib.c:241: dllName= libcoolkeypk11.so DEBUG:pam_pkcs11.c:324: Initializing pkcs #11 module... Found the Smart card. Welcome bob! Smart card PIN: ###### DEBUG:pkcs11_lib.c:761: cert 0: found (bob:PIV ID Certificate), "CN=bob,OU=HQ,O=Cromwell International,L=West Lafayette,ST=Indiana,C=US" DEBUG:mapper_mgr.c:172: Retrieveing mapper module list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'cn' DEBUG:mapper_mgr.c:197: Inserting mapper [cn] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'uid' DEBUG:mapper_mgr.c:197: Inserting mapper [uid] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'pwent' DEBUG:mapper_mgr.c:197: Inserting mapper [pwent] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'null' DEBUG:mapper_mgr.c:197: Inserting mapper [null] into list DEBUG:pam_pkcs11.c:494: verifying the certificate #1 DEBUG:cert_vfy.c:34: Verifying Cert: bob:PIV ID Certificate (CN=bob,OU=HQ,O=Cromwell International,L=West Lafayette,ST=Indiana,C=US) DEBUG:mapper_mgr.c:300: Mapper module cn match() returns 1 DEBUG:pam_pkcs11.c:547: certificate is valid and matches the user DEBUG:pam_pkcs11.c:610: Skipping signature check DEBUG:pam_pkcs11.c:679: Releasing pkcs #11 module... DEBUG:pam_pkcs11.c:702: authentication succeeded Password: ØØØØØØØØØØØØØØØ DEBUG:pam_pkcs11.c:695:pam_sm_setcred() called Last login: Wed Jan 08 10:31:33 UTC 2025 on tty3 [bob@localhost ~]$
If I had left the cert_policy
field
in /etc/pam_pkcs11/pam_pkcs11.conf
reading:
cert_policy = ca, signature
then I instead get the following output.
Bold lines are different.
Red Hat Enterprise Linux Server 7 Kernel 3.10.0 on an x86_64 localhost login: bob DEBUG:pam_config.c:230: Using config file /etc/pam_pkcs11/pam_pkcs11.conf DEBUG:pkcs11_lib.c:182: Initializing NSS ... DEBUG:pkcs11_lib.c:192: Initializing NSS ... database=/etc/pki/nssdb DEBUG:pkcs11_lib.c:212: ... NSS Complete DEBUG:pam_pkcs11.c:272: Is it a screen saver? DEBUG:pam_pkcs11.c:287: explicit username = [bob] DEBUG:pam_pkcs11.c:315: loading pkcs #11 module... DEBUG:pkcs11_lib.c:237: Looking up module in list DEBUG:pkcs11_lib.c:240: modlist = 0x2384ef0 next = 0x238f260 DEBUG:pkcs11_lib.c:241: dllName= <null> DEBUG:pkcs11_lib.c:240: modlist= 0x238f260 next = 0x0 DEBUG:pkcs11_lib.c:241: dllName= libcoolkeypk11.so DEBUG:pam_pkcs11.c:324: Initializing pkcs #11 module... Found the Smart card. Welcome bob! Smart card PIN: ###### DEBUG:pkcs11_lib.c:761: cert 0: found (bob:PIV ID Certificate), "CN=bob,OU=HQ,O=Cromwell International,L=West Lafayette,ST=Indiana,C=US" DEBUG:mapper_mgr.c:172: Retrieveing mapper module list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'cn' DEBUG:mapper_mgr.c:197: Inserting mapper [cn] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'uid' DEBUG:mapper_mgr.c:197: Inserting mapper [uid] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'pwent' DEBUG:mapper_mgr.c:197: Inserting mapper [pwent] into list DEBUG:mapper_mgr.c:73: Loading static module for mapper 'null' DEBUG:mapper_mgr.c:197: Inserting mapper [null] into list DEBUG:pam_pkcs11.c:494: verifying the certificate #1 DEBUG:cert_vfy.c:34: Verifying Cert: bob:PIV ID Certificate (CN=bob,OU=HQ,O=Cromwell International,L=West Lafayette,ST=Indiana,C=US) DEBUG:mapper_mgr.c:300: Mapper module cn match() returns 1 DEBUG:pam_pkcs11.c:547: certificate is valid and matches the user DEBUG:pam_pkcs11.c:831: Signature failed: (null) ERROR:pam_pkcs11.c:589: sign_value() failed: DEBUG:mapper_mgr.c:214: unloading mapper module list DEBUG:mapper_mgr.c:137: calling mapper_module_end() cn DEBUG:mapper_mgr.c:148: Module cn is static: don't remove DEBUG:mapper_mgr.c:137: calling mapper_module_end() uid DEBUG:mapper_mgr.c:148: Module uid is static: don't remove DEBUG:mapper_mgr.c:137: calling mapper_module_end() pwent DEBUG:mapper_mgr.c:148: Module pwent is static: don't remove DEBUG:mapper_mgr.c:137: calling mapper_module_end() null DEBUG:mapper_mgr.c:148: Module null is static: don't remove Login incorrect localhost login:
ykman
is fussy about locale settings. If you setLC_ALL=C
so that commands likels
won't ignore case when sorting words,ykman
won't like that. My solution is to setLC_ALL=en_US.utf8
orLC_ALL=en_CA.utf8
orLC_ALL=fr_CA.utf8
or whatever is appropriate. Then make an alias forls
in your~/.bashrc
file:alias ls='LC_ALL=C ls --color==auto'