Every now and then, you run into a situation where you have a certificate for a website or service, but for one reason or another, you don’t have the whole chain. You’re missing some or all of the intermediaries, and even the root certificate, perhaps.
This is something I run into constantly. This article explains how to fix it!
Background
In my day-to-day work, I maintain a number of customer systems running in Azure. Many of these systems have a custom host name using the customer’s domain – that has something to deal with customer brand identity, user trust, consistency and all that stuff. Makes sense, really, and who would we be to say “no” to the customer?
But that means that that the customer domain needs a certificate to enable TLS/SSL -secured connection, and that certificate needs to come from the customer. And the customers have wildly varying ways to order, generate and share certificates 😅
This, as you might guess, could lead to problems.
Problem
You might get your certificates in .pem, .pfx, .cer, .der or some other format. That’s fine – you can modify them using openssl and such tooling. But you might also get anything from 1 to 7 certificates, usually forming a chain (or being just the last one in the chain). While if you’re just securing communication between a server and a browser, you usually don’t need to care – the browser will figure out if it trusts the intermediary and root certificates, no matter whether they are presented by the server or not! But when you’re securing an API that gets called from the backend… That’s when you have a problem.
See, while a browser can download and cache certificates, a backend usually doesn’t do such things. So you’ll need the whole chain (except for the root certificate).
Naturally, we’ll need the chain in one file. So we need to get the certificates from somewhere and even have them in the same format as the “leaf” certificate.
Earlier, I shared a script that one can use to fetch a certificate from any website AND build its intermediaries. But now we’ll build a script that we can use to
Solution
Well, I came up with a script that could do a bunch of related things – magically exporting the certificates (intermediaries included!) is one thing, but modifying them into something readable and/or usable is another.
So, the script parts below should get us somewhere!
First, we’ll import the “leaf” certificate
# Run this script in PowerShell 5, not 6/7 as they are
# built on .NET Core and you can't use this method to
# build x509 certificate chains there!
# Define the path to your end-entity certificate
$endEntityCertPath = "C:\path\to\end-entity-cert.cer"
# Load the certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($endEntityCertPath)
Okay. So we’ve got the cert. Now what?
Then we’ll build the whole chain
By default, you’ll only have the certificate you just loaded. But if you want the whole chain, here’s what you can do:
# Get the chain
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$chain.Build($cert)
And finally export the certificates
# Export each certificate in the chain
$chain.ChainElements | ForEach-Object {
$certElement = $_.Certificate
$certPath = "C:\path\to\cert-$($_.Certificate.Thumbprint).cer"
[System.IO.File]::WriteAllBytes($certPath, $certElement.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
}
That’ll export your certificates in DER-encoding (so they’re binary data, not base64-encoded or PEM-formatted). Depending on what you want to do, you might want to further work on the certificates. Some examples are below:
# You can now convert the certificate into a base64-encoded PEM format.
# The extension could be .cer or .crt, too, but for clarity's sake,
# we'll use .pem here.
openssl x509 -inform der -in [thumbprint].cer -out mycer.pem
You could even loop the directory for all .cer (DER) files and turn them into .pems or something. Somewhat like this:
Get-Item * | ForEach-Object { openssl x509 -inform der -in $_ -out "$_.pem" }
And of course, if you have .pem -files (which are not binary) you can just concatenate them like this:
Get-Content *.pem | Set-Content complete-cert.pem
And there you have it!
The whole script to build the certificate chain for the leaf certificate
Were you thinking this would not be available in an easily copy-pasteable format? Well, you were wrong. Here you go:
# Run this script in PowerShell 5, not 6/7 as they are
# built on .NET Core and you can't use this method to
# build x509 certificate chains there!
# Define the path to your end-entity certificate
$endEntityCertPath = "C:\path\to\end-entity-cert.cer"
# Load the certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($endEntityCertPath)
# Get the chain
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$chain.Build($cert)
$exportPath = "C:\path\to\export"
# Export each certificate in the chain
$chain.ChainElements | ForEach-Object {
$certElement = $_.Certificate
$certPath = "$($exportPath)\cert-$($_.Certificate.Thumbprint).cer"
[System.IO.File]::WriteAllBytes($certPath, $certElement.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
}
# Change to the directory of our new files
cd $exportPath
# Export as pem files
Get-Item *.cer | ForEach-Object { openssl x509 -inform der -in $_ -out "$_.pem" }
# Combine to one file
Get-Content *.pem | Set-Content complete-cert.pem
And there you go! Not elegant, but it has done the trick so far… 😅
Questions and answers
No. For that, I have another script – find it here: https://www.koskila.net/how-to-export-the-ssl-tls-certificate-a-website-from-a-website-using-powershell
- “Performing cleanup” – Excel is stuck with an old, conflicted file and will never recover. - November 12, 2024
- How to add multiple app URIs for your Entra app registration? - November 5, 2024
- How to access Environment Secrets with GitHub Actions? - October 29, 2024