pythonazuredigital-signatureazure-keyvaultazure-python-sdk

Signing a string with the Azure Key Vault Python SDK


I am looking to sign a string with a certificate created and stored inside Azure Key Vault. I am using the Python SDK provided by Microsoft to fetch the certificate. I proceed to extract the private key from the decoded certificate secret.

Obtaining the private key in this manor is a problem, as it doesn't allow me to use the private key with CryptographyClient. I receive an error, which is expected and shown beneath, as the private key is not a KeyVaultKey instance.

The error I am facing:

Exception has occurred: ValueError
'key' must be a KeyVaultKey instance or a key ID string
  File "C:\Users\P.Markham\OneDrive - Royal Terberg Group B.V\Documents\SSF\init.py", line 31, in <module>
    cryptography_client = CryptographyClient(private_key, credential=credential)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: 'key' must be a KeyVaultKey instance or a key ID string

The code I am using is shown beneath:

import os
import hashlib
import base64
from cryptography.hazmat.primitives.serialization import pkcs12
from azure.identity import DefaultAzureCredential
from azure.keyvault.keys import KeyClient
from azure.keyvault.keys.crypto import SignatureAlgorithm
from azure.keyvault.certificates import CertificateClient
from azure.keyvault.secrets import SecretClient
from azure.keyvault.keys.crypto import CryptographyClient, EncryptionAlgorithm

VAULT_URL = os.environ["AZURE_VAULT_URL"]
CERTIFICATE_NAME = os.environ["AZURE_CERTIFICATE_NAME"]

credential = DefaultAzureCredential()
certificate_client = CertificateClient(vault_url=VAULT_URL, credential=credential)
secret_client = SecretClient(vault_url=VAULT_URL, credential=credential)

certificate_secret = secret_client.get_secret(name=CERTIFICATE_NAME)

cert_bytes = base64.b64decode(certificate_secret.value)
private_key, public_certificate, additional_certificates = pkcs12.load_key_and_certificates(
    data=cert_bytes,
    password=None
)
print(f"Certificate with name '{certificate_secret.name}' was parsed.")

digest = hashlib.sha256(b"an example string i want to sign").digest()
cryptography_client = CryptographyClient(private_key, credential=credential)

# sign returns the signature and the metadata required to verify it
result = cryptography_client.sign(SignatureAlgorithm.rs256, digest)
print(f"Key ID: {result.key_id}")
print(f"Algorithm: {result.algorithm}")
print(f"Signature: {result.signature}")
signature = result.signature

How do I sign a string with a certificate? I feel like I'm going about this the complete wrong way, but I cant find any documentation that links signing with certificates.


Solution

  • If you want to use the certificate to perform cryptographic operations, you can just use the key by the same name directly -- there shouldn't be a need to parse a key out from the certificate secret. For example:

    import hashlib
    import os
    from azure.identity import DefaultAzureCredential
    from azure.keyvault.keys import KeyClient
    from azure.keyvault.keys.crypto import CryptographyClient, SignatureAlgorithm
    
    VAULT_URL = os.environ["AZURE_VAULT_URL"]
    CERTIFICATE_NAME = os.environ["AZURE_CERTIFICATE_NAME"]
    
    credential = DefaultAzureCredential()
    key_client = KeyClient(VAULT_URL, credential)
    cert_key = key_client.get_key(CERTIFICATE_NAME)
    
    digest = hashlib.sha256(b"an example string i want to sign").digest()
    cryptography_client = CryptographyClient(cert_key, credential)
    
    result = cryptography_client.sign(SignatureAlgorithm.rs256, digest)
    print(f"Key ID: {result.key_id}")
    print(f"Algorithm: {result.algorithm}")
    print(f"Signature: {result.signature}")
    signature = result.signature