Why isn't my .PEM key in Key Vault correctly being used in my Function App?

129 views Asked by At

I am trying to complete a process using Python via an Azure Function App. This process takes a private key (.PEM, which has been uploaded to Azure Key Vault as a Key) as well as two secrets (our API key basically), declares several claims, signs, and returns a signed JWT as output.

I have run different variations of this over 170 times in my function app, and each time the secrets (so the API keys) are retrieved fine, but the process fails on the Key part. I created the key using OpenSSL in Git BASH, and when I run a version of this script locally (so calling directly the .PEM file), it works fine.

Do I need to also 'use' the public key too?? If so, how?

This is the current Python script I am using in my Function App (with certain parts redacted):

import azure.functions as func
import logging
import jwt
import uuid
from time import time
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
from azure.keyvault.keys import KeyClient
from azure.keyvault.keys.crypto import CryptographyClient, SignatureAlgorithm

async def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    # Azure Key Vault settings
    key_vault_url = "<my key vault>.vault.azure.net/"
    credential = DefaultAzureCredential()

    # Initialize the KeyClient to fetch the key for signing
    key_client = KeyClient(vault_url=key_vault_url, credential=credential)
    key_name = "int-test1"  # The name of the key in Azure Key Vault
    key = key_client.get_key(key_name)

    # Initialize the CryptographyClient for signing
    crypto_client = CryptographyClient(key, credential)

    # Initialize the SecretClient to fetch the 'sub' and 'iss' claims
    secret_client = SecretClient(vault_url=key_vault_url, credential=credential)
    sub_claim = secret_client.get_secret("int-sub").value
    iss_claim = secret_client.get_secret("int-iss").value

    # Your JWT claims
    claims = {
        "sub": sub_claim,
        "iss": iss_claim,
        "jti": str(uuid.uuid4()),
        "aud": "<api token endpoint>",
        "exp": int(time()) + 300 
    }

    additional_headers = {"kid": key_name, "alg": "RS512"}

    # Prepare the JWT to be signed
    encoded_jwt = jwt.encode(
        claims,
        "",  # Empty string for key, as we will sign the JWT using the CryptographyClient
        algorithm="RS512",
        headers=additional_headers
    )

    try:
        # Sign the JWT using the CryptographyClient
        result = crypto_client.sign(SignatureAlgorithm.rs512, encoded_jwt.encode('utf-8'))
        signature = result.signature

        # Append the signature to the JWT to complete the token
        signed_jwt = f"{encoded_jwt}.{signature.decode('utf-8')}"

    except Exception as e:
        logging.error(f"Error signing JWT: {e}")
        raise

    # Return the JWT in the HTTP response
    return func.HttpResponse(signed_jwt, status_code=200)

I have tried copying and pasting the .PEM text (which is Unix formatted, with correct headers (e.g. -----BEGIN PRIVATE KEY----- and at the end -----END PRIVATE KEY-----)) as a Secret, and calling it as a secret in the script, but this doesn't work either.

The function app has a Managed Identity with the Key Vault Crypto Officer and Key Vault Secret Officer roles assigned and uses this to interact with the Key Vault.

Running the current Python code provided, I always get this error in Application Insights:

Result: Failure Exception: ValueError: ('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [<OpenSSLError(code=75497580, lib=9, reason=108, reason_text=no start line)>]) Stack: File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/dispatcher.py", line 491, in _handle__invocation_request call_result = await self._run_async_func( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/dispatcher.py", line 774, in _run_async_func return await ExtensionManager.get_async_invocation_wrapper( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/extension.py", line 147, in get_async_invocation_wrapper result = await function(**args) ^^^^^^^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/Http_createSignedJWT/__init__.py", line 43, in main encoded_jwt = jwt.encode( ^^^^^^^^^^^ File "/home/site/wwwroot/.python_packages/lib/site-packages/jwt/api_jwt.py", line 73, in encode return api_jws.encode( ^^^^^^^^^^^^^^^ File "/home/site/wwwroot/.python_packages/lib/site-packages/jwt/api_jws.py", line 160, in encode key = alg_obj.prepare_key(key) ^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/.python_packages/lib/site-packages/jwt/algorithms.py", line 353, in prepare_key return cast(RSAPublicKey, load_pem_public_key(key_bytes)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/.python_packages/lib/site-packages/cryptography/hazmat/primitives/serialization/base.py", line 35, in load_pem_public_key return ossl.load_pem_public_key(data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/site/wwwroot/.python_packages/lib/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 794, in load_pem_public_key self._handle_key_loading_error() File "/home/site/wwwroot/.python_packages/lib/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 984, in _handle_key_loading_error raise ValueError(
1

There are 1 answers

0
RithwikBojja On

To retrieve .PEM file/key/Certificate i use below code:

import logging
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient
import azure.functions as func


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')
    rithwik_keyVaultURi = "https://keyvaultname.vault.azure.net"
    rith_certificate_name = "name of your certificate"
    creds = DefaultAzureCredential()
    rithwik_CC = CertificateClient(vault_url=rithwik_keyVaultURi, credential=creds)
    rith_certificate = rithwik_CC.get_certificate(rith_certificate_name)
    rith_cer_data = rith_certificate.cer
    print(rith_cer_data)
    return func.HttpResponse(f"Hello Rithwik Certificate Retrieved: {rith_certificate.name}", status_code=200)

Output:

enter image description here