Dummy intro to smart card authentication
Smart card authentication is just like authentication with certificates and private keys (X509, PKI).
The difference is that instead of fetching your private key and certificate from the disk, you let
smart card do the cryptographic operations for you and private key never leaves the card. Special
protocols are used on the client to allow communication between browser and the smart card.
Setting things can be difficult
Smart card authentication over HTTPS may be challenging thing to deploy. Especially, for
newcomers. At one step You have to set-up all the components properly.
- First of all, you need to prepare your smart card, PIV, and make sure proper X509 key and certificate is present.
- Then You need to configure your browser and whole client stack with nss, opensc, pcscd, p11-kit-proxy, etc).
- Finally, your server needs to be set-up properly to solicit and validate inserted card.
When the you make even one mistake with one of the components the authentication will not
work. And there will be very little debugging steps available. What You need to do is to
test each component alone to identify which component is not set-up properly.
Good idea is to start debug client parts (the card & the browser).
GnuTLS test server with stock CA & certificates
GnuTLS upstream provides minimalistic HTTPS server that logs detailed
debugging information about what the client system is sending over. You can either compile the gnutls from
sources or you can start in no time with single-purpose container
podman run -it \
-p "5556:5556" \
Upon this command, you will have HTTPS server running on localhost on port 5556, point your browser
You will be presented with browser warning about unknown certificate. This is expected. The server running
in the container does not contain certificate signed by any of the well known certificate authorities. You
are safe to accept the risk and continue.
After accepting the risk, your connection will fail.
You can now review the server logs in the podman container.
* Received alert '46': Unknown certificate.
Error in handshake: A TLS fatal alert has been received.
The failure here is caused by the fact that client and server cannot build shared trust.
Our server never saw your smart card in action and thus it should not admit the user in.
To fix the above error we either need the smart card certificate to be signed. And CA of
the signature needs to be known by our server.
GnuTLS test server with particular CA and stock certificates
In an easy case, where you already have your smartcard signed and CA certificate is available
to You. You can restart the gnutls container with bindmouted CA to proper location. Following
command assumes the CA certificate in
podman run -it -p "5556:5556" \
--mount type=bind,source=ca.crt.pem,target=/gnutls/doc/credentials/x509/ca.pem \
GnuTLS test server with custom CA and custom certificates
Getting smart card signed
In case you don’t have smart card signed and do not have access to the CA. You can generate your own CA
and get your smartcard secrets signed by that. There are two ways to achieve this, either you generate keys
outside of smart cards and upload it to the card, or you let card generate the keys and CSR (Certificate
Signing Request) and after your CA signs the CSR you upload resulting certificate to the card. The latter
option is preferential as private key never leaves the card.
Create your own CA
mkdir newcerts certs crl private requests
echo 1000 > serial
# Download dummy config for your testing CA
# Generate private key
openssl ecparam -genkey -name secp384r1 \
| openssl ec -aes256 -out private/ca.key.pem
# Generate certificate for the private key
openssl req -config openssl_root.cnf -new -x509 -sha384 \
-extensions v3_ca -key private/ca.key.pem -out certs/ca.crt.pem
# Check created certificate
openssl x509 -noout -text -in certs/ca.crt.pem
Generate CSR on the card
Consult your smart card manual on how to generate Certificate Signing Request.
Sign the CSR by Your CA
# verify CSR is well formed
openssl req -verify -in CSRfromCard.csr -text -noout
# sign CSR
openssl x509 -req -days 360 -in CSRfromCard.csr \
-CA certs/ca.crt.pem -CAkey private/ca.key.pem
-CAcreateserial -out signedCardCert.crt
# checked singned certificate
openssl x509 -text -noout -in signedCardCert.crt
Upload the signed certificate to your smart card
Consult your smart card manual on how to upload the certificate.
Make sure to not remove your private key that remains still on the smart card.
Create your own server certificates
Now, when we have client keys in place. We can generate keys for server as well.
openssl req -x509 -newkey rsa:2048 -days 365 -nodes \
-keyout serverPrivate.key \
-out serverCert.pem \
GnuTLS test server with custom CA & certificates
podman run -it -p "5556:5556" \
--mount type=bind,source=$(pwd),target=/certs \
../../src/gnutls-serv -d 4 --require-client-cert \
--x509cafile /certs/ca/certs/ca.crt.pem \
--x509certfile /certs/serverCert.pem \
Now, when you visit yours https://127.0.0.1:5556/ you will be asked for the PIV pin for your smart card.
Upon correct PIV pin entry, smart card will offer keys(s) on the card for you to select appropriate key
for this test server.
Finally success page will show up.
Kudos go to Jakub Jelen who hinted GnuTLS server to me. Thank You Jakube!