Last active
February 22, 2017 19:38
-
-
Save cdgriffith/9dfcc211919a370e81441d7eb0a9a8d4 to your computer and use it in GitHub Desktop.
OpenSSL certificate creation
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
#!/usr/bin/env python | |
# -*- coding: UTF-8 -*- | |
import os | |
import logging | |
from OpenSSL import crypto | |
log = logging.getLogger("x509_cert") | |
class GenError(Exception): | |
"""Error in cert generator""" | |
class X509Cert: | |
def __init__(self, key_file="x509.key", existing_serials=(), key=None): | |
""" | |
Class to help easily create and manage x509 certificates | |
:param key_file: path to existing or new key file | |
:param existing_serials: to make sure ones provided are unique | |
:param key: existing key | |
""" | |
self.existing_serials = list(existing_serials) | |
self.key_file = key_file | |
self.key = key | |
if os.path.exists(self.key_file): | |
log.info("Loading existing key") | |
with open(self.key_file, 'rb') as kf: | |
self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, | |
kf.read()) | |
@staticmethod | |
def generate_key(key_type=crypto.TYPE_RSA, strength=2048): | |
""" | |
Generate a new key | |
:param key_type: either crypto.TYPE_RSA or TYPE_DSA | |
:param strength: suggested 2048 or 4096 | |
:return: key | |
""" | |
key = crypto.PKey() | |
key.generate_key(key_type, strength) | |
return key | |
def generate_cert(self, common_name, key, serial_number, | |
country="US", state="Texas", | |
locality="San Antonio", company="None", | |
organization="None", digest='sha1', | |
start_time_from_now=0, end_time_from_now=365*24*60*60): | |
""" | |
Create a new certificate | |
:param common_name: url of certificate hosting location | |
:param key: key to generate cert off of | |
:param serial_number: unique number for the cert | |
:param country: Country of hosting location | |
:param state: State of hosting location | |
:param locality: City | |
:param company: Company cert is registered too | |
:param organization: Org cert is registered too | |
:param digest: string of type of hash to use | |
:param start_time_from_now: default is now, or 0 | |
:param end_time_from_now: default is one year | |
:return: certificate | |
""" | |
if serial_number in self.existing_serials: | |
raise GenError("Serial number is not unique") | |
cert = crypto.X509() | |
cert.get_subject().C = country | |
cert.get_subject().ST = state | |
cert.get_subject().L = locality | |
cert.get_subject().O = company | |
cert.get_subject().OU = organization | |
cert.get_subject().CN = common_name | |
cert.set_serial_number(serial_number) | |
cert.gmtime_adj_notBefore(start_time_from_now) | |
cert.gmtime_adj_notAfter(end_time_from_now) | |
cert.set_issuer(cert.get_subject()) | |
cert.set_pubkey(key) | |
cert.sign(key, digest) | |
return cert | |
@staticmethod | |
def dump_cert(cert, cert_file="x509.cert", file_type=crypto.FILETYPE_PEM, | |
overwrite=False): | |
""" | |
Write the certificate to a file | |
:param cert: PyOpenSSL cert | |
:param cert_file: path to write certificate too | |
:param file_type: defaults to PEM | |
:param overwrite: overwrite file if it exists | |
""" | |
if os.path.exists(cert_file) and not overwrite: | |
raise GenError("Cert file already exists") | |
with open(cert_file, 'wb') as cf: | |
cf.write(crypto.dump_certificate(file_type, cert)) | |
def dump_key(self, key, file_type=crypto.FILETYPE_PEM, overwrite=False): | |
""" | |
Write the key to a file | |
:param key: PyOpenSSL key | |
:param file_type: defaults to PEM | |
:param overwrite: overwrite file if it exists | |
""" | |
if os.path.exists(self.key_file) and not overwrite: | |
raise GenError("Key file already exists") | |
with open(self.key_file, 'wb') as kf: | |
kf.write(crypto.dump_privatekey(file_type, key)) | |
def create(self, common_name, serial_number, cert_file="x509.cert", | |
key=None, **kwargs): | |
""" | |
Create a new certificate and dump them to a file | |
:param common_name: host of where certificate is used | |
:param serial_number: unique number for the certificate | |
:param cert_file: path to write cert file too | |
:param key: PyOpenSSL key | |
:param kwargs: arguments to pass too generate_cert | |
""" | |
key = key or self.key | |
if not key: | |
log.warning("Generating self-signed key as none was provided") | |
key = self.generate_key() | |
self.dump_key(key) | |
cert = self.generate_cert(common_name=common_name, key=key, | |
serial_number=serial_number, **kwargs) | |
self.dump_cert(cert, cert_file) | |
@staticmethod | |
def cert_details(cert): | |
""" | |
View certificate details | |
:param cert: PyOpenSSL cert | |
:return: cert subject | |
""" | |
return cert.get_subject() | |
if __name__ == '__main__': | |
x = X509Cert() | |
x.create("localhost", 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment