Getting a card to work.
No card detected · browser detecting…
| Subject | |
|---|---|
| Issuer | |
| Verified | NONE |
| Serial | |
Empty means the browser offered nothing at all, which is the same answer whether there is no reader, an empty reader, or a prompt that was dismissed - nothing here can tell those apart. Work through the steps below. Verification is chain-only; revocation is deliberately not checked, because the card points at revocation services this environment cannot reach and turning it on would fail every login for the wrong reason.
sudo apt install -y opensc pcscd pcsc-tools libnss3-tools
sudo systemctl enable --now pcscd
sudo dnf install -y opensc pcsc-lite pcsc-tools nss-tools
sudo systemctl enable --now pcscd
Check the card is readable before blaming the browser:
opensc-tool --list-readers
opensc-tool --name # e.g. Gemalto IDPrime 940
pkcs15-tool --list-certificates
Run these in your local desktop session. Over SSH you will get
SCardEstablishContext: Access denied and the module will report
“no slots attached” — pcscd only serves the active local session, so a remote
shell makes a perfectly good reader look broken.
Firefox keeps its own PKCS#11 store per profile:
modutil -dbdir sql:$HOME/.mozilla/firefox/*.default* \
-add "OpenSC" -libfile /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
modutil -dbdir sql:$HOME/.mozilla/firefox/*.default* \
-add "OpenSC" -libfile /usr/lib64/opensc-pkcs11.so
Two things bite here.
SEC_ERROR_BAD_DATABASE usually means the profile does not exist yet —
the *.default* glob matched nothing. Start Firefox once, close it, retry.
And if Firefox came from snap its profile lives under
~/snap/firefox/common/.mozilla/firefox/ — but it still will not work,
because a confined snap cannot reach pcscd at all. Use the .deb from
Mozilla's apt repository, or use Chrome.
Chrome / Chromium has no “load module” dialog on Linux. It reads the shared NSS database in your home directory, so the module goes there:
# check first - if OpenSC is already listed, skip the add
modutil -dbdir sql:$HOME/.pki/nssdb -list
# first time only, if the database does not exist yet
mkdir -p $HOME/.pki/nssdb && certutil -d sql:$HOME/.pki/nssdb -N --empty-password
modutil -dbdir sql:$HOME/.pki/nssdb -add "OpenSC" \
-libfile /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
# download the CA before importing it - certutil reads a local file
curl -k -o /tmp/red-mock-root.crt https://51-15-197-131.sslip.io/red-mock-root.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "red mock root" -i /tmp/red-mock-root.crt
modutil -dbdir sql:$HOME/.pki/nssdb -list # already there? skip the add
mkdir -p $HOME/.pki/nssdb && certutil -d sql:$HOME/.pki/nssdb -N --empty-password
modutil -dbdir sql:$HOME/.pki/nssdb -add "OpenSC" \
-libfile /usr/lib64/opensc-pkcs11.so
curl -k -o /tmp/red-mock-root.crt https://51-15-197-131.sslip.io/red-mock-root.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "red mock root" -i /tmp/red-mock-root.crt
Verify with modutil -dbdir sql:$HOME/.pki/nssdb -list. Seeing
“Failed to add module … Failure to load dynamic library” while the module is
nonetheless listed as status: loaded means it was already added — check the
list before assuming it failed. Chrome must be fully closed
(chrome://restart) to pick it up, and snap/flatpak Chrome hits the same
pcscd confinement as the Firefox snap.
If certutil asks for a Password or Pin for “NSS Certificate DB” and
you never set one, press Enter — it is empty by default. Typing a value there
sets a database password, and Chrome will prompt for it from then on.
Undo with certutil -d sql:$HOME/.pki/nssdb -W and leave the new password
blank.
Everything can be correct — reader detected, module loaded, certificates visible to
certutil -L -h all — and the browser still sends nothing, because choosing a
certificate is a separate decision from being able to see one. Chrome can be told to pick
it automatically for this site:
sudo mkdir -p /etc/opt/chrome/policies/managed
sudo tee /etc/opt/chrome/policies/managed/red-cardauth.json >/dev/null <<'JSON'
{
"AutoSelectCertificateForUrls": [
"{\"pattern\":\"https://51-15-197-131.sslip.io\",\"filter\":{\"ISSUER\":{\"CN\":\"Swedish Public Sector Person 2 CA v1\"}}}"
]
}
JSON
Chromium reads the same file under /etc/chromium/policies/managed/. Confirm it
loaded at chrome://policy, then fully restart the browser. The filter matches on
the card issuer, so the signing certificate on the same card is not offered by mistake.
Start the browser from your desktop, not over SSH and not from a systemd
unit you launched remotely. A browser started outside the graphical session inherits
XDG_SESSION_TYPE=tty, and while pcscd may still answer, the certificate prompt
has nowhere to appear — so it silently sends nothing.
This site's certificate is issued by our own authority. Download
the root certificate — Chrome users already imported it
with the certutil line above; in Firefox use
Settings → Privacy & Security → Certificates → View Certificates → Authorities
and tick “trust to identify websites”. The same root signs the object store, so importing
it once quiets both.
Then reload this page. The browser will ask which certificate to send — pick the one whose usage includes client authentication; a card normally also carries a separate signing certificate, which will not work here.