HTTPS connection using PEM Certificate
Question: I'm trying to POST HTTPS requests using a PEM certificate like following:

    
    
    import httplib  
    CERT_FILE = '/path/certif.pem'
    conn = httplib.HTTPSConnection('10.10.10.10','443', cert_file =CERT_FILE)   
    conn.request("POST", "/") 
    response = conn.getresponse()       
    print response.status, response.reason
    conn.close()
    

I have the following error:

    
    
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/usr/lib/python2.6/httplib.py", line 914, in request
    self._send_request(method, url, body, headers)
    File "/usr/lib/python2.6/httplib.py", line 951, in _send_request
    self.endheaders()
    File "/usr/lib/python2.6/httplib.py", line 908, in endheaders
    self._send_output()
    File "/usr/lib/python2.6/httplib.py", line 780, in _send_output
    self.send(msg)
    File "/usr/lib/python2.6/httplib.py", line 739, in send
    self.connect()
    File "/usr/lib/python2.6/httplib.py", line 1116, in connect
    self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
    File "/usr/lib/python2.6/ssl.py", line 338, in wrap_socket
    suppress_ragged_eofs=suppress_ragged_eofs)
    File "/usr/lib/python2.6/ssl.py", line 118, in __init__
    cert_reqs, ssl_version, ca_certs)
    ssl.SSLError: [Errno 336265225] _ssl.c:339: error:140B0009:SSL       
    routines:**SSL_CTX_use_PrivateKey_file**:PEM lib
    

When I remove the cert_file from httplib, I've the following response:

    
    
    200 ok
    

When I add the Authentication header (like advised by MattH) with empty post
payload, it works also.

However, when I put the good request with the Path, the Body and the Header,
like following (I simplified them...)

    
    
    body = '<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">blablabla</S:Envelope>'
    URLprov = "/syncaxis2/services/XXXsyncService"
    auth_header = 'Basic %s' %  (":".join(["xxx","xxxxx"]).encode('Base64').strip('\r\n'))
    conn.request("POST",URLprov,body,{'Authenticate':auth_header})
    

I have 401 Unauthorized response !

As you can see, first, I'm asked to provide the PrivateKey ! why did I need
the PrivateKey if I'm a client ? then, when I remove the PrivateKey and the
certificate, and put the Path/Body/headers I have 401 Unauthorized error with
the message WWW-Authenticate: Basic realm="SYNCNB Server Realm".

Could any one explain this issue? Is there another way to send HTTPS request
using a certificate in Python?


Answer: It sounds like you need something similar to an answer I have provided before
to perform [simple client certificate
authentication](http://stackoverflow.com/questions/5700289/using-pyopenssl-to-
create-urllib-custom-opener/5707951#5707951). Here is the code for convenience
modified slightly for your question:

    
    
    import httplib
    import urllib2
    
    PEM_FILE = '/path/certif.pem' # Renamed from PEM_FILE to avoid confusion
    CLIENT_CERT_FILE = '/path/clientcert.p12' # This is your client cert!
    
    # HTTPS Client Auth solution for urllib2, inspired by
    # http://bugs.python.org/issue3466
    # and improved by David Norton of Three Pillar Software. In this
    # implementation, we use properties passed in rather than static module
    # fields.
    class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
        def __init__(self, key, cert):
            urllib2.HTTPSHandler.__init__(self)
            self.key = key
            self.cert = cert
        def https_open(self, req):
            #Rather than pass in a reference to a connection class, we pass in
            # a reference to a function which, for all intents and purposes,
            # will behave as a constructor
            return self.do_open(self.getConnection, req)
        def getConnection(self, host):
            return httplib.HTTPSConnection(host, key_file=self.key, cert_file=self.cert)
    
    
    cert_handler = HTTPSClientAuthHandler(PEM_FILE, CLIENT_CERT_FILE)
    opener = urllib2.build_opener(cert_handler)
    urllib2.install_opener(opener)
    
    f = urllib2.urlopen("https://10.10.10.10")
    print f.code
    

