I need to implement certificate-based authentication for web API hosted in app service on Azure. To do this I firstly generated .crt
certificate file and private key .key
file like this:
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout private_key.key -out cert.crt -subj '/CN=myapi.azurewebsites.net' -addext 'subjectAltName=DNS:myapi.azurewebsites.net'
Then I've created certificate in.pfx
format using following command:
openssl pkcs12 -inkey private_key.key -in cert.crt -export -out certificate.pfx
My code for fetching certificate data looks something like this:
from azure.identity import DefaultAzureCredential
from azure.keyvault.certificates import CertificateClient
from OpenSSL import crypto
key_client = CertificateClient(
vault_url=f"https://myteskeyvault.vault.azure.net/",
credential=DefaultAzureCredential(),
)
cert = key_client.get_certificate("certificate")
loaded_cert = crypto.load_pkcs12(
bytes(cert.cer), None
)
I get error on line when I'm trying to load certificate using crypto.load_pkcs12
function:
Traceback (most recent call last):
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 375, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
return await self.app(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\uvicorn\middleware\message_logger.py", line 82, in __call__
raise exc from None
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\uvicorn\middleware\message_logger.py", line 78, in __call__
await self.app(scope, inner_receive, inner_send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\fastapi\applications.py", line 261, in __call__
await super().__call__(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
raise exc
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\middleware\cors.py", line 92, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\middleware\cors.py", line 147, in simple_response
await self.app(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\exceptions.py", line 82, in __call__
raise exc
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in __call__
raise e
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
await self.app(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\routing.py", line 656, in __call__
await route.handle(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\routing.py", line 259, in handle
await self.app(scope, receive, send)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\routing.py", line 61, in app
response = await func(request)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\fastapi\routing.py", line 217, in app
solved_result = await solve_dependencies(
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\fastapi\dependencies\utils.py", line 529, in solve_dependencies
solved = await run_in_threadpool(call, **sub_values)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\anyio\to_thread.py", line 31, in run_sync
return await get_asynclib().run_sync_in_worker_thread(
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\anyio\_backends\_asyncio.py", line 937, in run_sync_in_worker_thread return await future
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\anyio\_backends\_asyncio.py", line 867, in run
result = context.run(func, *args)
File "C:\Users\devdz\Projects\myapp\.\myapp\dependencies.py", line 204, in __call__
api_token = self.__get_api_token(token, settings)
File "C:\Users\devdz\Projects\myapp\.\myapp\dependencies.py", line 149, in __get_api_token
msal_client = settings.aad.get_msal_client_using_certificate()
File "C:\Users\devdz\Projects\myapp\.\myapp\settings\aad.py", line 62, in get_msal_client_using_certificate
private_key = self.az_key_vault.get_private_key()
File "C:\Users\devdz\Projects\myapp\.\myapp\settings\aad.py", line 29, in get_private_key
loaded_cert = crypto.load_pkcs12(
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\OpenSSL\crypto.py", line 3295, in load_pkcs12
_raise_current_error()
File "C:\Users\devdz\Projects\myapp\.venv\lib\site-packages\OpenSSL\_util.py", line 57, in exception_from_error_queue
raise exception_type(errors)
OpenSSL.crypto.Error: [('asn1 encoding routines', '', 'wrong tag'), ('asn1 encoding routines', '', 'nested asn1 error'), ('asn1 encoding routines', '', 'nested asn1 error')]
What am I doing wrong that can cause such error? I've also tried approaches mentioned in this thread but it also did not help me.
I am not wrong, when you get the certificate as a Certificate, using:
cert = key_client.get_certificate("certificate")
You only get the public key part of the certificate, if you want to get it with the private key, then you need to load the certificate as a Secret instead.
Something like:
cert = key_client.get_secret("certificate")
Keyvault returns the certificate in base64 encoded pfx file.