I'm attempting to create a self signed certificate in KeyVault using the "Self" issuer.
$policy = New-AzureKeyVaultCertificatePolicy -SubjectName "CN=$($certificateName)" -IssuerName "Self" -ValidityInMonths 12
$policy.Exportable = $true
Add-AzureKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy
However, when getting the certificate back it doesn't appear to have a private key.
Creating certificates directly in KeyVault doesn't seem hugely covered online, after digging into the rest API documentation and source code for the powershell cmdlets, I'm stumped.
I'm hoping it's something simple I've missed, as I wish to avoid creating the certificate locally..
On the Key Vault properties pages, select Certificates. Click on Generate/Import. On the Create a certificate screen choose the following values: Method of Certificate Creation: Generate.
Azure Key Vault enables Microsoft Azure applications and users to store and use certificates, which are built on top of keys and secrets and add an automated renewal feature.
Under Private key source, select Azure Vault . Fill these input fields with the values you noted down when setting up the Azure Key Vault with the private key. Under Password, enter the password that you set when creating an Azure Key Vault with a private key. Click Import.
If you'd like to retrieve your certificate along with its private key, then you can export it to a PFX file (with an empty password) on your disk via:
$vaultName = "my-vault-name"
$certificateName = "my-cert-name"
$pfxPath = [Environment]::GetFolderPath("Desktop") + "\$certificateName.pfx"
$pfxSecret = Get-AzureKeyVaultSecret -VaultName $vaultName -Name $certificateName
$pfxUnprotectedBytes = [Convert]::FromBase64String($pfxSecret.SecretValueText)
[IO.File]::WriteAllBytes($pfxPath, $pfxUnprotectedBytes)
If you'd like to view just the private key itself in-memory without writing to disk, then try:
$vaultName = "my-vault-name"
$certificateName = "my-cert-name"
$pfxPath = [Environment]::GetFolderPath("Desktop") + "\$certificateName.pfx"
$pfxSecret = Get-AzureKeyVaultSecret -VaultName $vaultName -Name $certificateName
$pfxUnprotectedBytes = [Convert]::FromBase64String($pfxSecret.SecretValueText)
$pfx = New-Object Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import($pfxUnprotectedBytes, $null, [Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$pfx.PrivateKey.ExportParameters($true)
which will show the private parameters in addition to the exponent and modulus.
If you'd like to protect the PFX file on disk with your own password (as per the "Retrieve pfx file & add password back" instructions in this blog post), then try:
$vaultName = "my-vault-name"
$certificateName = "my-cert-name"
$pfxPath = [Environment]::GetFolderPath("Desktop") + "\$certificateName.pfx"
$password = "my-password"
$pfxSecret = Get-AzureKeyVaultSecret -VaultName $vaultName -Name $certificateName
$pfxUnprotectedBytes = [Convert]::FromBase64String($pfxSecret.SecretValueText)
$pfx = New-Object Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import($pfxUnprotectedBytes, $null, [Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$pfxProtectedBytes = $pfx.Export([Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $password)
[IO.File]::WriteAllBytes($pfxPath, $pfxProtectedBytes)
As mentioned in the REST API docs here and here, Azure Key Vault (AKV) represents a given X.509 certificate via three interrelated resources: an AKV-certificate, an AKV-key, and an AKV-secret. All three will share the same name and the same version - to verify this, examine the Id
, KeyId
, and SecretId
properties in the response from Get-AzureKeyVaultCertificate
.
Each of these 3 resources provide a different perspective for viewing a given X.509 cert:
n
and e
), as well as other cert metadata (thumbprint, expiry date, subject name, and so on). In PowerShell, you can obtain this via:(Get-AzureKeyVaultCertificate -VaultName $vaultName -Name $certificateName).Certificate
(Get-AzureKeyVaultKey -VaultName $vaultName -Name $certificateName).Key
(Get-AzureKeyVaultSecret -VaultName $vaultName -Name $certificateName).SecretValueText
Following is C# code to retrieve all versions of a certificate, including their private keys, from newest to oldest, given its certificate name and KeyVault connection info. It uses the new Azure.Core
, Azure.Identity
, and Azure.Security.KeyVault.[Certificates|Secrets]
SDK packages.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Azure.Core;
using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Secrets;
public static class CertTools
{
public static void MyMethod(string tenantId, string clientId, string clientSecret, Uri keyVaultUri)
{
var cred = new ClientSecretCredential(tenantId, clientId, clientSecret); // or any other means of obtaining Azure credential
var certs = GetAllCertificateVersions(keyVaultUri, cred, "MyCert");
}
public static List<X509Certificate2> GetAllCertificateVersions(Uri keyVaultUri, TokenCredential credential,
string certificateName)
{
var certClient = new CertificateClient(keyVaultUri, credential);
var secretClient = new SecretClient(keyVaultUri, credential);
var now = DateTimeOffset.UtcNow;
var certs = new List<X509Certificate2>();
foreach (var cert in certClient.GetPropertiesOfCertificateVersions(certificateName)
.OrderByDescending(x => x.CreatedOn)
// fetch all enabled, non-expired certificates. adjust this predicate if desired.
.Where(x => x.ExpiresOn >= now && (x.Enabled ?? false)))
{
var secret = secretClient.GetSecret(certificateName, cert.Version).Value;
certs.Add(new X509Certificate2(Convert.FromBase64String(secret.Value)));
}
return certs;
}
}
Thanks to @Nandun's answer here for pointing me in the right direction of using the SecretClient instead of CertificateClient, but that post was marked as a duplicate so posting this extended code here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With