OpenSSL python library extends all the functions of OpenSSL into python, such as creation and verification of CSR/Certificates. In this post, we present a simple utility in python to Create CSR & Self Signed Certificates in commonly used key formats namely PEM, DER, PFX or P12.
We will have this built in such a way that all the configurations needed to generate CSR/Keys/Cert can be configured in a yaml template (Config.yaml). The path to yaml template can be provided as an argument at the time of instantiation, as in the following example.

Usage
Files
├── Config.yaml
├── Gen_CA.py
└── test.py
Gen_CA.py (OpenSSL wrapper)
from OpenSSL import crypto import os import re import yaml from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, FILETYPE_PEM, load_certificate_request, PKCS12, FILETYPE_ASN1, load_privatekey, X509Req class Secure: def __init__(self, ConfigFile): self.ConfigFile = ConfigFile CertConfig = self.ParseConfig() if CertConfig['CERT'].get('KeyType') == 'RSA': self.KeyType = TYPE_RSA elif CertConfig['CERT'].get('KeyType') == 'DSA': self.KeyType = TYPE_DSA self.CsrFile = CertConfig['CSR'].get('OldCsrFile') self.OldCsrFileType = CertConfig['CSR'].get('OldCsrFileType') self.BitLength = CertConfig['CERT'].get('BitLength') self.digestType = CertConfig['CERT'].get('digestType') self.CertDir = CertConfig['CERT'].get('CertDir') self.set_notBefore = CertConfig['CSR'].get('validfrom') self.set_notAfter = CertConfig['CSR'].get('validto') self.OldPrivateKey = CertConfig['REUSE'].get('OldPrivateKey') self.OldPrivateKeyType = CertConfig['REUSE'].get('OldPrivateKeyType') self.Certificate = CertConfig['REUSE'].get('Certificate') self.validfrom = CertConfig['CERT'].get('validfrom') self.validto = CertConfig['CERT'].get('validto') def ParseConfig(self): with open(self.ConfigFile) as Config: try: CertConfig = yaml.safe_load(Config) except Exception as ConfigException: print("Failed to read Configuration %s" %(ConfigException)) return CertConfig def GetPrivateKey(self): if not self.OldPrivateKey: Key = crypto.PKey() Key.generate_key(self.KeyType, self.BitLength) else: with open(self.OldPrivateKey) as KeyFile: if self.OldPrivateKeyType == 'PEM': Key = load_privatekey(FILETYPE_PEM, KeyFile.read()) elif self.OldPrivateKeyType == 'DER': Key = load_privatekey(FILETYPE_ASN1, KeyFile.read()) return Key def CreateCsr(self, Key): if not self.CsrFile: CertConfig = self.ParseConfig() Csr = X509Req() Csr.get_subject().commonName = CertConfig['CSR'].get('commonName') Csr.get_subject().stateOrProvinceName = CertConfig['CSR'].get('stateOrProvinceName') Csr.get_subject().localityName = CertConfig['CSR'].get('localityName') Csr.get_subject().organizationName = CertConfig['CSR'].get('organizationName') Csr.get_subject().organizationalUnitName = CertConfig['CSR'].get('organizationalUnitName') Csr.get_subject().emailAddress = CertConfig['CSR'].get('emailAddress') Csr.get_subject().countryName = CertConfig['CSR'].get('countryName') Csr.set_pubkey(Key) Csr.sign(Key, self.digestType) else: with open(self.CsrFile) as CsrFile: if self.OldCsrFileType == 'PEM': Csr = load_certificate_request(FILETYPE_PEM, CsrFile.read()) elif self.OldCsrFileType == 'DER': Csr = load_certificate_request(FILETYPE_ASN1, CsrFile.read()) else: raise TypeError("Unknown Certificate Type %s" %(self.OldCsrFileType)) return Csr def CreateCert(self, Csr, Key): Cert = crypto.X509() Cert.get_subject().commonName = Csr.get_subject().commonName Cert.get_subject().stateOrProvinceName = Csr.get_subject().stateOrProvinceName Cert.get_subject().localityName = Csr.get_subject().localityName Cert.get_subject().organizationName = Csr.get_subject().organizationName Cert.get_subject().organizationalUnitName = Csr.get_subject().organizationalUnitName Cert.get_subject().emailAddress = Csr.get_subject().emailAddress Cert.get_subject().countryName = Csr.get_subject().countryName if self.validfrom and self.validto: Cert.set_notBefore(self.validfrom) Cert.set_notAfter(self.validto) Cert.set_pubkey(Key) Cert.sign(Key, self.digestType) return Cert def CreateP12(self, Key, Cert, p12File, passphrase=None): p12 = PKCS12() p12.set_certificate(Cert) p12.set_privatekey(Key) p12File.write(p12.export(passphrase = passphrase)) def DumpKeyCertCsr(self): Key = self.GetPrivateKey() Csr = self.CreateCsr(Key) Cert = self.CreateCert(Csr, Key) cn = Csr.get_subject().commonName cn = re.sub(' +', '_', cn) #Files in PEM format PemKeyPath = os.path.join(self.CertDir, cn + "_pkey.pem") PemCsrPath = os.path.join(self.CertDir, cn + "_csr.pem") PemCertPath = os.path.join(self.CertDir, cn + "_cert.pem") if not self.OldPrivateKey: with open(PemKeyPath, "w") as fPemPrivKey: fPemPrivKey.write(crypto.dump_privatekey(FILETYPE_PEM, Key)) if not self.CsrFile: with open(PemCsrPath, "w") as fPemcsr: fPemcsr.write(crypto.dump_certificate_request(FILETYPE_PEM, Csr)) with open(PemCertPath, "w") as fPemcert: fPemcert.write(crypto.dump_certificate(FILETYPE_PEM, Cert)) #Files in DER (a.k.a ASN1) format DerKeyPath = os.path.join(self.CertDir, cn + "_pkey.der") DerCsrPath = os.path.join(self.CertDir, cn + "_csr.der") DerCertPath = os.path.join(self.CertDir, cn + "_cert.der") if not self.OldPrivateKey: with open(DerKeyPath, "w") as fDerPrivKey: fDerPrivKey.write(crypto.dump_privatekey(FILETYPE_ASN1, Key)) if not self.CsrFile: with open(DerCsrPath, "w") as fDercsr: fDercsr.write(crypto.dump_certificate_request(FILETYPE_ASN1, Csr)) with open(DerCertPath, "w") as fDercert: fDercert.write(crypto.dump_certificate(FILETYPE_ASN1, Cert)) #Files in p12/PFX format P12CertPath = os.path.join(self.CertDir, cn + "_cert.p12") fP12cert = open(P12CertPath, "w") self.CreateP12(Key, Cert, fP12cert)
Certificate Configuration
Config.yaml, sample configuration.
--- # # # # # # # # # # # # # # # # # # # # # # # # # # This file is used to specify configurations # # for creating/renewing Certificates. # # Always specify full path wherever applicable. # # # # # # # # # # # # # # # # # # # # # # # # # # # CSR Configuration # CSR configurations will be ignored, when an # existing CSR path is provided in 'OldCsrFile' CSR: commonName: "unixutils" # Mandatory if 'OldCsrFile' is not used stateOrProvinceName: "MyState" # Mandatory if 'OldCsrFile' is not used localityName: "MyLocality " # Mandatory if 'OldCsrFile' is not used organizationName: "unixutils" # Mandatory if 'OldCsrFile' is not used organizationalUnitName: "Certification Authority" # Mandatory if 'OldCsrFile' is not used emailAddress: "admin@unixutils.com" # Mandatory if 'OldCsrFile' is not used countryName: "IN" # Mandatory if 'OldCsrFile' is not used OldCsrFile: # Optional OldCsrFileType: # Mandatory IF 'OldCsrFile' is used, example: "PEM" # CERT Configuration # The followin configurations are applied on # the generated certificate. # validty: Date Format: YYYYMMDDhhmmssZ (suffixed with 'Z') CERT: KeyType: RSA # Mandatory BitLength: 2048 # Mandatory digestType: 'sha256' # Mandatory CertDir: '/home/admin/mysslcerts' # Mandatory validfrom: "20200101000000Z" # Mandatory validto: "20210101000000Z" # Mandatory # CREATE CERTIFICATE WITH OLD PRIVATE KEY REUSE: OldPrivateKey: # Optional OldPrivateKeyType: # Mandatory IF OldPrivateKey is used, example: "PEM"
Test script
Generate certificates from Configuration.
from Gen_CA import Secure x = Secure('Config.yaml') x.DumpKeyCertCsr()
With this being run, you should be able to see the CSR, Private Key and Certificate in the intended formats under the path defined as ‘CertDir’ in Config.yaml