Join us and get access to thousands of tutorials and a community of expert Pythonistas.
This lesson is for members only.Join us and get access to thousands of tutorials and a community of expert Pythonistas.
Hosting HTTPS With Flask
00:00In the previous lesson,I showed you how to finish the code to become a Certificate Authority.In this lesson, I’m going to show you how to use the certificategenerated through the CA to host an HTTPS siteusing Flask. To host a web server that uses HTTPSyou need: a signed certificate, configuration for Flask to use the certificate, andconfiguration for your web browser to include your custom Certificate Authorityin its list of Trusted Third Parties.
00:30This is a new copy of the original Flask server,not the one using the Fernet keys.The only difference between this and the original is line 13.The Flask development server is being started with thessl_context parameter.
00:45This tells Flask to serve HTTPS.Thessl_context requires two arguments:the server public key and the server private key.The server public key is the signed certificate issued by the CA to Alice.
01:01The server private key is Alice’s private key that she used to create the CSRfor the CA. Remember that when a private key is created,it’s encrypted with a password.
01:12That’s going to be important in a second. In the lower window,I’m going to start up the server.
01:19And because the private key is being used, Flask asks for a PEM pass phrase.This is the password that was used to encrypt the server private key.Typing it in allows the server to start. It’s running on port5684just like before, in order to be consistent. In a third window,I’m going to hit that withcurl.
01:44Uh-oh, that doesn’t look very good. What’s the problem? Well,by default,curl doesn’t know who the CA is.Charlie’s CA service isn’t incurl’s default list of CAs.
01:57That means you have to tellcurl about Charlie. You can tellcurl about a different CA by passing in the CA’s public key on the command line.
02:07You do that with the--cacert (CA cert) argument.
02:13Well, that still didn’t work, but it’s a different error message this time.This time, it’s complaining that it doesn’t like the hostname'127.0.0.1'.
02:25If you think back to the previous lesson,the CSR included two valid hostnames for the certificate:'localhost' and'alice.example.net'.'127.0.0.1'isn’t one of those valid hostnames, so the certificate doesn’t recognize it.
02:44Even though your computer thinkslocalhostand127.0.0.1 are the same thing,the certificate doesn’t. Most Certificate Authorities refuse to sign certificatesfor IP addresses,so you usually have to have a hostname. Third time’s the charm,this time withlocalhost. Still need the CA’s public key.
03:09And there it is.shhhh, this is a secret. You’ve now successfully served your secret message over HTTPS.One thing to keep in mind is HTTPS only encrypts the channel.
03:22It stops someone from sniffing the contents of the channel,but it doesn’t stop anyone from actually hitting the port.Anyone who’s willing to ignore a browser’s warning about an invalid certificatecan still see the contents hosted on HTTPS.
03:38You would need to combine the ideas from the Fernet code with this code to servea secret message over HTTPS.
03:47Congratulations!You’re now a CA capable of signing your own CSRs and hosting an HTTPS serverwith a self-signed certificate. In the last lesson,I’ll wrap up and show you some shortcuts that you could use to skip past allthis code.
Stuart onApril 5, 2021
curl --cacert ca-public-key.pem https://localhost:5684/
I have followed the entire tutorial with the provided code on my machine up to this point, and everything has run just fine.
Unfortunately this final attempt to hit the server with curl is still returning exit code 60 (invalid certificate).
I have tried:
- generating new keys (PEM files)
- entering the full file path for ca-public-key.pem in the –cacert argument
I would appreciate any suggestions… I was really excited to get this final response from the server.

Bartosz ZaczyńskiRP Team onApril 6, 2021
@Stuart The sample certificate included in the supporting material has already expired. However, generating a new one with the included script doesn’t work for me either. Unfortunately, I’m not sure what the reason is off the top of my head.
You could try creating a self-signed certificate withopenssl as a quick workaround, though:
$opensslreq-x509-newkeyrsa:4096-keyoutserver-private-key.pem-outserver-public-key.pem-days365…and then ignore insecure certificates:
$curl-khttps://localhost:5684/shhhh, this is secretAnyway, we’ll probably need@Christopher’s help with this one.

Christopher TrudeauRP Team onApril 7, 2021
Hi @Stuart,
@Bartosz and I have been plugging away at this, and I think we’ve figured it out. The root certificate generation code needs to change a little bit, I’ll show you how.
The version ofcurl I’m using is a bit older and uses TLS 1.2. The version Bartosz is using (and I suspect you are as well) uses the more up to date TLS 1.3. When I install a new version ofcurl, I get the same problem you are experiencing.
The key to fixing it is inside of the builder code. Currently,make_builder has the following:
builder=(x509.CertificateBuilder().subject_name(subject).issuer_name(issuer).serial_number(x509.random_serial_number()).not_valid_before(valid_from).not_valid_after(valid_to))To get a valid CA certificate you also need another parameter. Change your builder to include:
.add_extension(x509.BasicConstraints(ca=True,path_length=None),critical=True)This change should only be for generating the public CA. In my own code I added ais_ca parameter tomake_builder and toPublicKey.generate. Mymake_builder now looks like this:
defmake_builder(subject,issuer,is_ca=False):# Expire this certificate 30 days from nowvalid_from,valid_to=date_range(3000)# Chain-call certificate builder with necessary parametersbuilder=(x509.CertificateBuilder().subject_name(subject).issuer_name(issuer).serial_number(x509.random_serial_number()).not_valid_before(valid_from).not_valid_after(valid_to))ifis_ca:builder=builder.add_extension(x509.BasicConstraints(ca=True,path_length=None),critical=True)returnbuilderThen you have to make changes toPublicKey andgenerate_keys.py to expose this new parameter. Or, you can cheat add the line by hand, commenting it in and out depending on your needs.
What does all this mean? I’m not 100% sure, but I suspect that either TLS 1.2 didn’t require this parameter, or was more forgiving when it was missing.
We’ll update the course with the new code at some point, but in the meantime, you can add the new.add_extension call and should be good to go.
Thanks for the comment. Hope you enjoyed the course, this bump not withstanding.
Happy coding!
alphafox28js onSept. 3, 2024
Hello,
In what specific module of this course did we create a second password for the CSR. I recall doing so with the Fernet/Cipher segements, but not with the last 3 modules. Are we supposed to use the original we made with the Fernet?

Christopher TrudeauRP Team onSept. 3, 2024
Hi alphafox28js,
If I understand the question you’re asking correctly, covering the creation of the “ca-private-key.pem” file, which requires a password is covered two modules back in “Creating Public and Private Keys”
realpython.com/lessons/public-private-keys/
The actual code shown to generate the file is around the 6:20-6:40 marks.
alphafox28js onSept. 3, 2024
Hello sir,
Oddly enough, I can create the key,have it generate, but I never had to actually put a password in or create one. Whereas in the earlier sections, and for setting up a GitHub account, I actually did have to set the password then type it out.
Become a Member to join the conversation.
Course Contents

