Azure Key Vault

Cloud: Microsoft Azure
HSM Level: FIPS 140-2 Level 2 (Standard) / Level 3 (Managed HSM)
PQ Support: Not yet available (as of 2024)

Integration of Azure Key Vault for certificate and key management.


Architecture

flowchart TB subgraph AZURE["AZURE"] subgraph KV["Key Vault"] K[Keys] S[Secrets] C[Certificates] end subgraph HSM["Managed HSM"] H[HSM Keys] end subgraph APPS["Applications"] A1[App Service] A2[AKS] A3[Functions] end end subgraph ONPREM["ON-PREM"] CA[Internal CA] end CA -->|Import| C K --> A1 & A2 & A3 C --> A1 & A2 & A3 H -->|Premium| K style KV fill:#e3f2fd style HSM fill:#e8f5e9


Setup

Create Key Vault

# Azure CLI
az login
 
# Resource Group
az group create --name rg-pki --location germanywestcentral
 
# Key Vault (Standard)
az keyvault create \
    --name kv-pki-prod \
    --resource-group rg-pki \
    --location germanywestcentral \
    --sku standard
 
# Key Vault (Premium with HSM)
az keyvault create \
    --name kv-pki-prod-hsm \
    --resource-group rg-pki \
    --location germanywestcentral \
    --sku premium

Managed HSM (FIPS 140-2 Level 3)

# Create Managed HSM
az keyvault create \
    --hsm-name hsm-pki-prod \
    --resource-group rg-pki \
    --location germanywestcentral \
    --administrators "user@example.com"
 
# Activate HSM (requires 3 RSA keys)
az keyvault security-domain download \
    --hsm-name hsm-pki-prod \
    --sd-wrapping-keys key1.pem key2.pem key3.pem \
    --sd-quorum 2 \
    --security-domain-file sd.json

Manage Certificates

Import Certificate

# Import PFX certificate
az keyvault certificate import \
    --vault-name kv-pki-prod \
    --name server-cert \
    --file server.pfx \
    --password "pfx-password"
// C# - Import certificate
using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
 
var client = new CertificateClient(
    new Uri("https://kv-pki-prod.vault.azure.net/"),
    new DefaultAzureCredential());
 
// Import PFX
byte[] pfxData = File.ReadAllBytes("server.pfx");
var importOptions = new ImportCertificateOptions("server-cert", pfxData)
{
    Password = "pfx-password"
};
 
KeyVaultCertificateWithPolicy cert = await client.ImportCertificateAsync(importOptions);
Console.WriteLine($"Imported: {cert.Name}, Thumbprint: {cert.Properties.X509Thumbprint}");

Retrieve Certificate

// C# - Load certificate from Key Vault
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using System.Security.Cryptography.X509Certificates;
 
var secretClient = new SecretClient(
    new Uri("https://kv-pki-prod.vault.azure.net/"),
    new DefaultAzureCredential());
 
// Retrieve certificate as secret (includes private key)
KeyVaultSecret secret = await secretClient.GetSecretAsync("server-cert");
 
byte[] certBytes = Convert.FromBase64String(secret.Value);
var certificate = new X509Certificate2(certBytes);
 
Console.WriteLine($"Subject: {certificate.Subject}");
Console.WriteLine($"Has Private Key: {certificate.HasPrivateKey}");

Create Certificate with Key Vault CA

# Define certificate policy
az keyvault certificate create \
    --vault-name kv-pki-prod \
    --name app-cert \
    --policy @cert-policy.json
// cert-policy.json
{
  "issuerParameters": {
    "name": "Self"
  },
  "keyProperties": {
    "exportable": true,
    "keySize": 4096,
    "keyType": "RSA",
    "reuseKey": false
  },
  "secretProperties": {
    "contentType": "application/x-pkcs12"
  },
  "x509CertificateProperties": {
    "subject": "CN=app.example.com",
    "subjectAlternativeNames": {
      "dnsNames": ["app.example.com", "*.app.example.com"]
    },
    "validityInMonths": 12
  }
}

Keys for Signing

Create Signing Key

# EC key for signatures
az keyvault key create \
    --vault-name kv-pki-prod \
    --name signing-key \
    --kty EC \
    --curve P-384
 
# RSA key
az keyvault key create \
    --vault-name kv-pki-prod \
    --name rsa-signing-key \
    --kty RSA \
    --size 4096

Remote Signing

// C# - Sign with Azure Key Vault key
using Azure.Identity;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;
 
var keyClient = new KeyClient(
    new Uri("https://kv-pki-prod.vault.azure.net/"),
    new DefaultAzureCredential());
 
KeyVaultKey key = await keyClient.GetKeyAsync("signing-key");
var cryptoClient = new CryptographyClient(key.Id, new DefaultAzureCredential());
 
// Sign data
byte[] dataToSign = Encoding.UTF8.GetBytes("Important document");
byte[] digest = SHA384.HashData(dataToSign);
 
SignResult signature = await cryptoClient.SignAsync(
    SignatureAlgorithm.ES384,
    digest);
 
Console.WriteLine($"Signature: {Convert.ToBase64String(signature.Signature)}");
 
// Verify signature
VerifyResult verified = await cryptoClient.VerifyAsync(
    SignatureAlgorithm.ES384,
    digest,
    signature.Signature);
 
Console.WriteLine($"Verified: {verified.IsValid}");

App Service / AKS Integration

App Service

# Key Vault reference in App Settings
az webapp config appsettings set \
    --name myapp \
    --resource-group rg-app \
    --settings "Certificate=@Microsoft.KeyVault(VaultName=kv-pki-prod;SecretName=server-cert)"
 
# Enable Managed Identity
az webapp identity assign \
    --name myapp \
    --resource-group rg-app
 
# Key Vault Access Policy
az keyvault set-policy \
    --name kv-pki-prod \
    --object-id <managed-identity-object-id> \
    --secret-permissions get list \
    --certificate-permissions get list

Azure Kubernetes Service (AKS)

# secrets-store-csi-driver.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-keyvault-tls
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true"
    userAssignedIdentityID: "<client-id>"
    keyvaultName: "kv-pki-prod"
    objects: |
      array:
        - |
          objectName: server-cert
          objectType: secret
    tenantId: "<tenant-id>"
  secretObjects:
    - secretName: tls-secret
      type: kubernetes.io/tls
      data:
        - objectName: server-cert
          key: tls.crt
        - objectName: server-cert
          key: tls.key
# pod-with-keyvault-cert.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-tls
spec:
  containers:
    - name: app
      image: myapp:latest
      volumeMounts:
        - name: secrets-store
          mountPath: "/mnt/secrets-store"
          readOnly: true
  volumes:
    - name: secrets-store
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: "azure-keyvault-tls"

Monitoring

# Enable diagnostics
az monitor diagnostic-settings create \
    --name kv-diagnostics \
    --resource /subscriptions/<sub>/resourceGroups/rg-pki/providers/Microsoft.KeyVault/vaults/kv-pki-prod \
    --logs '[{"category": "AuditEvent", "enabled": true}]' \
    --metrics '[{"category": "AllMetrics", "enabled": true}]' \
    --workspace <log-analytics-workspace-id>

KQL Query for certificate operations:

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName contains "Certificate"
| project TimeGenerated, OperationName, ResultType, CallerIPAddress, identity_claim_upn_s
| order by TimeGenerated desc

Checklist

# Checkpoint Done
——————
1 Key Vault created
2 Access policies configured
3 Certificates imported
4 Managed Identity for apps
5 Diagnostics enabled
6 Backup configured


« <- Cloud Integration | -> AWS KMS »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: on 2026/01/30 at 01:26 AM