A smart card that already holds the right key still has to be told which object on it to use. OpenConnect does not want a copied .key file in that situation; it wants a PKCS#11 URI that names the certificate and, when the automatic search cannot finish the job, the private key beside it. The awkward part is that a syntactically valid URI proves nothing. It has to name the enrollment the gateway already trusts, not merely an object the card is willing to open.

What the client wants instead of an exported key

The -c option normally takes a file path, but when the value parses as a PKCS#11 URI, OpenConnect treats it as a reference to an object inside the token rather than to a file on disk. The -k option behaves the same way for the private key. Nothing gets exported, and the key never leaves the protection the card provides. What the client needs from you is a name that the token module, the certificate store, and the gateway enrollment all agree on.

That last constraint is where most attempts stall. A URI can be perfectly well formed and still select a certificate the gateway will reject, because validity and relevance are separate questions. The check worth doing is a comparison against the enrollment record, not a scan for the first object that happens to open.

Two URIs, two different jobs

The certificate URI answers which identity you are claiming. It points at a certificate object whose public key and subject line match the account or device profile the VPN administrator issued. That same certificate usually arrives with a private key object stored alongside it, and the pair is what the TLS handshake actually needs.

The key URI answers where the matching private material lives when OpenConnect cannot infer it. With only -c set, the client builds a key search from the certificate properties; supplying -k replaces guesswork with an explicit object reference. The token PIN is a third, unrelated input: it unlocks the card for the operation, and it never identifies an object.

openconnect --protocol=anyconnect \
  -c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=1a2b3c4d;id=%01' \
  vpn.example.net

Everything in that quoted string is data. The semicolons separate URI attributes and the percent escapes encode characters that would otherwise break parsing, so the whole argument has to survive the shell intact.

Finding the URI that matches your enrollment

GnuTLS ships p11tool, which lists what the current user can actually reach through p11-kit. Start with p11tool --list-tokens, then list the certificate objects on the token you care about. Add --login when the objects only appear after a PIN, and compare the subject and issuer against the enrollment record rather than picking whatever appears first.

If the token does not show up at all, the problem sits below OpenConnect: p11-kit has no module registered for that middleware, or the module is registered for a different account. System-wide registration lives under /etc/pkcs11/modules, and a per-user directory can hold the same thing without touching the system copy. Check that the provider path in the module file points at a library the reader's software actually installed.

When you copy the URI out of the listing, keep the quotes around it. Unquoted semicolons truncate the argument at the first attribute and the client then reports an object it cannot find.

When the automatic key lookup gives up

OpenConnect can derive a key search from the certificate URI, but the exact matching it performs depends on the version, and the object type and label handling has changed over time. Advice that tells you to shorten the URI until it works is describing an old workaround, not a requirement. If the derived search fails, name the key explicitly with -k instead of editing the certificate URI into something vaguer.

Confirm the key exists and is visible with p11tool --list-keys before blaming the client. Then check that the object is flagged as usable for signing or decryption, since a key generated with the wrong attributes cannot complete a handshake no matter how correct the URI is. Do not transplant a serialized pkcs11-id value from an OpenVPN profile into an OpenConnect option; the two clients use different identifier formats even when they talk to the same card.

If the URI resolves and the PIN is accepted but authentication still fails, stop tuning the URI. Read the gateway's own error, because the next failure is upstream of the token.

Failures that only look like URI problems

A gateway that speaks a different protocol will fail identically whether your URI is right or wrong. OpenConnect replaced the legacy vpnc client for a reason, and card support does not make an incompatible gateway protocol compatible. Confirm the server's expected protocol and authentication arrangement before spending another hour on object attributes.

Server identity validation is a separate layer too. The client verifies the gateway certificate against its trust store, and that check runs after the card has already done its part. Reader permissions and a stopped pcscd service produce the same outward symptom, an unreadable token, with a much simpler fix.

Work outward from the object: does p11tool see the token, does it see the certificate and key, does OpenConnect accept the URI, and only then ask whether the gateway accepts the identity. Each step has its own error message, and each one is cheaper to test than the one after it.

ተዛማጅ ጽሑፎች