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).
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
There also used to be a COM/ActiveX library on top of CryptoAPI called CAPICOM, but it has meanwhile been deprecated.
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).
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 a
prefix while key storage and retrieval functions use the NCrypt prefix. I do not know what the
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
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
%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.
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.
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
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
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.
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:
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