Certificate enrollment: Crypto API, CNG, and other Windows APIs

In the last posts, we looked at the concepts behind certificate enrollment and how to manually create a certificate signing request by using OpenSSL, certreq, and the Certificate Manager MMC snap-in.

One thing certreq and the Certificate Manager MMC snap-in have in common is that they rely heavily on Windows’ built-in APIs for managing certificates, encryption, and keys.

Once you look at how these APIs work, you quickly realize that there are two APIs that play a particularly important role in how Windows deals with cryptography – the CryptoAPI and Cryptography Next Generation (CNG).

CryptoAPI

The CryptoAPI is the older one of the two APIs. Introduced in Windows NT 4.0, CryptoAPI provides a generic API for encryption and key management, but most functionality is actually implemented by Cryptographic Service Providers (CSPs). Microsoft provides a whole bunch of CSPs itself, and allows third parties to implement their own.

Although CryptoAPI is still widely used, it is considered deprecated. Out of the long list of Microsoft-provided CSPs, Microsoft RSA/Schannel Cryptographic Provider is the only one that is still relevant today, all other CSPs are best avoided.

Like most Win32 APIs, the CryptoAPI provides a C-compatible interface and functions use the Crypt prefix. There also used to be a COM/ActiveX library on top of CryptoAPI called CAPICOM, but it has meanwhile been deprecated.

CNG

Cryptography Next Generation (CNG) was introduced in Windows Vista/2008 as a replacement for the Crypt API. CNG is also extensible to third-parties, but distinguishes between Cryptographic Algorithm Providers (CAP) and key storage providers (KSP).

Microsoft provides a set of Cryptographic Algorithm Providers as well as two Key Storage Providers, one for software-based storage and one for smartcards.

CNG also provides a C-compatible interface, but uses some non-intuitive function prefixes: Functions implementing cryptographic primitives (such as hashing, encrypting or decrypting) use aBCrypt prefix while key storage and retrieval functions use the NCrypt prefix. I do not know what the B in BCrypt stands for, but it certainly does not have anything to do with bcrypt, which is a password hashing function commonly used un Unix.

In .NET, CNG is exposed via the System.Security.Cryptography.Cng* classes in the System.Security.Cryptography namespace.

Key storage

Both CryptoAPI and CNG support key storage and for both APIs, Microsoft provides software-based implementations that are used by default.

If you use CNG, the default keystore provider is the Microsoft Software Key Storage Provider and it stores a user’s keys in %APPDATA%\Microsoft\Crypto\Keys. If you use Crypto API with any of the Microsoft-provided CSPs, your keys end up in %APPDATA%\Microsoft\Crypto\Keys. As many applications still rely on CryptoAPI, there is a good chance that you have some keys in both of these directories.

Certificate storage

As we covered in the last post, a certificate both contains more data than a key and is less sensitive than a private key. It therefore should not come as a surprise that Windows handles certificate storage differently than key storage.

Certificate storage is implemented by the Certificate and Certificate Store Functions. Curiously, these functions are exported by Crypt32.dll, but they are somehow not really considered to be part of the CryptoAPI.

The certificates themselves are managed in logical and physical stores and persisted in the registry. Crucially, this does not include the private keys – the private keys remain in the key storage and only a “link” to the private key is stored alongside the certificate. The API integrates with both CNG and CryptoAPI, so with each certificate that you add to the store, you are free to decide where the corresponding key is stored.

Registry store

The fact that the certificate management functions can work with any keystore explains why the INF file that you have to pass to certutil requires you to specify a ProviderName. In the last post, we specified ProviderName = "Microsoft Software Key Storage Provider" – this caused the generated private key to be stored by CNG. If we had used Microsoft RSA SChannel Cryptographic Provider instead, the private key would have ended up being stored by CryptoAPI.

To get a list of all the CNG and CryptoAPI providers, you can run certutil -csplist:

Provider Name: Microsoft Base Cryptographic Provider v1.0
Provider Type: 1 - PROV_RSA_FULL
[...]
Provider Name: Microsoft Strong Cryptographic Provider
Provider Type: 1 - PROV_RSA_FULL
Provider Name: Microsoft Software Key Storage Provider
Provider Name: Microsoft Passport Key Storage Provider
Provider Name: Microsoft Platform Crypto Provider
Provider Name: Microsoft Smart Card Key Storage Provider
CertUtil: -csplist command completed successfully.

You can also see where the key of each certificate is stored by running certutil -store -user my:

================ Certificate 3 ================

Serial Number: 1378e87966960cc2
Issuer: O=...
 NotBefore: 1/10/2020 5:00 PM
 NotAfter: 1/9/2021 5:00 PM
Subject: CN=...
Non-root Certificate
Cert Hash(sha1): e10a7b7..
  Key Container = 7c8eb3c4...
  Simple container name: tp-a658a2ba-cf85-4693-9ae7-08e19ba9982c
  Provider = Microsoft Base Cryptographic Provider v1.0
  
[...]

================ Certificate 38 ================

Serial Number: d45f2f73ca3bf070
Issuer: CN=...
 NotBefore: 12/5/2019 2:31 AM
 NotAfter: 6/4/2023 2:36 PM
Subject: CN=...
Non-root Certificate
Cert Hash(sha1): 001769a58..
  Key Container = GECC
  Unique container name: 7ecb89...
  Provider = Microsoft Software Key Storage Provider
...

Notice that the key for Certificate 3 is stored by a CryptoAPI CNG while the key for Certificate 38 is stored by a CNG KSP.

Certificate and Certificate Store Functions use a Cert prefix (ex: CertOpenStore). In .NET you can use the X509Store class to access the certificate store.

Certificate enrollment

The certificate and certificate store functions allow you to store and manage certificates, but they do not support certificate enrollment. Certificate enrollment is implemented on top of these libraries as the following, high-level architecture diagram illustrates:

Architecture

The Certificate Enrollment API is is a COM-based API in certenroll.dll. The API supersedes Xenroll, which was the enrollment API used prior to Windows Vista.

One reason for the Certificate Enrollment API being COM-based is that it allows the API to be used from within Internet Explorer. Many CAs rely on this capability to implement a web-based certificate enrollment process – and it is the reason why some CAs still only provide limited or no support for browsers other than Internet Explorer.

In the next post, we will look at how you can use the Certificate Enrollment API to programmatically create a certificate signing request in C#.

« Back to home