How to create a SSL context using the certificates residing on a smartcard with non-exportable private keys?
Asked Answered
A

0

8

I'm attempting to use the requests library to connect to a server that requires a client certificate be provided. I can get the certificates, but not the private key.

I'm able to extract certificates from the smartcard (using the cryptography library to load the x509 certificate being access through pkcs11) and I'm able to serialize these certificates in the PEM format expected by requests. pkcs11 also provides access to the private keys located on the card; I can use them to perform operations such as signing or decryption. However, as they're marked as non-exportable (which makes sense, they're private keys after all), I can't serialize them to a file to provide when I create the SSL context.

I've seen solutions to this problem written in Java, where a keystore/key manager on the card is accessed, and the key is associated with the context that way, but I have yet to find a way of doing so in Python.

Edit: This is being done on a Windows machine. I have tried to look into using the M2Crypto library as well, however I have not been able to get the library to successfully install (even when using M2CryptoWin64).

import pkcs11
from pkcs11.constants import ObjectClass
from pkcs11.mechanisms import KeyType, Mechanism
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding

lib = pkcs11.lib("path/to/pkcs11/dll")
slots = lib.get_slots(token_present=True)
token = slots[0].get_token()
with token.open(rw=True, user_pin="1234") as session:
    attrs = {
        pkcs11.Attribute.CLASS: ObjectClass.CERTIFICATE
    }
    certs = tuple(session.get_objects(attrs))
    x509cert = x509.load_der_x509_certificate(certs[0][pkcs11.Attribute.VALUE], default_backend())
    with open("cert_file", 'wb') as certfile:
        certfile.write(x509cert.public_bytes(Encoding.PEM))

     r = requests.get("https://www.google.com",
         verify=False,
         cert="cert_file")
     print(r.text)
Alexiaalexin answered 1/8, 2019 at 19:13 Comment(4)
Prob dupe #45386464 #38284945 #41171477 -- but the only claimed answer uses M2Crypto and to me is very unclear.Strappado
I had seen those two posts; the first one is regarding getting the certificate off the card, which I've been able to accomplish, but I agree the second is similar. I am curious if there's any significant difference between a client SSL context (which I'm trying to set up) or a server's context, which appears to be what that questions is more focused on. I do know that I'm able to open the HTTPS connection if I don't attempt to provide a certificate (which allows me to get to sites like Google), but I need to provide a client cert to authenticate with my desired server.Alexiaalexin
There are some differences in OpenSSL (which Python uses) context client vs server, but handling "my-cert[-and-chain]-and-key", which is your issue, is the same. Though since there is not a clear solution for either endpoint, that doesn't help much :-( Worst case, you could go through a proxy that does the clientauth for you, like squid varnish nginx haproxy or even burp charles fiddler etc.Strappado
Are you looking for web client authentication using the Digital Certificate of Client on Smartcard (issued by some Certifying Authority) ? for web application ?Mensa

© 2022 - 2025 — McMap. All rights reserved.