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


PIV smart cards (Identity Device (NIST SP 800-73 [PIV])) are smart card on which a PIV applet has been installed. A PIV smart card cannot be determined by its ATR, at the contrary of the current architecture (the Calais Database).

We can see that the ATR field is missing when looking at the smart card registration data.

PIV without ATR

Container name

When using cryptographic functions, a context must be acquired by cryptacquirecontext. The requirement is to have a CSP (found in the Calais database through SCARD functions – called also ProviderName) and a container. With smart cards, the container is either NULL (default container of the first smart card found) or a reader specifiction (“\\.\MySmartCardReader\”) or directly the container name.

The container name is generally build by the driver using the ATR or the serial number of the smart card or its position (the 2nd container). So this container name is always the same, for each user, and for each time the smart card is inserted. In the following example, the container name is 569f1c0b-0caa-44ea-a695-389f96408132. It is the default container.

card container certutil

Certificate auto registration

When a smart card is inserted, a service list all of the container, look for certificate, and register them into the user store (certmgr.msc). The registration includes a CRYPT_KEY_PROV_INFO structure recording the container name and the CSP name.

But there is a glitch with PIV smart card : the container name changes every time a new enumeration is done. You can list the container name and access it directly through the same CryptAcquireContext, but if you close the context, you cannot access this container anymore using its name.

So how a PIV certificate can be registered into the user store ?

certificate propagation service smart card

Simple : each time the smart card is inserted, the container name changes, but the registration is done once again, and the flag CERT_SET_KEY_PROV_HANDLE_PROP_ID (also called CERT_SET_KEY_CONTEXT_PROP_ID) is set.

Did you look at the function “CryptAcquireCertificatePrivateKey” ? You though that it was the same that CertGetCertificateContextProperty(CERT_KEY_PROV_INFO_PROP_ID) followed by a CryptAcquireContext ? Wrong : you can cache a HCRYPTPROV inside a PCCERT_CONTEXT if the flag CERT_SEKEY_PROV_HANDLE_PROP_ID is set and retrieve it if the flag CRYPT_ACQUIRE_USE_PROV_INFO_FLAG is set in CryptAcquireCertificatePrivateKey.

So as still a PCCERT_CONTEXT is used, you can access a container by its temporary name, and optionally benefit from an authentication. Note the possibility to change the SILENT_CONTEXT in CryptAcquireCertificatePrivateKey and that the CryptoAPI context will be free when the certificate context will be freed.

Note : CryptGetProvParam with PP_UNIQUE_CONTAINER return the same container name that PP_CONTAINER.