Skip to content

Commit

Permalink
[Key Vault] Add sample for parsing private key/public certificate fro…
Browse files Browse the repository at this point in the history
…m certificate (#15863)

* Add certificate key/cert parsing samples

* Thanks, Charles!

* Better wording

Co-authored-by: Charles Lowell <chlowe@microsoft.com>
  • Loading branch information
mccoyp and chlowell authored Dec 18, 2020
1 parent b62d6a0 commit 6d4efde
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 19 deletions.
5 changes: 4 additions & 1 deletion sdk/keyvault/azure-keyvault-certificates/samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ recover certificates
* [recover_purge_operations.py][recover_purge_operations_sample] and [recover_purge_operations_async.py][recover_purge_operations_async_sample] - recover and purge certificates
* [issuers.py][issuers_sample] and [issuers_async.py][issuers_async_sample] - manage certificate issuers
* [contacts.py][contacts_sample] and [contacts_async.py][contacts_async_sample] - manage certificate contacts
* [parse_certificate.py][parse_sample] and [parse_certificate_async.py][parse_async_sample] - extract a certificate's private key

[backup_operations_sample]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates/samples/backup_restore_operations.py
[backup_operations_async_sample]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates/samples/backup_restore_operations_async.py
Expand All @@ -32,4 +33,6 @@ recover certificates
[contacts_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/contacts.py
[contacts_async_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/contacts_async.py
[issuers_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/issuers.py
[issuers_async_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/issuers_async.py
[issuers_async_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/issuers_async.py
[parse_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py
[parse_async_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py
15 changes: 7 additions & 8 deletions sdk/keyvault/azure-keyvault-certificates/samples/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
#
# 2. azure-keyvault-certificates and azure-identity packages (pip install these)
#
# 3. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL
# (See https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-keys#authenticate-the-client)
# 3. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL. (See
# https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates#authenticate-the-client)
#
# ----------------------------------------------------------------------------------------------------------
# Sample - demonstrates the basic CRUD operations on a vault(certificate) resource for Azure Key Vault
Expand All @@ -39,15 +39,14 @@
try:
# Let's create a certificate for holding bank account credentials valid for 1 year.
# if the certificate already exists in the Key Vault, then a new version of the certificate is created.
print("\n.. Create Certificate")
print("\n.. Create certificate")

# Before creating your certificate, let's create the management policy for your certificate.
# Here you specify the properties of the key, secret, and issuer backing your certificate,
# the X509 component of your certificate, and any lifetime actions you would like to be taken
# on your certificate

# Alternatively, if you would like to use our default policy, don't pass a policy parameter to
# our certificate creation method
# Alternatively, if you would like to use our default policy, use CertificatePolicy.get_default()
cert_policy = CertificatePolicy(
issuer_name=WellKnownIssuerNames.self,
subject="CN=*.microsoft.com",
Expand All @@ -70,12 +69,12 @@
print("Certificate with name '{0}' created".format(certificate.name))

# Let's get the bank certificate using its name
print("\n.. Get a Certificate by name")
print("\n.. Get a certificate by name")
bank_certificate = client.get_certificate(cert_name)
print("Certificate with name '{0}' was found'.".format(bank_certificate.name))

# After one year, the bank account is still active, and we have decided to update the tags.
print("\n.. Update a Certificate by name")
print("\n.. Update a certificate by name")
tags = {"a": "b"}
updated_certificate = client.update_certificate_properties(
certificate_name=bank_certificate.name, tags=tags
Expand All @@ -92,7 +91,7 @@
)

# The bank account was closed, need to delete its credentials from the Key Vault.
print("\n.. Delete Certificate")
print("\n.. Delete certificate")
deleted_certificate = client.begin_delete_certificate(bank_certificate.name).result()
print("Certificate with name '{0}' was deleted.".format(deleted_certificate.name))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
#
# 2. azure-keyvault-certificates and azure-identity packages (pip install these)
#
# 3. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL
# (See https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-keys#authenticate-the-client)
# 3. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL. (See
# https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates#authenticate-the-client)
#
# ----------------------------------------------------------------------------------------------------------
# Sample - demonstrates the basic CRUD operations on a vault(certificate) resource for Azure Key Vault
Expand All @@ -43,15 +43,14 @@ async def run_sample():
try:
# Let's create a certificate for holding bank account credentials valid for 1 year.
# if the certificate already exists in the Key Vault, then a new version of the certificate is created.
print("\n.. Create Certificate")
print("\n.. Create certificate")

# Before creating your certificate, let's create the management policy for your certificate.
# Here you specify the properties of the key, secret, and issuer backing your certificate,
# the X509 component of your certificate, and any lifetime actions you would like to be taken
# on your certificate

# Alternatively, if you would like to use our default policy, don't pass a policy parameter to
# our certificate creation method
# Alternatively, if you would like to use our default policy, use CertificatePolicy.get_default()
cert_policy = CertificatePolicy(
issuer_name=WellKnownIssuerNames.self,
subject="CN=*.microsoft.com",
Expand All @@ -71,12 +70,12 @@ async def run_sample():
print("Certificate with name '{0}' created".format(certificate.name))

# Let's get the bank certificate using its name
print("\n.. Get a Certificate by name")
print("\n.. Get a certificate by name")
bank_certificate = await client.get_certificate(cert_name)
print("Certificate with name '{0}' was found.".format(bank_certificate.name))

# After one year, the bank account is still active, and we have decided to update the tags.
print("\n.. Update a Certificate by name")
print("\n.. Update a certificate by name")
tags = {"a": "b"}
updated_certificate = await client.update_certificate_properties(
certificate_name=bank_certificate.name, tags=tags
Expand All @@ -93,9 +92,9 @@ async def run_sample():
)

# The bank account was closed, need to delete its credentials from the Key Vault.
print("\n.. Delete Certificate")
print("\n.. Delete certificate")
deleted_certificate = await client.delete_certificate(bank_certificate.name)
print("Deleting Certificate..")
print("Deleting certificate..")
print("Certificate with name '{0}' was deleted.".format(deleted_certificate.name))

except HttpResponseError as e:
Expand All @@ -114,4 +113,4 @@ async def run_sample():
loop.close()

except Exception as e:
print("Top level Error: {0}".format(str(e)))
print("Top level error: {0}".format(str(e)))
103 changes: 103 additions & 0 deletions sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import base64
import os
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient, CertificatePolicy
from azure.keyvault.secrets import SecretClient
from azure.core.exceptions import HttpResponseError
from cryptography.hazmat.primitives.serialization import pkcs12

# ----------------------------------------------------------------------------------------------------------
# Prerequisites:
# 1. An Azure Key Vault. (https://docs.microsoft.com/en-us/azure/key-vault/quick-create-cli)
#
# 2. A service principal with certificate get, delete, and purge permissions, as well as secret get
# permissions.
#
# 3. azure-keyvault-certificates, azure-keyvault-secrets, azure-identity, and cryptography (v3.3+) packages
# (pip install these).
#
# 4. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL. (See
# https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates#authenticate-the-client)
#
# ----------------------------------------------------------------------------------------------------------
# Sample - demonstrates how to get the private key of an existing Key Vault certificate
#
# 1. Create a new certificate (CertificateClient.begin_create_certificate)
#
# 2. Get a certificate secret (SecretClient.get_secret)
#
# 3. Delete a certificate (CertificateClient.begin_delete_certificate)
#
# 4. Purge a certificate (CertificateClient.purge_deleted_secret)
#
# ----------------------------------------------------------------------------------------------------------

# Instantiate a certificate client that will be used to call the service.
# Notice that the client is using default Azure credentials.
# To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID',
# 'AZURE_CLIENT_SECRET' and 'AZURE_TENANT_ID' are set with the service principal credentials.
VAULT_URL = os.environ["VAULT_URL"]
credential = DefaultAzureCredential()
certificate_client = CertificateClient(vault_url=VAULT_URL, credential=credential)

# Instantiate a secret client that will be used to call the service.
# Notice that this client can reuse the credential object created above.
secret_client = SecretClient(vault_url=VAULT_URL, credential=credential)
try:
# Let's create a certificate in the vault.
# If the certificate already exists in the Key Vault, then a new version of the certificate is created.
print("\n.. Create certificate")

# Before creating your certificate, let's create the management policy for your certificate.
# Here we use the default policy.
cert_name = "PrivateKeyCertificate"
cert_policy = CertificatePolicy.get_default()

# begin_create_certificate returns a poller. Calling result() on the poller will return the certificate
# as a KeyVaultCertificate if creation is successful, and the CertificateOperation if not. The wait()
# call on the poller will wait until the long running operation is complete.
created_certificate = certificate_client.begin_create_certificate(
certificate_name=cert_name, policy=cert_policy
).result()
print("Certificate with name '{}' was created".format(created_certificate.name))

# Key Vault also creates a secret with the same name as the created certificate.
# This secret contains the certificate's bytes, which include the private key if the certificate's
# policy indicates that the key is exportable.
print("\n.. Get a secret by name")
certificate_secret = secret_client.get_secret(name=cert_name)
print("Certificate secret with name '{}' was found.".format(certificate_secret.name))

# Now we can extract the private key and public certificate from the secret using the cryptography
# package. `additional_certificates` will be empty since the secret only contains one certificate.
# This example shows how to parse a certificate in PKCS12 format since it's the default in Key Vault,
# but PEM certificates are supported as well. With a PEM certificate, you could use load_pem_private_key
# in place of load_key_and_certificates.
cert_bytes = base64.b64decode(certificate_secret.value)
private_key, public_certificate, additional_certificates = pkcs12.load_key_and_certificates(
data=cert_bytes,
password=None
)
print("Certificate with name '{}' was parsed.".format(certificate_secret.name))

# Now we can clean up the vault by deleting, then purging, the certificate.
print("\n.. Delete certificate")
delete_operation_poller = certificate_client.begin_delete_certificate(
certificate_name=cert_name
)
deleted_certificate = delete_operation_poller.result()
delete_operation_poller.wait()
print("Certificate with name '{}' was deleted.".format(deleted_certificate.name))

certificate_client.purge_deleted_certificate(certificate_name=deleted_certificate.name)
print("Certificate with name '{}' is being purged.".format(deleted_certificate.name))

except HttpResponseError as e:
print("\nrun_sample has caught an error. {}".format(e.message))

finally:
print("\nrun_sample done")
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import asyncio
import base64
import os
from azure.identity.aio import DefaultAzureCredential
from azure.keyvault.certificates.aio import CertificateClient
from azure.keyvault.certificates import CertificatePolicy
from azure.keyvault.secrets.aio import SecretClient
from azure.core.exceptions import HttpResponseError
from cryptography.hazmat.primitives.serialization import pkcs12

# ----------------------------------------------------------------------------------------------------------
# Prerequisites:
# 1. An Azure Key Vault. (https://docs.microsoft.com/en-us/azure/key-vault/quick-create-cli)
#
# 2. A service principal with certificate get, delete, and purge permissions, as well as secret get
# permissions.
#
# 3. azure-keyvault-certificates, azure-keyvault-secrets, azure-identity, and cryptography (v3.3+) packages
# (pip install these).
#
# 4. Set Environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET, VAULT_URL. (See
# https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/keyvault/azure-keyvault-certificates#authenticate-the-client)
#
# ----------------------------------------------------------------------------------------------------------
# Sample - demonstrates how to get the private key of an existing Key Vault certificate
#
# 1. Create a new certificate (CertificateClient.create_certificate)
#
# 2. Get a certificate secret (SecretClient.get_secret)
#
# 3. Delete a certificate (CertificateClient.delete_certificate)
#
# 4. Purge a certificate (CertificateClient.purge_deleted_secret)
#
# ----------------------------------------------------------------------------------------------------------

async def run_sample():
# Instantiate a certificate client that will be used to call the service.
# Notice that the client is using default Azure credentials.
# To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID',
# 'AZURE_CLIENT_SECRET' and 'AZURE_TENANT_ID' are set with the service principal credentials.
VAULT_URL = os.environ["VAULT_URL"]
credential = DefaultAzureCredential()
certificate_client = CertificateClient(vault_url=VAULT_URL, credential=credential)

# Instantiate a secret client that will be used to call the service.
# Notice that this client can reuse the credential object created above.
secret_client = SecretClient(vault_url=VAULT_URL, credential=credential)
try:
# Let's create a certificate in the vault.
# If the certificate already exists in the Key Vault, then a new version of the certificate is created.
print("\n.. Create certificate")

# Before creating your certificate, let's create the management policy for your certificate.
# Here we use the default policy.
cert_name = "PrivateKeyCertificate"
cert_policy = CertificatePolicy.get_default()

# Awaiting create_certificate will return the certificate as a KeyVaultCertificate
# if creation is successful, and the CertificateOperation if not.
created_certificate = await certificate_client.create_certificate(
certificate_name=cert_name, policy=cert_policy
)
print("Certificate with name '{}' was created".format(created_certificate.name))

# Key Vault also creates a secret with the same name as the created certificate.
# This secret contains protected information about the certificate, such as its private key.
print("\n.. Get a secret by name")
certificate_secret = await secret_client.get_secret(name=cert_name)
print("Certificate secret with name '{}' was found.".format(certificate_secret.name))

# Now we can extract the private key and public certificate from the secret using the cryptography
# package. `additional_certificates` will be empty since the secret only contains one certificate.
# This example shows how to parse a certificate in PKCS12 format since it's the default in Key Vault,
# but PEM certificates are supported as well. With a PEM certificate, you could use load_pem_private_key
# in place of load_key_and_certificates.
cert_bytes = base64.b64decode(certificate_secret.value)
private_key, public_certificate, additional_certificates = pkcs12.load_key_and_certificates(
data=cert_bytes,
password=None
)
print("Certificate with name '{}' was parsed.".format(certificate_secret.name))

# Now we can clean up the vault by deleting, then purging, the certificate.
print("\n.. Delete certificate")
deleted_certificate = await certificate_client.delete_certificate(certificate_name=cert_name)
print("Certificate with name '{}' was deleted.".format(deleted_certificate.name))

await certificate_client.purge_deleted_certificate(certificate_name=deleted_certificate.name)
print("Certificate with name '{}' is being purged.".format(deleted_certificate.name))

except HttpResponseError as e:
print("\nrun_sample has caught an error. {}".format(e.message))

finally:
print("\nrun_sample done")
await credential.close()
await certificate_client.close()
await secret_client.close()


if __name__ == "__main__":
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(run_sample())
loop.close()

except Exception as e:
print("Top level error: {}".format(str(e)))

0 comments on commit 6d4efde

Please sign in to comment.