Python: SSL Certificates with OpenSSL
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
xazrad блог
# Создание сертификата
cert = crypto.X509()
cert.get_subject().C = «RU» # указываем свои данные
cert.get_subject().ST = «Tatarstan» # указываем свои данные
cert.get_subject().L = «Naberezhnye Chelny» # указываем свои данные
cert.get_subject().O = «xazrad» # указываем свои данные
cert.get_subject().OU = «xazrad» # указываем свои данные
cert.get_subject().CN = «xazrad.blogspot.com» # указываем свои данные
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60) # срок «жизни» сертификата
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, ‘sha1’)
with open(os.path.join(cert_dir, CERT_FILE), «w») as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
with open(os.path.join(cert_dir, KEY_FILE), «w») as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
2. Создание подписанных сертификатов
# _*_ coding: utf-8 _*_
import os
from OpenSSL import crypto
KEY_FILE = «app.key»
CERT_FILE = «app.crt»
CA_CRT = «CA_CRT.crt» # сертификат, которым производится подпись
CA_KEY = «CA_KEY.key» # ключ, которым производится подпись
def create_signed_cert(cert_dir):
# Загружаем промежуточный сертификат для подписи
ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(CA_CRT).read())
# Загружаем промежуточный ключ, последний параметр пароль
ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, open(CA_KEY).read(), «password»)
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 1024) # размер может быть 2048, 4196
cert = crypto.X509() cert.get_subject().C = «RU» # указываем свои данные
cert.get_subject().ST = «Tatarstan» # указываем свои данные
cert.get_subject().L = «Naberezhnye Chelny» # указываем свои данные
cert.get_subject().O = «xazrad» # указываем свои данные
cert.get_subject().OU = «xazrad» # указываем свои данные
cert.get_subject().CN = «xazrad.blogspot.com» # указываем свои данные
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60) # срок «жизни» сертификата
cert.set_issuer(ca_cert.get_subject())
cert.set_pubkey(k)
cert.sign(ca_key, «sha1»)
with open(os.path.join(cert_dir, CERT_FILE), «w») as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
with open(os.path.join(cert_dir, KEY_FILE), «w») as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
Использование crypto.FILETYPE_ASN1 (вместо crypto.FILETYPE_PEM) возвращает битовое значение сертификатов.