Creating a Self-Signed Certificate With Python

E

Eric Hansen

Guest
Writing a web application in Python is easily frustrating, but what’s more troublesome is requiring a self-signed certificate for HTTPS. If you’re only using it for yourself then its easy, but if you want to allow anyone on the Internet (or clients) to deploy it, it causes a bit more of a troublesome stir. While it doesn’t take a long time to generate one, not even I really feel like creating a self-signed certificate for an application I have to deploy and test. So, lets make it possible to do it in Python!

Required
Python 2.7 is used for this. Beyond that, PyOpenSSL (Python’s bindings to the OpenSSL library) is required as well, which installing is out of the scope of this guide (note: use pip install pyopenssl). Some knowledge and/or experience with Python or similar would be nice as I’m not going to explain every aspect of the code, but besides that you should be set! I tested this on Ubuntu 13.04 and had no issues.

Code
Since I’ve never done an article like this before, going to try something out. I’ll be explaining the Python code that’s necessary to do this. If this isn’t very effective (or if it is for that matter) please leave a post in the comments to let me know.
Code:
from OpenSSL import crypto, SSL
from socket import gethostname
from pprint import pprint
from time import gmtime, mktime
from os.path import exists, join
We want to include only specific parts of Python’s various libraries (OpenSSL = PyOpenSSL), which saves us on overhead since Python is an interpreted language.
Code:
CN = raw_input("Input the hostname of the website the certificate is for: ")
CERT_FILE = "%s.crt" % CN
KEY_FILE = "%s.key" % CN
Since the CN is to match the hostname of the HTTP header, this is part of the dynamic aspect of creating our self-signed certificate. The key and certificate files will also be <CN>.crt/.key.
Code:
def create_self_signed_cert(cert_dir=”.”):
C_F = join(cert_dir, CERT_FILE)
K_F = join(cert_dir, KEY_FILE)

if not exists(C_F) or not exists(K_F):
The start of our method for creating a self-signed certificate. We pass it the directory we want to store our certificate and key (defaults to current directory), and then we specify the full path of the certificate and key file (C_F & K_F), and check if both exists. If one or the other (or both) don’t, then we continue on. Rest of the code will be inside the if block.
Code:
 # create a key pair
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 1024)
Create an instance of generating a key-pair with PyOpenSSL and tell it we want a 1024-bit RSA key pair.
Code:
 # create a self-signed cert
cert = crypto.X509()
SSL certificates are X-509, so we need a reference to load the appropriate data, which we will manipulate next!
Code:
 cert.get_subject().C = raw_input("Country: ")
cert.get_subject().ST = raw_input("State: ")
cert.get_subject().L = raw_input("City: ")
cert.get_subject().O = raw_input("Organization: ")
cert.get_subject().OU = raw_input("Organizational Unit: ")
cert.get_subject().CN = CN
Similar to using the openssl command to generate a self-signed certificate. Unfortunately there doesn’t seem to be a way (according to documents) on how to parse the OpenSSL configuration file (/etc/ssl/openssl.cnf). So, for now, we have to prompt the user.
Code:
 cert.set_serial_number(1000)
Sets the serial number of the certificate. Should be incremented each time the certificate is renewed.
Code:
 cert.gmtime_adj_notBefore(0)
States that the certificate is not valid before present time (basically once the certificate is made it shouldn’t be valid prior to that point in time).
Code:
 cert.gmtime_adj_notAfter(315360000)
This amounts to 3,650 days and says that the certificate is not valid after 3,650 days after its created.
Code:
 cert.set_issuer(cert.get_subject())
States that the information provided above (where you’re prompted for location information) is the issuer of the certificate. In a self-signed certificate this would state that the person you bought the certificate from is the issuer, but since we made it ourselves we’re the issuer!
Code:
 cert.set_pubkey(k)
Remember creating the 1024-bit RSA key? This is how we establish how to encrypt the data. You can pass the key file (.key) for anything that needs to validate a connection to the server, but the certificate (.crt) must remain private.
Code:
 cert.sign(k, 'sha1')
Sign the key with the public key using SHA-1 hash.
Code:
 open(C_F, "wt").write(
crypto.dump_certificate(crypto.FILETYPE_PEM, cert))

open(K_F, "wt").write(
crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
These two lines open up the certificate and key files for writing text to and then we tell OpenSSL to dump the certificate and key information.
Getting the Public Key
In order to let others authenticate with the self-signed certificate you need to give the public key. But how do we do that? Run this command in the console (assuming same directory where the key file is):
Code:
openssl rsa -in some.key -pubout -out pubkey.key
Replace "some.key" with the name of the key file and give away the pubkey.key file. This way people can authenticate SSL sessions!
 

Attachments

  • slide.jpg
    slide.jpg
    85.9 KB · Views: 118,942
Last edited:


A small thing: note that the code snippets are not shown as valid Python (maybe talk to linux-org admins to fix that).

E.g.,
def create_self_signed_cert(cert_dir=”.”):
C_F = join(cert_dir, CERT_FILE)
K_F = join(cert_dir, KEY_FILE)


Should have been:
def create_self_signed_cert(cert_dir=”.”):
....C_F = join(cert_dir, CERT_FILE)
....K_F = join(cert_dir, KEY_FILE)
 
It's a matter of taste, I guess, but usually one first sees introduction of what the code will do, then the code (and only if explanations could not be written before the code stanza, write them after the code).
 
this:

Remember creating the 1024-bit RSA key? This is how we establish how to encrypt the data. You can pass the key file (.key) for anything that needs to validate a connection to the server, but the certificate (.crt) must remain private.

is backwards. You need to keep the private key PRIVATE. you can share the CRT file (public portion of the certificate) with anyone that needs to validate. In the case of a CA signed certificate (not self-signed) you can even chain the CA intermediate and root certs as well.

...sorry for replying to such an old thread, but if people are going to use it as a reference, it should be corrected.
 


Latest posts

Top