1 min read
Azure Key Vault Certificates: Complete Lifecycle Management
I wrote “Azure Key Vault Certificates: Complete Lifecycle Management” to share practical, production-minded guidance on this topic.
Certificate Storage Concepts
Key Vault stores certificates as:
- Certificate: The X.509 certificate (public)
- Key: The private key (protected)
- Secret: The combined certificate + private key (for download)
Creating Certificates
Self-Signed Certificate
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient, CertificatePolicy
credential = DefaultAzureCredential()
vault_url = "https://my-keyvault.vault.azure.net"
client = CertificateClient(vault_url, credential)
# Create self-signed certificate
policy = CertificatePolicy(
issuer_name="Self",
subject="CN=myapp.contoso.com",
validity_in_months=12,
exportable=True,
key_type="RSA",
key_size=2048,
content_type="application/x-pkcs12", # or "application/x-pem-file"
san_dns_names=["myapp.contoso.com", "www.myapp.contoso.com"]
)
# Start certificate creation
operation = client.begin_create_certificate(
certificate_name="myapp-cert",
policy=policy
)
# Wait for completion
certificate = operation.result()
print(f"Certificate created: {certificate.name}")
print(f"Thumbprint: {certificate.properties.x509_thumbprint.hex()}")
CA-Issued Certificate
# Policy for CA-issued certificate
ca_policy = CertificatePolicy(
issuer_name="DigiCertCA", # Pre-configured CA issuer
subject="CN=api.contoso.com,O=Contoso,C=US",
validity_in_months=24,
exportable=True,
key_type="RSA",
key_size=4096,
content_type="application/x-pem-file",
san_dns_names=["api.contoso.com", "api-staging.contoso.com"],
key_usage=["digitalSignature", "keyEncipherment"],
enhanced_key_usage=["1.3.6.1.5.5.7.3.1"] # Server authentication
)
# Create CSR and submit to CA
operation = client.begin_create_certificate(
certificate_name="api-cert",
policy=ca_policy
)
# Check status
print(f"Status: {operation.status()}")
# For manual issuers, get CSR
pending = client.get_certificate_operation("api-cert")
if pending.csr:
import base64
csr_pem = f"-----BEGIN CERTIFICATE REQUEST-----\n{base64.b64encode(pending.csr).decode()}\n-----END CERTIFICATE REQUEST-----"
print(f"CSR:\n{csr_pem}")
Importing Certificates
import base64
def import_pfx_certificate(client, cert_name, pfx_path, password=None):
"""Import certificate from PFX file"""
with open(pfx_path, 'rb') as f:
pfx_data = f.read()
# Import requires base64-encoded content
certificate = client.import_certificate(
certificate_name=cert_name,
certificate_bytes=pfx_data,
password=password,
policy=CertificatePolicy(
exportable=True,
content_type="application/x-pkcs12"
)
)
return certificate
def import_pem_certificate(client, cert_name, cert_path, key_path):
"""Import certificate from PEM files"""
with open(cert_path, 'r') as f:
cert_pem = f.read()
with open(key_path, 'r') as f:
key_pem = f.read()
# Combine into single PEM
combined_pem = cert_pem + "\n" + key_pem
certificate = client.import_certificate(
certificate_name=cert_name,
certificate_bytes=combined_pem.encode(),
policy=CertificatePolicy(
exportable=True,
content_type="application/x-pem-file"
)
)
return certificate
Retrieving Certificates
from azure.keyvault.secrets import SecretClient
from cryptography import x509
from cryptography.hazmat.primitives import serialization
import base64
def get_certificate_with_key(vault_url, cert_name, credential):
"""Get certificate with private key"""
# Get certificate (public only)
cert_client = CertificateClient(vault_url, credential)
certificate = cert_client.get_certificate(cert_name)
# Get secret (contains private key)
secret_client = SecretClient(vault_url, credential)
secret = secret_client.get_secret(cert_name)
if certificate.policy.content_type == "application/x-pem-file":
# PEM format - can use directly
pem_data = secret.value
return {
'certificate': certificate,
'pem': pem_data
}
else:
# PKCS#12 format - decode
pfx_data = base64.b64decode(secret.value)
return {
'certificate': certificate,
'pfx': pfx_data
}
def get_certificate_for_ssl(vault_url, cert_name, credential):
"""Get certificate in format usable for SSL"""
result = get_certificate_with_key(vault_url, cert_name, credential)
if 'pem' in result:
return result['pem'].encode()
else:
# Convert PKCS#12 to PEM
from cryptography.hazmat.primitives.serialization import pkcs12
private_key, certificate, chain = pkcs12.load_key_and_certificates(
result['pfx'],
password=None
)
# Export to PEM
pem_key = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
pem_cert = certificate.public_bytes(serialization.Encoding.PEM)
return pem_cert + pem_key
Auto-Renewal Configuration
from azure.keyvault.certificates import (
LifetimeAction,
CertificatePolicyAction
)
# Create certificate with auto-renewal
auto_renew_policy = CertificatePolicy(
issuer_name="DigiCertCA",
subject="CN=api.contoso.com",
validity_in_months=12,
lifetime_actions=[
LifetimeAction(
action=CertificatePolicyAction.AUTO_RENEW,
days_before_expiry=30 # Renew 30 days before expiry
),
LifetimeAction(
action=CertificatePolicyAction.EMAIL_CONTACTS,
lifetime_percentage=80 # Email when 80% of lifetime passed
)
]
)
# Update existing certificate policy
client.update_certificate_policy(
certificate_name="api-cert",
policy=auto_renew_policy
)
Certificate Contacts
from azure.keyvault.certificates import CertificateContact
# Set contacts for certificate notifications
contacts = [
CertificateContact(email="security@contoso.com", name="Security Team"),
CertificateContact(email="ops@contoso.com", name="Operations")
]
client.set_contacts(contacts)
# Get current contacts
current_contacts = client.get_contacts()
for contact in current_contacts:
print(f"Contact: {contact.name} <{contact.email}>")
Using Certificates with Azure Services
App Service
# Import Key Vault certificate to App Service
az webapp config ssl import \
--resource-group my-rg \
--name my-webapp \
--key-vault my-keyvault \
--key-vault-certificate-name my-cert
# Bind certificate to custom domain
az webapp config ssl bind \
--resource-group my-rg \
--name my-webapp \
--certificate-thumbprint <thumbprint> \
--ssl-type SNI
Azure Functions
# In function, load certificate from Key Vault
import azure.functions as func
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient
import ssl
import requests
def main(req: func.HttpRequest) -> func.HttpResponse:
credential = DefaultAzureCredential()
cert_client = CertificateClient(
"https://my-keyvault.vault.azure.net",
credential
)
# Get certificate for client authentication
cert_pem = get_certificate_for_ssl(
"https://my-keyvault.vault.azure.net",
"client-cert",
credential
)
# Write to temp file for requests
import tempfile
with tempfile.NamedTemporaryFile(delete=False, suffix='.pem') as f:
f.write(cert_pem)
cert_path = f.name
# Use certificate for mutual TLS
response = requests.get(
"https://api.partner.com/secure-endpoint",
cert=cert_path
)
return func.HttpResponse(response.text)
Application Gateway
# Create SSL certificate from Key Vault
az network application-gateway ssl-cert create \
--gateway-name my-appgw \
--resource-group my-rg \
--name my-ssl-cert \
--key-vault-secret-id https://my-keyvault.vault.azure.net/secrets/my-cert
# Use in HTTPS listener
az network application-gateway http-listener update \
--gateway-name my-appgw \
--resource-group my-rg \
--name https-listener \
--ssl-cert my-ssl-cert
Certificate Monitoring
from datetime import datetime, timedelta
def check_expiring_certificates(client, days_threshold=30):
"""Find certificates expiring soon"""
expiring = []
threshold_date = datetime.utcnow() + timedelta(days=days_threshold)
for cert_props in client.list_properties_of_certificates():
cert = client.get_certificate(cert_props.name)
if cert.properties.expires_on and cert.properties.expires_on < threshold_date:
expiring.append({
'name': cert.name,
'expires_on': cert.properties.expires_on,
'thumbprint': cert.properties.x509_thumbprint.hex()
})
return expiring
# Check and alert
expiring = check_expiring_certificates(client, days_threshold=30)
for cert in expiring:
print(f"WARNING: {cert['name']} expires on {cert['expires_on']}")
Resources
- Key Vault Certificates Documentation
- Certificate Client Library
- Certificate Issuers\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n