Playing certificate authority: Using Cloud KMS to sign certificate signing requests
Last time, we looked at how we can use a Cloud KMS asymmetric signing key to create a self-signed X.509 certificate. But we’re not limited to self-signed certificates: we can use Cloud KMS to sign other certificate signing requests too, just like a certificate authority (CA).
Trusting the issuer
If we want to act like a CA, we first need to establish trust. That is, we have to obtain the certificate authority’s certificate and register it as a trusted authority on other machines.
A CA root certificate is essentially nothing else than a self-signed certificate and we already
know how to create that.
All that’s left to do is to add this certificate to the list of trusted CAs. On Windows, that’s done
by importing the certificate
to the Trusted Root Certification Authorities container. On Linux, we can place the certificate
in /etc/pki/ca-trust/source/anchors/
(in PEM format) and run update-ca-trust
(that’s the process
for CentOS/RHEL, other distributions differ).
Issuing a certificate
Now let’s see how we can use our newfound CA to issue a certificate.
First, we create a local RSA key pair. We can use CNG for that:
var keyParams = new CngKeyCreationParameters
{
// Do not overwrite, store in user profile.
KeyCreationOptions = CngKeyCreationOptions.None,
// Do not allow exporting.
ExportPolicy = CngExportPolicies.None,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
KeyUsage = CngKeyUsages.AllUsages
};
keyParams.Parameters.Add(
new CngProperty(
"Length",
BitConverter.GetBytes(2048),
CngPropertyOptions.None));
var keyPair = new RSACng(CngKey.Create(
CngAlgorithm.Rsa,
"Bob's key",
keyParams));
Next, we create a certificate signing request (CSR). Remember, a 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.
Let’s say the certificate is for Bob:
//
// (1) Create a certificate signing request.
//
var csr = new CertificateRequest(
new X500DistinguishedName("Bob's key"), // Bob's DN
keyPair, // Bob's key pair
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
After creating a CSR, we’d typically send it to the CA to have it signed. To keep things simple, let’s
assume we have direct access to the Cloud KMS signing key. We can then reuse the KmsSignatureGenerator
class
from the last post and sign the CSR:
//
// (2) Sign the certificate signing request.
//
var signatureGenerator = new KmsSignatureGenerator(
kmsService,
kmsKeyName);
var signedCertificate = csr.Create(
new X500DistinguishedName("CN=My KMS key"), // DN of our CA
signatureGenerator, // Use KMS to sign
DateTime.UtcNow, // Validity of cert
DateTime.UtcNow.AddDays(30),
new byte[] { 1 }); // Serial number
signedCertificate
now contains a certificate that’s issued to Bob
and signed by our CA.
If we save the certificate to a file that uses the .cer
file extension, we can use the Windows
certificate viewer to verify that the certificate uses the subject Bob’s cert, and that the
issuer is My KMS key – just like we’d expect from a CA-issued certificate:
And because the CA certificate is trusted, the Certificate Path tab also shows the relationship between the two certificates and confirms that Bob’s certificate is ok:
Just because we can…
As the example above shows, it’s not too difficult to use Cloud KMS to sign certificates, effectively acting as a certificate authority. But in reality, there’s typically more to running a CA than being able to sign a CSR – so you might be better off using a managed service like Certificate Authority Service.