SSL Connection Using .pem Certificate With Python
Question: I'm trying to establish successful communication over an HTTPS connection
using authentication. I'm using Python 2.7 w/ Django 1.4 on Ubuntu 12.04.

The API documentation I'm following has specific requirements for
authentication. Including the `Authentication` header you'll find below and
sending certificate information.

This is the code:

    
    
    import httplib
    import base64
    
    HOST = 'some.host.com'
    API_URL = '/some/api/path'
    
    username = '1234'
    password = '5678'
    
    auth_value = base64.b64encode('WS{0}._.1:{1}'.format(username, password))
    path = os.path.join(os.path.dirname(__file__), 'keys/')
    pem_file = '{0}WS{1}._.1.pem'.format(path, username)
    
    xml_string = '<some><xml></xml><stuff></stuff></some>'
    
    headers = { 'User-Agent'     : 'Rico',
                'Content-type'   : 'text/xml',
                'Authorization'  : 'Basic {0}'.format(auth_value),
              }
    
    
    
    conn = httplib.HTTPSConnection(HOST, cert_file = pem_file)
    conn.putrequest("POST", API_URL, xml_string, headers)
    response = conn.getresponse()
    

I'm getting the following error:

    
    
    Traceback:
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
      111.                         response = callback(request, *callback_args, **callback_kwargs)
    File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
      77.         return view_func(*args, **kwargs)
    File "/home/tokeniz/tokeniz/gateway_interface/views.py" in processPayment
      37.         processCreditCard = ProcessCreditCard(token, postHandling)
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in __init__
      75.             self.processGateway()
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in processGateway
      95.         gateway = Gateway(self)
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in __init__
      37.         self.postInfo()
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in postInfo
      245.         response = conn.getresponse()
    File "/usr/lib/python2.7/httplib.py" in getresponse
      1018.             raise ResponseNotReady()
    
    Exception Type: ResponseNotReady at /processPayment/
    Exception Value: 
    

Why am I getting this error?

UPDATE 1: I've been using the `.pem` file they gave me (Link Point Gateway)
but have read that the certificate file should contain both the certificate
and the RSA private key. Is that correct? I tried to send a `.pem` file
containing both and received the following error:

    
    
        Traceback:
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
      111.                         response = callback(request, *callback_args, **callback_kwargs)
    File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
      77.         return view_func(*args, **kwargs)
    File "/home/tokeniz/tokeniz/gateway_interface/views.py" in processPayment
      37.         processCreditCard = ProcessCreditCard(token, postHandling)
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in __init__
      75.             self.processGateway()
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in processGateway
      95.         gateway = Gateway(self)
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in __init__
      37.         self.postInfo()
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in postInfo
      251.         conn.request('POST', self.API_URL, self.xml_string, headers)
    File "/usr/lib/python2.7/httplib.py" in request
      958.         self._send_request(method, url, body, headers)
    File "/usr/lib/python2.7/httplib.py" in _send_request
      992.         self.endheaders(body)
    File "/usr/lib/python2.7/httplib.py" in endheaders
      954.         self._send_output(message_body)
    File "/usr/lib/python2.7/httplib.py" in _send_output
      814.         self.send(msg)
    File "/usr/lib/python2.7/httplib.py" in send
      776.                 self.connect()
    File "/usr/lib/python2.7/httplib.py" in connect
      1161.             self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
    File "/usr/lib/python2.7/ssl.py" in wrap_socket
      381.                      ciphers=ciphers)
    File "/usr/lib/python2.7/ssl.py" in __init__
      141.                                         ciphers)
    
    Exception Type: SSLError at /processPayment/
    Exception Value: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
    

I can't tell if this is a step forward or backward.

UPDATE 2: I've tried to pass both a certificate file and a key file when
creating the connection object.

    
    
    conn = httplib.HTTPSConnection(HOST, cert_file = pem_file, key_file = key_file)
    

I get the following error:

    
    
    Traceback:
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
      111.                         response = callback(request, *callback_args, **callback_kwargs)
    File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
      77.         return view_func(*args, **kwargs)
    File "/home/tokeniz/tokeniz/gateway_interface/views.py" in processPayment
      37.         processCreditCard = ProcessCreditCard(token, postHandling)
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in __init__
      75.             self.processGateway()
    File "/home/tokeniz/tokeniz/gateway_interface/credit_card_handling.py" in processGateway
      95.         gateway = Gateway(self)
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in __init__
      37.         self.postInfo()
    File "/home/tokeniz/tokeniz/gateway_interface/first_data.py" in postInfo
      252.         conn.request('POST', self.API_URL, self.xml_string, headers)
    File "/usr/lib/python2.7/httplib.py" in request
      958.         self._send_request(method, url, body, headers)
    File "/usr/lib/python2.7/httplib.py" in _send_request
      992.         self.endheaders(body)
    File "/usr/lib/python2.7/httplib.py" in endheaders
      954.         self._send_output(message_body)
    File "/usr/lib/python2.7/httplib.py" in _send_output
      814.         self.send(msg)
    File "/usr/lib/python2.7/httplib.py" in send
      776.                 self.connect()
    File "/usr/lib/python2.7/httplib.py" in connect
      1161.             self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
    File "/usr/lib/python2.7/ssl.py" in wrap_socket
      381.                      ciphers=ciphers)
    File "/usr/lib/python2.7/ssl.py" in __init__
      141.                                         ciphers)
    
    Exception Type: SSLError at /processPayment/
    Exception Value: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
    

If I try and combine the certificate file and key file and send it as the
certificate argument I receive the same error.


Answer: It would seem that some of the "higher level" libraries in Python are not
equipped to handle this kind of authentication connection. After many days of
attempts I was finally able to come up with a solution going down to the
socket level.

    
    
    host = 'some.host.com'
    service = '/some/api/path'
    port = 443
    
    # Build the authorization info
    username = '1234'
    password = '5678'
    path = '/path/to/key/files/'
    pem_file = '{0}WS{1}._.1.pem'.format(path, username)
    key_file = '{0}WS{1}._.1.key'.format(path, username)
    auth = base64.b64encode('WS{0}._.1:{1}'.format(username, password))
    
    ## Create the header
    http_header = "POST {0} HTTP/1.0\nHost: {1}\nContent-Type: text/xml\nAuthorization: Basic {2}\nContent-Length: {3}\n\n"
    req = http_header.format(service, host, auth, len(xml_string)) + xml_string
    
    ## Create the socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    conn = ssl.wrap_socket(sock, keyfile = key_file, certfile = pem_file)
    conn.connect((host, port))
    conn.send(req)
    
    response = ''
    while True:
        resp = conn.recv()
        response += resp
    
        if (resp == ''):
            break
    

I hope someone finds this useful. This particular implementation was to
interface with Link Point Gateway with Python (not supported by First Data
(Link Point)). Talk about a nightmare this whole project has been. haha

Anyway, for anyone having trouble using Link Point please be advised that the
`.key` file they provide you is not sufficient for creating a connection in
Python.

`openssl rsa -in orig_file.key -out new_file.key`

Then use the `new_file.key` instead.

