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)
  • 1
    Prob dupe https://stackoverflow.com/questions/45385964/how-to-make-a-tls-request-using-a-smartcard-with-python https://stackoverflow.com/questions/38284445/python-provide-ssl-socket-with-private-key-from-pkcs11-wrapper https://stackoverflow.com/questions/41170977/providing-ssl-connections-in-python-using-pkcs11 -- but the only claimed answer uses M2Crypto _and_ to me is very unclear. – dave_thompson_085 Aug 02 '19 at 00:07
  • 1
    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. – Jonathan Stone Aug 02 '19 at 12:56
  • 1
    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. – dave_thompson_085 Aug 03 '19 at 07:26
  • Are you looking for web client authentication using the Digital Certificate of Client on Smartcard (issued by some Certifying Authority) ? for web application ? – Bharat Vasant Aug 27 '19 at 07:05

0 Answers0