From 3b0cadf2eb0907e34050f3fa9fe1a2167fd42f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Thu, 17 Dec 2020 17:04:06 -0800 Subject: [PATCH 1/3] Add certificate key/cert parsing samples --- .../samples/hello_world.py | 15 ++- .../samples/hello_world_async.py | 19 ++- .../samples/parse_certificate.py | 99 ++++++++++++++++ .../samples/parse_certificate_async.py | 110 ++++++++++++++++++ 4 files changed, 225 insertions(+), 18 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py create mode 100644 sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/hello_world.py b/sdk/keyvault/azure-keyvault-certificates/samples/hello_world.py index 1bf307637889..b18287e46848 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/hello_world.py +++ b/sdk/keyvault/azure-keyvault-certificates/samples/hello_world.py @@ -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 @@ -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", @@ -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 @@ -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)) diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/hello_world_async.py b/sdk/keyvault/azure-keyvault-certificates/samples/hello_world_async.py index 753aadb90baa..5b9a0574f0fa 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/hello_world_async.py +++ b/sdk/keyvault/azure-keyvault-certificates/samples/hello_world_async.py @@ -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 @@ -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", @@ -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 @@ -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: @@ -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))) diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py new file mode 100644 index 000000000000..6176f866e2f4 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py @@ -0,0 +1,99 @@ +# ------------------------------------ +# 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 protected information about the certificate, such as its private key. + 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. + 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") diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py new file mode 100644 index 000000000000..f7274fb612a3 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py @@ -0,0 +1,110 @@ +# ------------------------------------ +# 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. + 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))) From baef50b9c175437ddcb5b03106fe372d02c6ba0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= Date: Fri, 18 Dec 2020 10:28:03 -0800 Subject: [PATCH 2/3] Thanks, Charles! --- sdk/keyvault/azure-keyvault-certificates/samples/README.md | 5 ++++- .../samples/parse_certificate.py | 6 +++++- .../samples/parse_certificate_async.py | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/README.md b/sdk/keyvault/azure-keyvault-certificates/samples/README.md index 3b984246177d..56557de0efe2 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/README.md +++ b/sdk/keyvault/azure-keyvault-certificates/samples/README.md @@ -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 public certificate and 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 @@ -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 \ No newline at end of file +[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 \ No newline at end of file diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py index 6176f866e2f4..fcb3e0625491 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py +++ b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate.py @@ -66,13 +66,17 @@ 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. + # 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, diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py index f7274fb612a3..ce93bf09723c 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py +++ b/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py @@ -75,6 +75,9 @@ async def run_sample(): # 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, From 705f45d5353f28d120ea065f14dc82aa5cb9343d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McCoy=20Pati=C3=B1o?= <39780829+mccoyp@users.noreply.github.com> Date: Fri, 18 Dec 2020 10:59:25 -0800 Subject: [PATCH 3/3] Better wording Co-authored-by: Charles Lowell --- sdk/keyvault/azure-keyvault-certificates/samples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-certificates/samples/README.md b/sdk/keyvault/azure-keyvault-certificates/samples/README.md index 56557de0efe2..26b909786da7 100644 --- a/sdk/keyvault/azure-keyvault-certificates/samples/README.md +++ b/sdk/keyvault/azure-keyvault-certificates/samples/README.md @@ -19,7 +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 public certificate and private key +* [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 @@ -35,4 +35,4 @@ recover certificates [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 [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 \ No newline at end of file +[parse_async_sample]: https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/keyvault/azure-keyvault-certificates/samples/parse_certificate_async.py