Skip to content

PIV smart cards and CERT_SET_KEY_PROV_HANDLE_PROP_ID

Context

A PIV smart card (NIST SP 800-73) is a smart card with a PIV applet. Unlike other cards, a PIV card can't be identified by its ATR, while the Calais database registers cards by ATR.

PIV smart card without ATR in registry
Smart card registration in the Calais database: the PIV entry has no ATR
Container name

CryptoAPI calls need a context from CryptAcquireContext, which takes a CSP (from the Calais database) and a container name. On most smart cards, the container name is stable: it comes from the ATR, the serial number, or the card position.

certutil showing container name
Example container name (569f1c0b-0caa-44ea-a695-389f96408132) from certutil -scinfo
PIV problem: On a PIV card, the container name changes at every enumeration. The name works while the context that returned it is open. Once that context is closed, the name no longer opens the container.
Certificate propagation and the fix

When a smart card is inserted, the Certificate Propagation service enumerates the containers, finds the certificates, and adds them to the user store. Each certificate gets a CRYPT_KEY_PROV_INFO with the container and CSP names.

Certificate propagation service
Certificate propagation service flow on smart card insertion

The fix: the container name changes at each insertion, but the service registers the certificate again each time, with the CERT_SET_KEY_PROV_HANDLE_PROP_ID flag (also CERT_SET_KEY_CONTEXT_PROP_ID) set.


CryptAcquireCertificatePrivateKey does more than call CertGetCertificateContextProperty(CERT_KEY_PROV_INFO_PROP_ID) and then CryptAcquireContext.

When CERT_SET_KEY_PROV_HANDLE_PROP_ID is set, the PCCERT_CONTEXT caches an HCRYPTPROV. CryptAcquireCertificatePrivateKey returns that handle when called with CRYPT_ACQUIRE_USE_PROV_INFO_FLAG.

The key stays reachable through its temporary container name, and the authentication cached on that handle can be reused. The caller can still change the SILENT_CONTEXT flag in CryptAcquireCertificatePrivateKey. The CryptoAPI context is released with the certificate context.

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