PIV smart cards and the CERT SET KEY PROV HANDLE PROP ID optimisation

Context

PIV smart cards (NIST SP 800-73 [PIV]) are smart cards with a PIV applet installed. Unlike other smart cards, a PIV card cannot be identified by its ATR — which is contrary to the standard Calais Database architecture.

PIV smart card without ATR in registry
Smart card registration data — note the missing ATR field for PIV cards
Container name

Cryptographic functions require a context acquired via CryptAcquireContext, specifying a CSP (from the Calais database) and a container. With standard smart cards, the container name is stable — built from the ATR, serial number, or card position.

certutil showing container name
Example container name (569f1c0b-0caa-44ea-a695-389f96408132) from certutil -scinfo
PIV problem: With PIV smart cards, the container name changes on every enumeration. You can access it directly after opening a context, but once that context is closed, the container can no longer be accessed by its name.
Certificate auto-registration & the solution

When a smart card is inserted, a service enumerates all containers, finds certificates, and registers them in the user store with a CRYPT_KEY_PROV_INFO structure recording the container and CSP name.

Certificate propagation service
Certificate propagation service flow on smart card insertion

The solution: each time a PIV card is inserted, the container name changes, but the registration is re-done, and the flag CERT_SET_KEY_PROV_HANDLE_PROP_ID (also CERT_SET_KEY_CONTEXT_PROP_ID) is set.


Did you look at CryptAcquireCertificatePrivateKey? It is not simply CertGetCertificateContextProperty(CERT_KEY_PROV_INFO_PROP_ID) followed by CryptAcquireContext.

You can cache a HCRYPTPROV inside a PCCERT_CONTEXT when the flag CERT_SET_KEY_PROV_HANDLE_PROP_ID is set, then retrieve it when the flag CRYPT_ACQUIRE_USE_PROV_INFO_FLAG is set in CryptAcquireCertificatePrivateKey.

This allows you to access a container via its temporary name and optionally benefit from cached authentication. The SILENT_CONTEXT flag can be changed in CryptAcquireCertificatePrivateKey, and the CryptoAPI context is freed when the certificate context is freed.

Note: CryptGetProvParam with PP_UNIQUE_CONTAINER returns the same container name as PP_CONTAINER.