Sometimes, you may need to convert a certificate to PEM format for use on *nix or non-Windows platforms. Here is a way to do this using PowerShell without any third party software.
First, we need to get a cert loaded into a variable. This cert can be pulled from a PFX or loaded directly from a certificate store.
Here is how to pull from a PFX file:
$pfxPath = "[path]\Cert.pfx"
$pfxPassword = 'MySuperSecretPassword!'
$Import_Pfx_ExportOptions = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($pfxPath, $pfxPassword, $Import_Pfx_ExportOptions)
Or if you prefer to pull a cert from the user or system certificate store:
# User store
$cert = Get-ChildItem Cert:\CurrentUser\My
# Computer store
$cert = Get-ChildItem Cert:\LocalMachine\My
When pulling from user or computer store, you may need to use a where-object command to filter to the desired cert, assuming you may have more than one to select from.
Once you have the cert assigned to the $cert variable, you can use the following to export the key, cert and key+cert file, depending on your requirements.
# Public key to Base 64
$CertBase64 = [System.Convert]::ToBase64String($cert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks)
# Private key to Base 64
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
$KeyBytes = $RSACng.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
$KeyBase64 = [System.Convert]::ToBase64String($KeyBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$PemKey = @"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
"@
$PemCert = @"
-----BEGIN CERTIFICATE-----
$CertBase64
-----END CERTIFICATE-----
"@
$PemAll = @"
$PemKey
$PemCert
"@
# Output to files
$PemAll | Out-File -FilePath CertAndKey.pem -Encoding Ascii
$PemKey | Out-File -FilePath Cert.key -Encoding Ascii
$PemCert | Out-File -FilePath Cert.crt -Encoding Ascii
The code block above does not include the certificate chain. But if you need to include the entire certificate chain in the cert, you can use this method instead:
# Collect certificates
$CertChain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain
$CertChain.Build($cert)
$certs = $CertChain.ChainElements | ForEach-Object {$_.Certificate}
[System.Array]::Reverse($certs)
# Private key to Base 64
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
$KeyBytes = $RSACng.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
$KeyBase64 = [System.Convert]::ToBase64String($KeyBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$PemKey = @"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
"@
$PemCert = ""
$i = 0
ForEach ($crt in $certs) {
if ($i -eq 0) {
$PemCert += @"
-----BEGIN CERTIFICATE-----
$([System.Convert]::ToBase64String($crt.RawData, [System.Base64FormattingOptions]::InsertLineBreaks))
-----END CERTIFICATE-----
"@
} else {
$PemCert += @"
-----BEGIN CERTIFICATE-----
$([System.Convert]::ToBase64String($crt.RawData, [System.Base64FormattingOptions]::InsertLineBreaks))
-----END CERTIFICATE-----
"@
}
$i++
}
$PemAll = @"
$PemKey
$PemCert
"@
# Output to files
$PemAll | Out-File -FilePath CertWithChainAndKey.pem -Encoding Ascii
$PemKey | Out-File -FilePath CertWithChain.key -Encoding Ascii
$PemCert | Out-File -FilePath CertWithChain.crt -Encoding Ascii
These commands will create the certificate(s) and key files, which you can use with 3rd party systems the need PEM format certificates.
Note: the above assumes 1) the certificate you are converting has a private key and 2) the private key marked as exportable.