# 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
Last active
February 2, 2024 20:44
-
-
Save nebulak/6d865ddd768fb905a562d6026cdd508a 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 ) |
In these lines:
# 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
This means that python checks the client signature, i.e., it checks if the client actually has the private key in question. Right?
Yes, that's right. You may also check the source which contains a blog post with explanations here:
https://www.ajg.id.au/2018/01/01/mutual-tls-with-python-flask-and-werkzeug/
WOW! this was everything I needed! Thanks!
God save the person who snapshotted this page into the archive! https://web.archive.org/web/20210928235937/https://www.ajg.id.au/2018/01/01/mutual-tls-with-python-flask-and-werkzeug/
Hi, sorry for boring you, is it possible to print out client certificate to the web page?
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.
Thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How i decode OpenSSL.crypto.x509 object inside template??