Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Key Vault] Add sample for parsing private key/public certificate from certificate #15863

Merged
merged 3 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Key Vault supports PEM as well. If these bytes were PEM encoded, you'd want load_pem_private_key instead. I think it's sufficient to mention the encoding depends on the content type set on the certificate policy, and we're showing PKCS12 here because it's the default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! I didn't want to provide a PEM example in fear of complicating things, but you're right that it's worth a mention even if we don't walk through an example.

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)))