Search code examples
python-requestsclient-certificates

Python client certicate request fails on Ubuntu


I have the following minimal file:

import requests
index_response = requests.get("https://my.end.point.com/", cert="mypem.pem")
print(index_response.status_code)
  • Windows Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32 gives a response of 200
  • Ubuntu Python 2.7.15 (default, Jun 3 2019, 14:08:52) [GCC 4.8.4] on linux2 gives a response of 403

Both use Python 2.7.15 and there's no error displayed, so I’m not clear on where to start looking. I appreciate this isn’t an easily reproducible example; if there are any example PEM/endpoints I can use, I’d be happy to edit my question.


Solution

  • First, as @wowkin2 mentioned, make sure you use the same versions of requests and urllib3 on your two computers. I'm using requests==2.22.0 and urllib3==1.25.7 here.

    And please consider switching to Python 3, Python 2 support ends in January 2020.

    OK, so if you have the same versions but still see the issue, let's try to debug it by first using another client certificate. badssl.com is a TLS test service who provides such a client certificate. Testing with badssl.com is more complicated than it should be because it uses a password and requests does not support that out of the box, but here's how you can do it:

    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.poolmanager import PoolManager
    
    
    class ClientCertPasswordAdapter(HTTPAdapter):
        def __init__(self, password):
            self.password = password
            super(ClientCertPasswordAdapter, self).__init__()
    
        def init_poolmanager(self, connections, maxsize, block=False):
            self.poolmanager = PoolManager(
                num_pools=connections,
                maxsize=maxsize,
                block=block,
                key_password=self.password,
            )
    
    
    print("fetching client certificate")
    with open("client.pem", "wb") as f:
        r = requests.get("https://badssl.com/certs/badssl.com-client.pem")
        f.write(r.content)
    
    print("requesting data")
    s = requests.Session()
    s.mount("https://client.badssl.com/", ClientCertPasswordAdapter("badssl.com"))
    r = s.get("https://client.badssl.com/", cert="client.pem")
    print(r.status_code)
    

    Does this print 200 for your two OSes? This would allow to rule out an issue in your certificate.

    Another test you can make is to use your client certificate and give it to a server that won't verify it but will print you the public information it received. I'm sending the badssl.com client certificate here:

    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.poolmanager import PoolManager
    
    
    class ClientCertPasswordAdapter(HTTPAdapter):
        def __init__(self, password):
            self.password = password
            super(ClientCertPasswordAdapter, self).__init__()
    
        def init_poolmanager(self, connections, maxsize, block=False):
            self.poolmanager = PoolManager(
                num_pools=connections,
                maxsize=maxsize,
                block=block,
                key_password=self.password,
            )
    
    
    print("fetching client certificate")
    with open("client.pem", "wb") as f:
        r = requests.get("https://badssl.com/certs/badssl.com-client.pem")
        f.write(r.content)
    
    print("requesting data")
    s = requests.Session()
    s.mount("https://server.cryptomix.com/", ClientCertPasswordAdapter("badssl.com"))
    r = s.get("https://server.cryptomix.com/secure/", cert="client.pem")
    print(r.status_code)
    print(r.text)
    

    This should print debug information including SSL_CLIENT information:

    [SSL_CLIENT_S_DN_C] => US
    [SSL_CLIENT_S_DN_ST] => California
    [SSL_CLIENT_S_DN_L] => San Francisco
    [SSL_CLIENT_S_DN_O] => BadSSL
    [SSL_CLIENT_S_DN_CN] => BadSSL Client Certificate
    [SSL_CLIENT_I_DN_C] => US
    [SSL_CLIENT_I_DN_ST] => California
    [SSL_CLIENT_I_DN_L] => San Francisco
    [SSL_CLIENT_I_DN_O] => BadSSL
    [SSL_CLIENT_I_DN_CN] => BadSSL Client Root Certificate Authority
    [SSL_CLIENT_VERIFY] => GENEROUS
    [SSL_CLIENT_M_VERSION] => 3
    [SSL_CLIENT_M_SERIAL] => 2B936CE32D82CE8B01FD9A0595AC6366AA014C82
    [SSL_CLIENT_V_START] => Nov 27 00:19:57 2019 GMT
    [SSL_CLIENT_V_END] => Nov 26 00:19:57 2021 GMT
    [SSL_CLIENT_V_REMAIN] => 730
    [SSL_CLIENT_S_DN] => CN=BadSSL Client Certificate,O=BadSSL,L=San Francisco,ST=California,C=US
    [SSL_CLIENT_I_DN] => CN=BadSSL Client Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
    [SSL_CLIENT_A_KEY] => rsaEncryption
    [SSL_CLIENT_A_SIG] => sha256WithRSAEncryption
    [SSL_CLIENT_CERT_RFC4523_CEA] => { serialNumber 248774298121081469895139733087446207053965642882, issuer rdnSequence:"CN=BadSSL Client Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US" }
    

    Do you see the same values with your two OSes?