title: How to create a SSL context using the certificates residing on a smartcard with non-exportable private keys?
body: <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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).</p>
  
  <pre class="lang-py prettyprint-override"><code>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)
  </code></pre>