Alex
Alex

Reputation: 31

How to use a client certificate from the Windows certificate store in python?

I want to invoke a web request using a client certificate (public+private key) stored in the Windows certificate store.

With PowerShell my call would look like this (this works):
Invoke-WebRequest -CertificateThumbprint $thumbprint -Uri $uri

Now I am searching for an equivalent in python. I do not want to extract the certificate and pass the file but directly use the store or at least only keep the certificate in memory.

I have tried wincertstore but the certificate lies in the UserStore(cert:\CurrentUser\My) so I cannot access it. Same problem with sslContext.

Installing python-certifi-win32 as mentioned in this answer seems to only load the CA-certificates in order to verify the server, but what I need is a client certificate to verify myself against the server.

Are there any ways other than calling powershell with subprocess to achieve this?
Many thanks in advance.

Upvotes: 1

Views: 2829

Answers (1)

Alex
Alex

Reputation: 31

For anyone with the same problem. I solved it using clr to export the certificate into memory and requests_toolbelt to use it with requests.

Code example to make it work:

import clr
import requests
import requests_toolbelt
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from cryptography.hazmat.backends import default_backend
from requests_toolbelt.adapters.x509 import X509Adapter

clr.AddReference('System')
clr.AddReference('System.Linq')
clr.AddReference('System.Security.Cryptography.X509Certificates')
clr.AddReference('System.Security.Cryptography')

from System.Security.Cryptography.X509Certificates import X509Store, StoreName, StoreLocation,OpenFlags,X509Certificate2Collection,X509FindType,X509Certificate2, X509ContentType
from System.Security.Cryptography import AsymmetricAlgorithm

store = X509Store(StoreName.My, StoreLocation.CurrentUser)
store.Open(OpenFlags.ReadOnly)
user = os.environ['USERNAME']

certCollection = store.Certificates.Find(
                            X509FindType.FindBySubjectName,
                            user,
                            False)
cert = certCollection.get_Item(0)
pkcs12 = cert.Export(X509ContentType.Pkcs12, <passphrase>)

backend = default_backend()
pkcs12_password_bytes = "<password>".encode('utf8')
pycaP12 = load_key_and_certificates(pkcs12, pkcs12_password_bytes, backend)
cert_bytes = pycaP12[1].public_bytes(Encoding.DER)
pk_bytes = pycaP12[0].private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption())
adapter = X509Adapter(max_retries=3, cert_bytes=cert_bytes, pk_bytes=pk_bytes, encoding=Encoding.DER)
session = requests.Session()
session.mount('https://', adapter)
session.get('url', verify=True)

Upvotes: 2

Related Questions