3

I am trying to POST xml to a site using python. I have to include a certificate but am unsure of how to do this. Is it enough to specify the file path of the certificate locally on my computer?

Can anybody show my an example of how to include the certificate in the request?

import http.client, urllib.parse

xml="""<?xml version="1.0" encoding="UTF-8"?>
<home>
  <bathroom>1</bathroom>
  <kitchen>1</kitchen>
  <street>515</street>
</home>);"""

headers = {"username": "password"}

conn = http.client.HTTPSConnection("someurl.com", cert_file="D:\Users\Username\certificate.p12")
conn.request("POST", "/to/this/place", xml, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data)
conn.close()
dagrun
  • 621
  • 3
  • 11
  • 29

2 Answers2

10

I used this to avoid having clients do the pkcs12 to PEM conversion. Basically override the context class to allow it to load pkcs12 files. This works with python3

import requests
from OpenSSL import crypto

class Pkcs12Context(requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context):
    def __init__(self, method):
        super().__init__(method)
        p12 = crypto.load_pkcs12(open("/certs/client.p12", 'rb').read(), 'password')
        self.use_certificate(p12.get_certificate())
        self.use_privatekey(p12.get_privatekey())

# Monkey-patch the subclass into OpenSSL.SSL so it is used in place of the stock version
requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context = Pkcs12Context

r = requests.get('https://example.com')

print(r.text)
cloudpta
  • 134
  • 1
  • 6
  • This is great. When I export pem files I have to export everything (not just clcerts) or get a unknonw CA error. Similarly if I use your wrapper I get the error [('PEM routines', 'get_name', 'no start line'), ('SSL routines', 'use_certificate_chain_file', 'PEM lib')] . I tried for ca in p12.get_ca_certificates(): self.add_extra_chain_cert(ca) but it did not help. – dashesy Feb 13 '21 at 00:07
  • I am glad it helped. What exactly was the problem? Maybe post some code i can try to run? – cloudpta Feb 14 '21 at 07:23
  • Have you figured out a better way to do this? – RodriKing Jul 16 '21 at 16:31
  • I havent tried it in a while but it uses openssl underneath and fundamentally openssl prefers everything in PEM format so I doubt there is a cleverer way but always happy to learn if there is – cloudpta Jul 29 '21 at 05:55
  • 1
    ```Traceback (most recent call last): File "hello.py", line 4, in class Pkcs12Context(requests.packages.urllib3.contrib.pyopenssl.OpenSSL.SSL.Context): AttributeError: module 'urllib3.contrib' has no attribute 'pyopenssl' ```I am getting this erro – Vimal Raj Dec 02 '21 at 10:12
6

If you aren't using self-signed certs and your cert is signed by a relatively trustworthy authority (i.e. to a normal someurl.com on the internet) then you should use the system CA certificates when making your connection.

You can do that by just invoking the HTTPSConnection() without giving it a cert file or context in Python 3.4.3 or greater.

If you are using self-signed certificates then yes you can load the certs from the local hard drive. You will need to have the private key included in the certificate file or specified as the key file. You will also want to do host verification if it is at all possible in your environment. See the verify_mode and check_hostname options.

import http.client
import ssl

password = input("Key password (enter for none):") or None

xml = """<?xml version="1.0" encoding="UTF-8"?>
<home>
  <bathroom>1</bathroom>
  <kitchen>1</kitchen>
  <street>515</street>
</home>);"""

headers = {"username": "password"}

context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain("cert.pem", "key.pem", password=password)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

conn = http.client.HTTPSConnection("someurl.com", port=443, context=context)

conn.request("POST", "/to/this/place")
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data)
conn.close()
Ian Lewis
  • 917
  • 1
  • 7
  • 17