简体   繁体   中英

How can I do certificate pinning for TLS sockets in python 2.7?

I have a python TLS server using a self signed certificate. That works. The code looks like this for now:

#!/usr/bin/python

import socket, ssl

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="server.crt", keyfile="server.key")

bindsocket = socket.socket()
bindsocket.bind(('127.0.0.1', 8888))
bindsocket.listen(5)

while True:
    newsocket, fromaddr = bindsocket.accept()
    connstream = context.wrap_socket(newsocket, server_side=True)
    try:
        print("Got connection!")
    finally:
        connstream.shutdown(socket.SHUT_RDWR)
        connstream.close()

I am now trying to make a client in python that connects to this server. On the first connection attempt, I'd like to retrieve either the public key, or a hash of the public key, and then verify this on all future connections. How can I do with with python and the ssl package?

This is the code I'm playing with:

#!/usr/bin/python

import ssl, socket, pprint

context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False


cnx = context.wrap_socket(socket.socket(socket.AF_INET), certfile="server.crt")
cnx.connect(('127.0.0.1', 8888))
pprint.pprint(cnx.getpeercert())

As it stands right now, it fails because there is no certificate chain to verify the cert. I don't care about that, though. All I care about is that the server I'm talking to has the private key that matches the public key. What do I do?

So I've made this work, though it's not exactly what I hoped for. My biggest complaint is that it requires storing the entire server certificate in a file on disk, whereas really all I care about is the public key. However, here's what I have working:

#!/usr/bin/python

import ssl, socket, pprint, os, sys

# If we haven't retrieved the certificate previously...
if not os.path.isfile('server_cert.pem'):

    # Grab the cert
    cert = ssl.get_server_certificate(('127.0.0.1', 8888))

    # If it worked...
    if cert:

        # Write it to a file
        with open('server_cert.pem', 'w') as f:
            f.write(cert)
    else:
        sys.exit()

# Prepare context, including reference to the server's certificate
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False
context.load_verify_locations(cafile='server_cert.pem')
cnx = context.wrap_socket(socket.socket(socket.AF_INET))

# Connect and evaluate
cnx.connect(('127.0.0.1', 8888))

I'll wait a bit to mark this answer accepted to see if someone can find a cleaner way of doing this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM