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: "[email protected]" # 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










