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(
To retrieve .PEM file/key/Certificate i use below code:
Output: