Many of the protocols we use every day rely on certificates. The process to request and obtain a new certificate from a CA is called certificate enrollment. This post explains the basic concepts behind certificate enrollment.
The basic idea of certificate enrollment is simple: A client requests a certificate with certain qualities from a CA, the CA validates the request, creates and signs a certificate, and returns it to the client.
Given that the basic process is so simple, it is tempting to assume that certificate enrollment could be implemented by means of a simple API that allows a client to pass the information it wants to have included in the certificate and that returns a new certificate as result. Unfortunately, there are some fundamental problems with this naive approach. To see what these problems are, let’s review what a certificate really is.
A certificate consists of a public key and a set of metadata, all signed by a private key. If the private key that is used for signing and the public key embedded in the certificate are part of the same keypair, then it is a self-signed certificate. If the private key is from another party, then that other party effectively acts as a certificate authority.
The original X.509 certificate format only allowed a few pieces of metadata to be included in a certificate. These included:
- A subject, identifying the user or machine that the certificate is issued to.
- Information about the issuing certificate authority.
- Date of issue and expiry.
- Information about the encryption and signing algorithms supported by the certificate.
It soon turned out that the original X.509 certificate format was a bit too simple: If two certificates had the same issuer or subject, then there was no reliable way to distinguish these certificates. In an attempt to fix this, X.509 version 2 introduced two new fields, the _issuer unique ID _ and the _subject unique ID. _But this revision never experienced wide adoption and was ultimately superseded by X.509 version 3, which is the format we use today.
The biggest addition version 3 brought was extension attributes. Extension attributes are key/value pairs where the key is an OID (which is globally unique). The standard defines a set of OIDs, but you are free to come up with your own extension attributes so long as you have a unique OID for them.
On Windows, the most commonly used format is PEM and the file extension is typically .cer. Double-clicking a .cer files opens the familiar certificate dialog:
If you open a PEM file with a text editor, you will notice that they always start with a file header that makes them easy to recognize:
-----BEGIN CERTIFICATE----- MIIFFDCCAvygAwIBAgIJAOiogGpKdQAiMA0GCSqGSIb3DQEBCwUAMB8xEDAOBgNV BAoMB1Rlc3QgQ0ExCz... -----END CERTIFICATE-----
A certificate only contains a public key, the corresponding private key is not part of the certificate. A certificate is therefore safe to share publicly while the corresponding private key must be kept confidential.
The best way to protect a private key is to save it in a keystore. A keystore provides encrypted storage for keys and provides APIs that allow you to use a key without ever having to “touch” it. Ideally, this means that you never even need to load the key into memory in clear-text format where it could be snooped on – instead, you let the keystore do all sensitive operations on your behalf.
Keystores can be implemented in hardware (HSMs and smartcards being the most common examples) or in software. By default, Windows uses a software-based keystore which we’ll look at in the next post.
The fact that a certificate depends on a private key, and that the private key must be kept confidential highlights a fundamental issue with the naive certificate enrollment process outlined in the beginning: For the certificate authority to generate a certificate, it needs to have a public key to embed in the certificate – but at the same time, we cannot allow the certificate authority to know the corresponding private key!
Having to conceal the private key from the CA implies that we cannot expect the CA to generate a public/private key pair on the client’s behalf. Instead, the client has to generate the keypair by itself and pass the public key to the CA as part of the enrollment request. This is the idea of a certificate signing request.
Certificate Signing Requests
A certificate signing request (CSR) is like an unsigned contract: It has all the necessary details that the client wants to have included in the certificate, but lacks the final, crucial bit, the CA’s signature. Specifically, a certificate signing request can include:
- The client’s public key.
- The distinguished name of the client.
- Any number of extension attributes.
- A digital signature, which is the hash of the request, encrypted with the client’s private key.
- The hashing algorithm used for the creation of the digital signature.
Certificate signing requests have a similar structure as X.509 certificates, but use a different file format, PKCS#10, which stands for Certification Request Syntax Specification #10. Also like X.509 certificates, PKCS#10 requests are commonly encoded as PEM, but if you look at a file in a text editor, you will notice that they use a different header:
-----BEGIN CERTIFICATE REQUEST----- MIICpzCCAY8CAQAwLzETMBEGA1UEAwwKTXkgTWFjaGluZTEYMBYGA1UECgwPTXkg T3JnYW5pemF0aW9... -----END CERTIFICATE REQUEST-----
Now that we have looked at the structure of X.509 certificates and certificate signing requests, we can design a more sensible certificate enrollment protocol:
- The client generates a public/private key pair and uses it to create a CSR in PKCS#10 format. The CSR contains the client’s public key and is signed by its private key.
- The client sends the CSR to the CA.
- The CA verifies the integrity of the CSR by extracting the client’s public key and using it to check the digital signature of the CSR.
- Assuming the client is considered eligible to be issued a certificate, the CA creates an X.509 certificate and signs it.
- The CA returns the certificate to the client.
- The client “accepts” the certificate and connects it to the private key created earlier.
This process describes the gist of how certificate enrollment protocols like SCEP, EST, and ACME work. The biggest difference between these protocols is how the CA verifies the CSR and which additional evidence the CA is provided to decide whether it is okay to issue a certificate or not.
In Windows environments, arguably the most commonly used internal CA is Active Directory Certificate Services (AD CS). AD CS does not use SCEP or EST, but Windows Client Certificate Enrollment Protocol. _(WCCE) and WS-Trust X.509v3 Token Enrollment Extensions (WSTEP). The first of the two, WCCE runs on top of MS-RPC and seems to be the primary protocol AD CS relies on for certificate enrollment.
Having covered the basic idea of certificate enrollments and CSRs, the next post will look at how you can create CSRs using OpenSSL, Certificate Manager, or certreq.