Developer Notes

Grafitti artist at work with a spray can. Photo by Clem Onojeghuo

Offline Certificate Authority with Azure Key Vault

Getting proper certificates for TLS has been made a lot easier in recent years with excellent initiatives like Let's Encrypt. However, for some applications, I want to be able to sign my own certificates.

One example is when putting an Azure API Management instance behind an Azure AppGateway. Another example is connecting MQTT clients to Azure EventGrid.

The problem is that an online Certificate Authority (CA), like Let's Encrypt, can't verify your host against public DNS. Besides that, getting certificates from a public CA is not free, and especially in the MQTT case or when applying mTLS, you may need a lot of certificates.

The solution I present here is to issue your own certificates from your own CA, without actually running a CA service. I'll put the CA certificates in Azure KeyVault for safekeeping. This also enables automatic renewal to keep the certificates valid.

There's a GitHub repo with all the scripts and code.

CA certificates in Key Vault with an Azure Function to handle automatic renewal
CA certificates in Key Vault with an Azure Function to handle automatic renewal

Creating CA certs

While you could probably dig up the OpenSSL commands to create and sign certificates, there's an easier solution in Step CLI. It's a Swiss army knife for all things to do with certificates.

The following Powershell script will generate a root and intermediate certificate under ca/certs. The keys are under ca/secrets. The CA certs are valid for 10 years.

$ENV:STEPPATH="$PWD/ca"
mkdir $PWD/ca
step ca init --deployment-type standalone --name MyLocalDomain --dns mycompany.local --address 127.0.0.1:443 --provisioner MyCompany 

If you follow these steps to create a new CA, make sure you keep the generated passwords safe! You'll need them to prepare the CA certificates for Azure later.

The secrets generated for this CA must not be checked into source control. Once the certificates have been pushed to Key Vault, the secrets should be deleted and passwords are no longer needed.

Upload CA certificates to Azure Key Vault

Azure CLI needs PKCS12 (.pfx) files to import the certificates into Key Vault. The .pfx files must not have their keys encrypted with a password. Azure CLI cannot import encrypted keys into Key Vault.

step certificate p12 --no-password --insecure `
    root_ca.pfx .\ca\certs\root_ca.crt .\ca\secrets\root_ca_key

step certificate p12 --no-password --insecure `
    intermediate_ca.pfx .\ca\certs\intermediate_ca.crt .\ca\secrets\intermediate_ca_key

Next, import the certificates into Key Vault.

az keyvault certificate import --vault-name my-kv-name  `
   -n mycompany-local-intermediate -f .\.\ca\certs\intermediate_ca.pfx 

az keyvault certificate import --vault-name my-kv-name  `
   -n mycompany-local-root -f .\ca\certs\root_ca.pfx

You're now ready to issue certificates from your Key Vault.

Managing Certificates using the offline CA

Since we're not running an online CA service, we need to handle certificate issuing and renewal ourselves. Using Azure CLI and Step CLI we can do this.

To issue a certificate, start by creating a new certificate signing request (CSR) in Azure Key Vault. Make sure you specify you want to use a non-integrated CA and add any (internal) DNS names you'll need. You can also create a wild card certificate.

Offline signing

The following steps are required to complete the CSR:

  1. Download the CSR
  2. Download the intermediate CA certificate with it's private key
  3. Create and sign the certificate using the CSR and the intermediate cert
    Step conveniently adds the intermediate certificate to form the certificate chain in the output files.
  4. Upload the signed certificate to Key Vault
  5. Delete all the certificates
    We don't want certificates with private keys lingering around on our dev box.

The GitHub repo has a Powershell script that demonstrates these steps. 

Renewing a certificate

The renewal process is the same as the initial signing request, except it's initiated by telling Key Vault to create a new version of the certificate. This will make the CSR available again. The same script can be used to sign the CSR and push it back into KeyVault.

I will cover automatic renewal in a future post.

Caveats

First off, I'm no expert on cryptography. Secondly, public CAs exist for good reasons. They are trusted by many parties and have to prove their trustworthiness continuously. They operate under strict guidelines. If you manage certificates yourself, you are responsible for keeping the signing certificates safe.

On top of that, a CA service allows you to directly verify certificate validity with the CA by managing Certificate Revocation Lists (CRL). The solution presented here cannot do this.

If you need to have the general public access your services from a browser or other off-the-shelf software or appliance, this solution will not work for you.

There are solutions to running a full CA yourself, for example, Active Directory Certificate Services (AD CS). These are great solutions if you need to manage certificates for an entire organization but they come at operational cost.

What I'm describing here is a lighter solution that aims to secure communications within a controlled environment. Trust is not an issue because we control all the entities that communicate with each other.

References