Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTPS connection using PEM Certificate

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?

like image 913
psikais Avatar asked May 05 '11 10:05

psikais


People also ask

Can you curl https?

Curl is a command line tool for transferring data to and from servers. Curl supports over 25+ protocols including HTTP and HTTPS.


2 Answers

It sounds like you need something similar to an answer I have provided before to perform simple client certificate authentication. 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
like image 65
jathanism Avatar answered Oct 14 '22 19:10

jathanism


See http://docs.python.org/library/httplib.html

httplib.HTTPSConnection does not do any verification of the server’s certificate.

The option to include your private certificate is when the server is doing certificate based authentication of clients. I.e. the server is checking the client has a certificate signed by a CA that it trusts and is allowed to access it's resources.


If you don't specify the cert optional argument, you should be able to connect to the HTTPS server, but not validate the server certificate.


Update

Following your comment that you've tried basic auth, it looks like the server still wants you to authenticate using basic auth. Either your credentials are invalid (have you independently verified them?) or your Authenticate header isn't formatted correctly. Modifying your example code to include a basic auth header and an empty post payload:

import httplib  
conn = httplib.HTTPSConnection('10.10.10.10','443')   
auth_header = 'Basic %s' % (":".join(["myusername","mypassword"]).encode('Base64').strip('\r\n'))
conn.request("POST", "/","",{'Authorization':auth_header}) 
response = conn.getresponse()       
print response.status, response.reason
conn.close()
like image 45
MattH Avatar answered Oct 14 '22 19:10

MattH