36

I'm calling a REST API with requests in python and so far have been successful when I set verify=False.

Now, I have to use client side cert that I need to import for authentication and I'm getting this error everytime I'm using the cert (.pfx). cert.pfx is password protected.

r = requests.post(url, params=payload, headers=headers, 
                  data=payload, verify='cert.pfx')

This is the error I'm getting:

Traceback (most recent call last):
File "C:\Users\me\Desktop\test.py", line 65, in <module>
r = requests.post(url, params=payload, headers=headers, data=payload, verify=cafile)
File "C:\Python33\lib\site-packages\requests\api.py", line 88, in post
return request('post', url, data=data, **kwargs)
File "C:\Python33\lib\site-packages\requests\api.py", line 44, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Python33\lib\site-packages\requests\sessions.py", line 346, in request
resp = self.send(prep, **send_kwargs)
File "C:\Python33\lib\site-packages\requests\sessions.py", line 449, in send
r = adapter.send(request, **kwargs)
File "C:\Python33\lib\site-packages\requests\adapters.py", line 322, in send
raise SSLError(e)
requests.exceptions.SSLError: unknown error (_ssl.c:2158)

I've also tried openssl to get .pem and key but with .pem and getting SSL: CERTIFICATE_VERIFY_FAILED

Can someone please direct me on how to import the certs and where to place it? I tried searching but still faced with the same issue.

tshepang
  • 12,111
  • 21
  • 91
  • 136
onlyme
  • 457
  • 1
  • 5
  • 8

3 Answers3

81

I had this same problem. The verify parameter refers to the server's certificate. You want the cert parameter to specify your client certificate.

import requests
cert_file_path = "cert.pem"
key_file_path = "key.pem"

url = "https://example.com/resource"
params = {"param_1": "value_1", "param_2": "value_2"}
cert = (cert_file_path, key_file_path)
r = requests.get(url, params=params, cert=cert)
bbayles
  • 4,389
  • 1
  • 26
  • 34
  • 4
    Thank you so much. Sorry didn't respond earlier, but that seemed to fix the issue! Appreciate your response. – onlyme Aug 06 '13 at 19:28
  • 4
    how did you extract cert and key from the .pfx? – ierdna Feb 20 '18 at 15:53
  • 3
    You should not set `verify=False` in most cases as that disables verification of the server's certificate which makes the whole connection insecure and vulnerable to man-in-the-middle attacks. If you are connecting to a server with a certificate that's issued by a well known CA, then don't specify `verify` at all. If you are connecting to a server with a certificate issued by a private/custom CA then you should pass the path to that CA's certificate as the value of the `verify` parameter (if the server's cert is self-signed, then treat the server's cert the same as a CA cert). – kbolino Jan 23 '19 at 18:56
  • 4
    @ierdna .pfx is a synonym for .p12 which is PKCS#12 format, converting to PEM with OpenSSL is covered by [this question](https://stackoverflow.com/questions/15144046) – kbolino Jan 23 '19 at 18:58
7

I had the same problem and to resolve this, I came to know that we have to send RootCA along with certificate and its key as shown below,

response = requests.post(url, data=your_data, cert=('path_client_certificate_file', 'path_certificate_key_file'), verify='path_rootCA')
Ahmad Khan
  • 81
  • 1
  • 2
  • Thanks for this. In hindsight it makes sense that the server would need my root cert to validate CA certificate, but this was the missing piece. – Hethcox Nov 10 '21 at 14:22
0

If it is possible to transfer a certificate directly from the Windows certificate store? From what I understand, the requests library only accepts the file path. are there any adapters or other libraries that make it as easy as requests?

cert_name = "NAME CERT IN CERT STORE"
def export_cert_to_pem(cert, pem_path):
    pem = cert.get_pem()

    with open(pem_path, "w") as pem_file:
        pem_file.write(pem)


def find_cert_in_trusted_roots(cert_name):
    for storename in ("CA", "ROOT"):
        with wincertstore.CertSystemStore(storename) as store:
            for cert in store.itercerts(usage=wincertstore.SERVER_AUTH):
                if cert.get_name().lower() == cert_name.lower():
                    export_cert_to_pem(cert, cert_name + ".pem")

                    #export_cert_to_pem(cert, pem_path)
                    return 1

    return 0




cert=find_cert_in_trusted_roots(cert_name)
response = requests.post(some_url, data=your_data, cert=cert)