#!/usr/bin/python3 # # script to create certificate use for test # # Copyright (C) 2017 AleaJactaEst # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Generate all certificates CA certificate (emulate CA certificate) Appli certification (our global certificate, use to sign our key) Server certificate (our key & certificate for server side) Client certificate (our key & certificate for server side) CA \ Apppli / \ Server Client Example command to generate all certificate : create_certificate.py --show-log-console --log debug """ import argparse import logging import logging.config import os import stat import subprocess import sys class Certificate: def __init__(self, openssl, workdir_cert_ca, workdir_cert_appli, passca='OpenNelCA9439', passappli='OpenNelAPPLI1097', countryName='FR', stateOrProvinceName='France', localityName='Paris', organizationName='khanat', commonName='khanat.org', sizeCa = 4096, sizeAppli = 4096, sizeChild = 2048): self.workdir_cert_ca = os.path.abspath(workdir_cert_ca) self.workdir_cert_appli = os.path.abspath(workdir_cert_appli) self.openssl = openssl self.passca = passca self.passappli = passappli self.countryName = countryName self.stateOrProvinceName = stateOrProvinceName self.localityName = localityName self.organizationName = organizationName self.commonName = commonName self.configca = os.path.join(self.workdir_cert_ca, 'openssl.cnf') self.configappli = os.path.join(self.workdir_cert_appli, 'openssl.cnf') self.sizeCa = sizeCa self.sizeAppli = sizeAppli self.sizeChild = sizeChild def directory_create(self, dirpath): if not dir: raise ValueError if not os.path.exists(dirpath): os.makedirs(dirpath) def SendCommandOpenssl(self, args): command = '%s %s' % (self.openssl, args) logging.debug("command:%s" % command) code = subprocess.call(command.split(), stdout=sys.stdout, stderr=sys.stderr) if code != 0: logging.error("Command '%s' return code:%d" % (command, code)) exit(code) def writeConfigOpenssl(self, config, dirpath, certfile, keyfile): with open(config, 'w') as f: f.write('[ ca ]\n' 'default_ca = CA_default\n' '\n[ CA_default ]\n' 'dir = %s\n' 'certs = $dir/certs\n' 'crl_dir = $dir/crl\n' 'database = $dir/index.txt\n' 'new_certs_dir = $dir/newcerts\n' 'certificate = $dir/certs/%s\n' 'serial = $dir/serial\n' 'crlnumber = $dir/crlnumber\n' 'crl = $dir/crl/%s\n' 'private_key = $dir/private/%s\n' 'RANDFILE = $dir/private/.rand\n' 'name_opt = ca_default\n' 'cert_opt = ca_default\n' 'default_days = 390\n' 'default_crl_days = 30\n' 'default_md = sha256\n' 'preserve = no\n' 'policy = policy_match\n' 'crl_extensions = crl_ext\n' 'unique_subject = no\n' '\n[ policy_match ]\n' 'countryName = match\n' 'stateOrProvinceName = match\n' 'organizationName = match\n' 'organizationalUnitName = optional\n' 'commonName = supplied\n' 'emailAddress = optional\n' '\n[ req ]\n' 'default_bits = 2048\n' 'distinguished_name = req_distinguished_name\n' 'x509_extensions = v3_ca\n' 'string_mask = utf8only\n' 'unique_subject = no\n' '\n[ server_cert ]\n' 'basicConstraints=CA:false\n' 'nsComment = "OpenSSL Generated Certificate"\n' 'subjectKeyIdentifier=hash\n' 'authorityKeyIdentifier=keyid,issuer:always\n' 'keyUsage = critical, digitalSignature, keyEncipherment\n' 'nsCertType = server\n' '\n[ client_cert ]\n' 'basicConstraints=CA:false\n' 'nsComment = "OpenSSL Generated Certificate"\n' 'subjectKeyIdentifier=hash\n' 'authorityKeyIdentifier=keyid:always,issuer\n' 'keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment\n' 'nsCertType = client\n' '\n[ v3_ca ]\n' 'basicConstraints=critical, CA:true\n' 'nsComment = "OpenSSL Generated Certificate"\n' 'subjectKeyIdentifier=hash\n' 'authorityKeyIdentifier=keyid:always,issuer\n' 'keyUsage = critical, digitalSignature, cRLSign, keyCertSign\n' '\n[ v3_application_ca ]\n' 'basicConstraints=critical, CA:true, pathlen:0\n' 'nsComment = "OpenSSL Generated Certificate"\n' 'subjectKeyIdentifier=hash\n' 'authorityKeyIdentifier=keyid:always,issuer\n' 'keyUsage = critical, digitalSignature, cRLSign, keyCertSign\n' '\n[ req_distinguished_name ]\n' 'countryName = Country Name (2 letter code)\n' 'countryName_default = FR\n' 'countryName_min = 2\n' 'countryName_max = 2\n' 'stateOrProvinceName = State or Province Name (full name)\n' 'stateOrProvinceName_default = France\n' 'localityName = Locality Name (eg, city)\n' '0.organizationName = Organization Name (eg, company)\n' '0.organizationName_default = Khanat\n' 'organizationalUnitName = Organizational Unit Name (eg, section)\n' 'commonName = Common Name (e.g. server FQDN or YOUR name)\n' 'commonName_max = 64\n' 'emailAddress = Email Address\n' 'emailAddress_max = 64\n' '\n[ crl_ext ]\n' 'authorityKeyIdentifier=keyid:always\n' % (dirpath, certfile, 'cacrl.pem', keyfile)) def createCACertificate(self): logging.info("Create CA Certificate") # Create directory certfilename = 'cacert.pem' keyfilename = 'cakey.pem' self.directory_create(self.workdir_cert_ca) self.directory_create(os.path.join(self.workdir_cert_ca, 'certs')) private=os.path.join(self.workdir_cert_ca, 'private') self.directory_create(private) os.chmod(private, stat.S_IEXEC | stat.S_IWUSR | stat.S_IRUSR) self.directory_create(os.path.join(self.workdir_cert_ca, 'crl')) self.directory_create(os.path.join(self.workdir_cert_ca, 'newcerts')) # Create files use in CA index = os.path.join(self.workdir_cert_ca, 'index.txt') with open(index, 'w') as f: f.write('') serial = os.path.join(self.workdir_cert_ca, 'serial') with open(serial, 'w') as f: f.write('10') # Create configuration self.writeConfigOpenssl(self.configca, self.workdir_cert_ca, certfilename, keyfilename) # Create private key for our CA keyfile = os.path.join(self.workdir_cert_ca, 'private', keyfilename) self.SendCommandOpenssl('genrsa -aes256 -out %s -passout pass:%s %d' % (keyfile, self.passca, self.sizeCa)) os.chmod(keyfile, stat.S_IEXEC | stat.S_IWUSR | stat.S_IRUSR) # Create certificate for our CA certfile = os.path.join(self.workdir_cert_ca, 'certs', certfilename) self.SendCommandOpenssl('req ' '-config %s ' '-key %s ' '-passin pass:%s ' '-new ' '-x509 ' '-days 390 ' '-sha256 ' '-extensions v3_ca ' '-out %s ' '-subj /C=%s/ST=%s/L=%s/O=%s/CN=%s' % (self.configca, keyfile, self.passca, certfile, self.countryName, self.stateOrProvinceName, self.localityName, self.organizationName, self.commonName)) os.chmod(certfile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) # Check certificate self.SendCommandOpenssl('x509 -noout -text -in %s' % certfile) def createApplicationCertificate(self): logging.info("Create Application Certificate") certfilename = 'applicert.pem' csrfilename = 'applicsr.pem' keyfilename = 'applikey.pem' # Create directory self.directory_create(self.workdir_cert_appli) self.directory_create(os.path.join(self.workdir_cert_appli, 'certs')) private=os.path.join(self.workdir_cert_appli, 'private') self.directory_create(private) os.chmod(private, stat.S_IEXEC | stat.S_IWUSR | stat.S_IRUSR) self.directory_create(os.path.join(self.workdir_cert_appli, 'crl')) self.directory_create(os.path.join(self.workdir_cert_appli, 'newcerts')) self.directory_create(os.path.join(self.workdir_cert_appli, 'csr')) # Create files use in CA index = os.path.join(self.workdir_cert_appli, 'index.txt') with open(index, 'w') as f: f.write('') serial = os.path.join(self.workdir_cert_appli, 'serial') with open(serial, 'w') as f: f.write('10') serial = os.path.join(self.workdir_cert_appli, 'crlnumber') with open(serial, 'w') as f: f.write('10') # Create configuration self.writeConfigOpenssl(self.configappli, self.workdir_cert_appli, certfilename, keyfilename) # Create private key for our Application keyfile = os.path.join(self.workdir_cert_appli, 'private', keyfilename) self.SendCommandOpenssl('genrsa -aes256 -out %s -passout pass:%s %d' % (keyfile, self.passappli, self.sizeAppli)) os.chmod(keyfile, stat.S_IEXEC | stat.S_IWUSR | stat.S_IRUSR) # Create certificate for our CA csrfile = os.path.join(self.workdir_cert_appli, 'csr', csrfilename) self.SendCommandOpenssl('req ' '-config %s ' '-new ' '-sha256 ' '-passin pass:%s ' '-key %s ' '-out %s ' '-subj /C=%s/ST=%s/L=%s/O=%s/CN=%s' % (self.configappli, self.passappli, keyfile, csrfile, self.countryName, self.stateOrProvinceName, self.localityName, self.organizationName, self.commonName)) os.chmod(csrfile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) certfile = os.path.join(self.workdir_cert_appli, 'certs', certfilename) # Sign certificate self.SendCommandOpenssl('ca ' '-config %s ' '-extensions v3_application_ca ' '-days 390 ' '-notext ' '-md sha256 ' '-passin pass:%s ' '-in %s ' '-batch ' '-out %s ' % (self.configca, self.passca, csrfile, certfile)) os.chmod(csrfile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) self.SendCommandOpenssl('x509 -noout -text -in %s' % certfile) certcafilename = os.path.join(self.workdir_cert_ca, 'certs', 'cacert.pem') self.SendCommandOpenssl('verify -CAfile %s %s' % (certcafilename, certfile)) # concat applicert & cacert cachainfile = os.path.join(self.workdir_cert_appli, 'certs', 'cachaincert.pem') with open(cachainfile, 'w') as outfp: with open(certfile, 'r') as infp: outfp.write(infp.read()) with open(certcafilename, 'r') as infp: outfp.write(infp.read()) def createChildCertificate(self, childname, extension): keyfilename = "%skey.pem" % childname csrfilename = "%scsr.pem" % childname certfilename = "%scert.pem" % childname keyfile = os.path.join(self.workdir_cert_appli, 'private', keyfilename) self.SendCommandOpenssl('genrsa -out %s %d' % (keyfile, self.sizeChild)) csrfile = os.path.join(self.workdir_cert_appli, 'csr', csrfilename) self.SendCommandOpenssl('req ' '-config %s ' '-new ' '-sha256 ' '-key %s ' '-out %s ' '-subj /C=%s/ST=%s/L=%s/O=%s/CN=%s' % (self.configappli, keyfile, csrfile, self.countryName, self.stateOrProvinceName, self.localityName, self.organizationName, self.commonName)) certfile = os.path.join(self.workdir_cert_appli, 'certs', certfilename) # Sign certificate self.SendCommandOpenssl('ca ' '-config %s ' '-extensions %s ' '-days 390 ' '-notext ' '-md sha256 ' '-passin pass:%s ' '-in %s ' '-batch ' '-out %s ' % (self.configappli, extension, self.passappli, csrfile, certfile)) self.SendCommandOpenssl('x509 -noout -text -in %s' % (certfile)) certcafilename = os.path.join(self.workdir_cert_appli, 'certs', 'cachaincert.pem') self.SendCommandOpenssl('verify -CAfile %s %s' % (certcafilename, certfile)) def main(fileLog, logLevel, show_log_console, workdir_cert_ca, workdir_cert_appli, openssl, sizeCa, sizeAppli, sizeChild, passca, passappli, countryName, stateOrProvinceName, localityName, organizationName, commonName): """ Main function """ # Manage log logging.getLogger('logging') numeric_level = getattr(logging, logLevel.upper(), None) if not isinstance(numeric_level, int): raise ValueError('Invalid log level: %s' % logLevel) handlers=[] if show_log_console: handlers.append(logging.StreamHandler()) if fileLog: handlers.append(logging.FileHandler(fileLog.name)) logging.basicConfig(handlers=handlers, level=numeric_level, format='%(asctime)s %(levelname)s [pid:%(process)d] [%(funcName)s:%(lineno)d] %(message)s') certicate = Certificate(openssl, workdir_cert_ca, workdir_cert_appli, passca, passappli, countryName, stateOrProvinceName, localityName, organizationName, commonName, sizeCa, sizeAppli , sizeChild) logging.info("Generate CA certificate") certicate.createCACertificate() logging.info("Generate Application certificate") certicate.createApplicationCertificate() logging.info("Generate Server certificate") certicate.createChildCertificate('server', 'server_cert') logging.info("Generate Client certificate") certicate.createChildCertificate('client', 'client_cert') logging.info("Certifcate generated") if __name__ == '__main__': parser = argparse.ArgumentParser(description='Create certificate') parser.add_argument('--version', action='version', version='%(prog)s 1.0') parser.add_argument( '--openssl', default='openssl', help='binary openssl') parser.add_argument('-c', '--workdir-cert-ca', default='ca', help='workdir certificate CA') parser.add_argument('-a', '--workdir-cert-appli', default='ca/appli', help='workdir certificate Application') parser.add_argument( '--show-log-console', action='store_true', help='show message in console', default=False) parser.add_argument('--filelog', type=argparse.FileType('wt'), default=None, help='log file') parser.add_argument('--log', default='INFO', help='log level [DEBUG, INFO, WARNING, ERROR') parser.add_argument('--sizeCa', type=int, default=4096, help='Define size key for CA certificate') parser.add_argument('--sizeAppli', type=int, default=4096, help='Define size key for Application certificate') parser.add_argument('--sizeChild', type=int, default=4096, help='Define size key for Child certificate') parser.add_argument('--passca', default='OpenNelCA9439', help='define password for CA certificate') parser.add_argument('--passappli', default='OpenNelAPPLI1097', help='define password for Application certificate') parser.add_argument('--countryName', default='FR', help='countryName for certicate') parser.add_argument('--stateOrProvinceName', default='France', help='stateOrProvinceName for certicate') parser.add_argument('--localityName', default='Paris', help='localityName for certicate') parser.add_argument('--organizationName', default='khanat', help='organizationName for certicate') parser.add_argument('--commonName', default='khanat', help='commonName for certicate') args = parser.parse_args() main(fileLog = args.filelog, logLevel = args.log, show_log_console = args.show_log_console, workdir_cert_ca = args.workdir_cert_ca, workdir_cert_appli = args.workdir_cert_appli, openssl = args.openssl, sizeCa = args.sizeCa, sizeAppli = args.sizeAppli, sizeChild = args.sizeChild, passca = args.passca, passappli = args.passappli, countryName = args.countryName, stateOrProvinceName = args.stateOrProvinceName, localityName = args.localityName, organizationName = args.organizationName, commonName = args.commonName)