# sources:
# https://kb.op5.com/pages/viewpage.action?pageId=19073746#sthash.9gTMRKM1.dpbs
# https://stackoverflow.com/a/26093147
# https://jamielinux.com/docs/openssl-certificate-authority/sign-server-and-client-certificates.html
# additional ressource: https://gist.github.com/Soarez/9688998
# TODO: renew certificates and ca, add capability for authentication to client cert
# TODO ressources: https://gist.github.com/richieforeman/3166387
HKP_PATH=""
CLIENT_PATH=""
# Create CA-key and CA-cert
openssl genrsa -out FlexCA.key 2048
openssl req -x509 -new -nodes -key FlexCA.key -sha256 -days 1024 -out FlexCA.pem -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=example.com"
# Create client key & cert signing request
openssl genrsa -out FlexClient.key 2048
openssl req -new -key FlexClient.key -out FlexClient.csr -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=example.com"
# Generate Client certificate
openssl x509 -req -in FlexClient.csr -CA FlexCA.pem -CAkey FlexCA.key -CAcreateserial -out FlexClient.pem -days 1024 -sha256
# //TODO: restart hkp-server
-
-
Save leonletto/cf747cc51c4cfdcd6998800af37d5d4f to your computer and use it in GitHub Desktop.
python mutual tls for client certificate validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# //TODO: for readme: how to create ca & client-certs: | |
# // ---> https://jamielinux.com/docs/openssl-certificate-authority/sign-server-and-client-certificates.html | |
# source: https://www.ajg.id.au/2018/01/01/mutual-tls-with-python-flask-and-werkzeug/ | |
from flask import Flask, render_template, request | |
import werkzeug.serving | |
import ssl | |
import OpenSSL | |
class PeerCertWSGIRequestHandler( werkzeug.serving.WSGIRequestHandler ): | |
""" | |
We subclass this class so that we can gain access to the connection | |
property. self.connection is the underlying client socket. When a TLS | |
connection is established, the underlying socket is an instance of | |
SSLSocket, which in turn exposes the getpeercert() method. | |
The output from that method is what we want to make available elsewhere | |
in the application. | |
""" | |
def make_environ(self): | |
""" | |
The superclass method develops the environ hash that eventually | |
forms part of the Flask request object. | |
We allow the superclass method to run first, then we insert the | |
peer certificate into the hash. That exposes it to us later in | |
the request variable that Flask provides | |
""" | |
environ = super(PeerCertWSGIRequestHandler, self).make_environ() | |
x509_binary = self.connection.getpeercert(True) | |
x509 = OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_ASN1, x509_binary ) | |
environ['peercert'] = x509 | |
return environ | |
app = Flask(__name__) | |
# to establish an SSL socket we need the private key and certificate that | |
# we want to serve to users. | |
# | |
# app_key_password here is None, because the key isn't password protected, | |
# but if yours is protected here's where you place it. | |
app_key = './app.key' | |
app_key_password = None | |
app_cert = './app.crt' | |
# in order to verify client certificates we need the certificate of the | |
# CA that issued the client's certificate. In this example I have a | |
# single certificate, but this could also be a bundle file. | |
ca_cert = './ca.crt' | |
# create_default_context establishes a new SSLContext object that | |
# aligns with the purpose we provide as an argument. Here we provide | |
# Purpose.CLIENT_AUTH, so the SSLContext is set up to handle validation | |
# of client certificates. | |
ssl_context = ssl.create_default_context( purpose=ssl.Purpose.CLIENT_AUTH, | |
cafile=ca_cert ) | |
# load in the certificate and private key for our server to provide to clients. | |
# force the client to provide a certificate. | |
ssl_context.load_cert_chain( certfile=app_cert, keyfile=app_key, password=app_key_password ) | |
ssl_context.verify_mode = ssl.CERT_REQUIRED | |
# now we get into the regular Flask details, except we're passing in the peer certificate | |
# as a variable to the template. | |
@app.route('/') | |
def hello_world(): | |
return render_template('helloworld.html', client_cert=request.environ['peercert']) | |
# start our webserver! | |
if __name__ == "__main__": | |
app.run( ssl_context=ssl_context, request_handler=PeerCertWSGIRequestHandler ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment