#!/usr/bin/python3 import subprocess import jinja2 import os import configparser import sys from colorama import Fore, Style import shutil import time config = configparser.ConfigParser() config.read('samba-pki-tools.ini') # # write config file # jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_3cx)) # template = jinja_env.get_template("3CXVoipPhone.j2") # template_variables = { # "name": "TIS (%s)" % get_current_user(), } class Printing(): def information(string): print(Fore.CYAN + string) return print(Style.RESET_ALL) def success(string): print(Fore.GREEN + string) return print(Style.RESET_ALL) def warning(string): print(Fore.YELLOW + string) return print(Style.RESET_ALL) def error(string): print(Fore.RED + string) return print(Style.RESET_ALL) class TisPKI: def pki_dir(): return config.get('general','pki_dir') def root_ca_certfile(): return os.path.join(TisPKI.pki_dir(),'certs','root_ca.crt') def root_ca_keyfile(): return os.path.join(TisPKI.pki_dir(),'private','root_ca.key') def root_keyout_path(): return os.path.join(TisPKI.pki_dir(),'private') def root_csr_path(): return os.path.join(TisPKI.pki_dir(),'csr') def root_cert_path(): return os.path.join(TisPKI.pki_dir(),'certs') def root_p12_path(): return os.path.join(TisPKI.pki_dir(),'p12') def root_config_path(): return os.path.join(TisPKI.pki_dir(),'config') def root_crl_path(): return os.path.join(TisPKI.pki_dir(),'crl') def intermediate_ca(): if config.getboolean('openssl_config','create_intermediate'): return True def pki_intermediate_dir(name): name = name.replace(' ','_') return os.path.join(config.get('general','pki_dir'),f'{name}_intermediate_ca') def intermediate_cert_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'certs') def intermediate_keyout_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'private') def intermediate_csr_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'csr') def intermediate_p12_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'p12') def intermediate_config_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'config') def intermediate_crl_path(name): return os.path.join(TisPKI.pki_intermediate_dir(name),'crl') def intermediate_ca_certfile(name): name = name.replace(' ','_') return os.path.join(TisPKI.intermediate_cert_path(name),f'{name}_intermediate_ca.crt') def intermediate_ca_keyfile(name): name = name.replace(' ','_') return os.path.join(TisPKI.intermediate_keyout_path(name),f'{name}_intermediate_ca.key') def check_directories(path,verbose=False): if verbose: Printing.information('Check directories') directories_list = ['certs','config','crl','private','csr','p12','newcerts'] if not os.path.isdir(path): if verbose: Printing.information(f'Create { path } directory') os.makedirs(path) for directory in directories_list: directory_path = os.path.join(path,directory) if not os.path.isdir(directory_path): if verbose: Printing.information(f'Create { directory_path } directory') os.makedirs(directory_path) if not os.path.isfile(os.path.join(path,'index.txt')): with open(os.path.join(path,'index.txt'),'w') as file: pass def create_openssl_config(force=False,verbose=False): if force: Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') if destroy == 'I want to remove all my PKI': Printing.information('OK, too late ! Destroying your PKI !') time.sleep(1) shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) Printing.information('Check Root CA OpenSSL Config') if config.get('general','pki_dir'): check_directories(path=config.get('general','pki_dir'),verbose=verbose) else: Printing.error('No pki_dir set in samba-pki-tools.ini') sys.exit(1) root_ca_config = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca.ini') if not os.path.isfile(root_ca_config): Printing.information('Root CA OpenSSL configfile not exist. Creating...') template_dir = os.path.join('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) root_ca_tmpl = jinja_env.get_template('openssl_root_ca.tmpl') root_ca_tmpl_var = { 'organization_ou': config.get('openssl_config','organization_name') + ' CA', 'default_cert_duration': config.get('openssl_config','default_cert_duration'), 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), 'pki_dir': TisPKI.pki_dir(), 'country': config.get('openssl_config','country'), 'state': config.get('openssl_config','state'), 'city': config.get('openssl_config','city'), 'organization_name': config.get('openssl_config','organization_name'), 'organization_ou': config.get('openssl_config','organization_ou'), 'organization_cn': config.get('openssl_config','organization_cn'), 'crl_uri': config.get('openssl_config','crl_uri') } config_string = root_ca_tmpl.render(root_ca_tmpl_var) with open(root_ca_config,'wt') as file: file.write(config_string) if os.path.isfile(root_ca_config): Printing.success('Root CA OpenSSL config file is correctly generated !') else: Printing.warning('Root CA OpenSSL config already exist. Skip.') # Generate privkey and cert for Root CA if not os.path.isfile(TisPKI.root_ca_keyfile()) or not os.path.isfile(TisPKI.root_ca_certfile()): Printing.information('Generate Root CA private key') gen_root_ca = subprocess.run(f'/usr/bin/openssl req -x509 -new -sha512 -config {root_ca_config} \ -days 3650 -extensions v3_ca -keyout {TisPKI.root_ca_keyfile()} -out {TisPKI.root_ca_certfile()}', shell=True, check=False, executable='/bin/bash') if gen_root_ca.returncode == 0: if verbose: subprocess.run(f'openssl x509 -in {TisPKI.root_ca_certfile()} -text', shell=True, check=True, executable='/bin/bash') Printing.information(f'Root CA Certfile is stored in {TisPKI.root_ca_certfile()}') else: Printing.error('Error on generating Root CA private key') os.remove(TisPKI.root_ca_keyfile()) retry = input('If you want to retry, press Y : ') if retry == "y" or retry == 'Y': create_openssl_config(force,verbose) else: Printing.warning('Root CA private key and certificate already exist. Skip.') def create_openssl_intermediate(name, force=False,verbose=False): ## if force: ## Printing.error("Do you realy want to remove ALL you PKI ? This will destroy ALL YOUR CERTIFICATES AND PRIVATE KEY") ## Printing.error("After that, you MUST REGENERATE YOUR PKI with NEW certificates and private key for ALL YOUR DOMAIN CONTROLLERS AND USERS") ## destroy = input('If you are realy sure, please enter : "I want to remove all my PKI" : ') ## if destroy == 'I want to remove all my PKI': ## Printing.information('OK, too late ! Destroying your PKI !') ## time.sleep(1) ## shutil.rmtree(TisPKI.pki_dir(), ignore_errors=True) Printing.information(f'Create intermediate CA {name} ') check_directories(path=TisPKI.pki_intermediate_dir(name),verbose=verbose) intermediate_ca_config = os.path.join(TisPKI.intermediate_config_path(name),'create_intermediate_ca.ini') intermediate_crl_file = os.path.join(TisPKI.intermediate_crl_path(name),f'{name}_intermediate_ca.crl') root_ca_sign_intermediate = os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini') if not os.path.isfile(root_ca_sign_intermediate): if TisPKI.intermediate_ca: template_dir = os.path.join('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) root_ca_tmpl = jinja_env.get_template('openssl_sign_intermediate_csr.tmpl') root_ca_tmpl_var = { 'organization_ou': config.get('openssl_config','organization_name') + ' CA', 'pki_dir': TisPKI.pki_dir(), 'root_ca_keyfile': TisPKI.root_ca_keyfile(), 'root_ca_certfile': TisPKI.root_ca_certfile(), 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri'), 'default_crl_duration' : config.get('openssl_config','default_crl_duration'), } config_string = root_ca_tmpl.render(root_ca_tmpl_var) with open(root_ca_sign_intermediate,'wt') as file: file.write(config_string) if os.path.isfile(root_ca_sign_intermediate): Printing.success('Root CA OpenSSL sign intermediate config file is correctly generated !') if not os.path.isfile(intermediate_ca_config): Printing.information('Intermediate CA OpenSSL configfile not exist. Creating...') template_dir = os.path.join('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) intermediate_ca_tmpl = jinja_env.get_template('openssl_create_intermediate_ca.tmpl') intermediate_ca_tmpl_var = { 'country': config.get('openssl_config','country'), 'state': config.get('openssl_config','state'), 'city': config.get('openssl_config','city'), 'organization_name': config.get('openssl_config','organization_name'), 'organization_ou': config.get('openssl_config','organization_ou'), 'intermediate_organization_cn': name, 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri') } config_string = intermediate_ca_tmpl.render(intermediate_ca_tmpl_var) with open(intermediate_ca_config,'wt') as file: file.write(config_string) if os.path.isfile(intermediate_ca_config): Printing.success('Intermediate CA OpenSSL configfile is correctly generated !') else: Printing.information('Intermediate CA OpenSSL config already exist. Skip.') if not os.path.isfile(TisPKI.intermediate_ca_keyfile(name)) and not os.path.isfile(TisPKI.intermediate_ca_certfile(name)): Printing.information('Generate Intermediate CA private key and CSR') gen_intermediate_ca = subprocess.run(f"/usr/bin/openssl req -config {intermediate_ca_config} -new -sha512 -keyout {TisPKI.intermediate_ca_keyfile(name)} \ -out {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr", shell=True, check=True, executable='/bin/bash') Printing.information('Sign Intermediate CA with Root CA') sign_intermediate_ca = subprocess.run(f"/usr/bin/openssl ca -config {os.path.join(TisPKI.root_config_path(),'openssl_root_ca_sign_intermediate.ini')} \ -extensions v3_intermediate_ca -days 1825 -notext -md sha512 -create_serial -in {TisPKI.intermediate_csr_path(name)}/{name.replace(' ','_')}_intermediate_ca.csr \ -out {TisPKI.intermediate_ca_certfile(name)}", shell=True, check=True, executable='/bin/bash') if sign_intermediate_ca.returncode == 0: if verbose: subprocess.run(f'openssl x509 -in {TisPKI.intermediate_ca_certfile(name)} -text', shell=True, check=True, executable='/bin/bash') Printing.success(f'Intermediate CA Certfile is stored in : {TisPKI.intermediate_ca_certfile(name)}') else: Printing.error('Error on generating Intermediate CA private key') os.remove(TisPKI.intermediate_ca_keyfile(name)) retry = input('If you want to retry, press Y : ') if retry == "y" or retry == 'Y': create_openssl_intermediate(name, force, verbose) else: Printing.warning('Intermediate CA private key and certificate already exist. Skip.') def generate_dc_certificate(dc_name=None, ca_name=None, force=False, verbose=False): if ca_name != "Root": dc_certfile = os.path.join(TisPKI.intermediate_cert_path(ca_name),f'{dc_name}.crt') dc_keyfile = os.path.join(TisPKI.intermediate_keyout_path(ca_name),f'{dc_name}.key') dc_csrfile = os.path.join(TisPKI.intermediate_csr_path(ca_name),f'{dc_name}.csr') dc_openssl_configfile = os.path.join(TisPKI.intermediate_config_path(ca_name),f'openssl_{dc_name}.ini') crl_file = os.path.join(TisPKI.intermediate_crl_path(ca_name),'intermediate_ca.crl') dc_ca_keyfile = TisPKI.intermediate_ca_keyfile(ca_name) dc_ca_certfile = TisPKI.intermediate_ca_certfile(ca_name) crl_uri = config.get('openssl_config','intermediate_crl_uri') pki_dir = TisPKI.pki_intermediate_dir(ca_name) else: dc_certfile = os.path.join(TisPKI.root_cert_path(),f'{dc_name}.crt') dc_keyfile = os.path.join(TisPKI.root_keyout_path(),f'{dc_name}.key') dc_csrfile = os.path.join(TisPKI.root_csr_path(),f'{dc_name}.csr') dc_openssl_configfile = os.path.join(TisPKI.root_config_path(),f'openssl_{dc_name}.ini') crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') crl_uri = config.get('openssl_config','crl_uri') pki_dir = TisPKI.pki_dir() if not os.path.isfile(dc_certfile) and not os.path.isfile(dc_keyfile): Printing.information(f'Generate certificate for {dc_name}') dc_guid = subprocess.run('/bin/bash get_guid.sh',shell=True, check=True, executable='/bin/bash') if dc_guid.returncode != 0: Printing.error('Unable to find dc_guid') sys.exit(1) else: dc_name_guid = dc_name.split('.')[0] dc_guid = str(subprocess.check_output(f'/bin/bash get_guid.sh {dc_name_guid}',shell=True).decode("utf-8")).strip() if verbose: Printing.information(f'{dc_name} GUID is : ' + str(dc_guid).strip()) template_dir = ('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) dc_tmpl = jinja_env.get_template('openssl_server_cert.tmpl') dc_tmpl_var = { 'dc_name': dc_name, 'dc_guid': str(dc_guid), 'crl_uri': crl_uri, 'pki_dir': pki_dir, 'default_cert_duration': config.get('openssl_config','default_cert_duration'), 'country': config.get('openssl_config','country'), 'state': config.get('openssl_config','state'), 'city': config.get('openssl_config','city'), 'organization_name': config.get('openssl_config','organization_name'), 'organization_ou': config.get('openssl_config','organization_ou'), 'dc_ca_keyfile': dc_ca_keyfile, 'dc_ca_certfile': dc_ca_certfile, 'commonName': dc_name } config_string = dc_tmpl.render(dc_tmpl_var) with open(dc_openssl_configfile,'wt') as file: file.write(config_string) if os.path.isfile(dc_openssl_configfile): Printing.information(f'{dc_name} OpenSSL configfile is correctly generated !') Printing.information(f'Generate private key and CSR for {dc_name}') print(subprocess.run(f"openssl req -new -addext 'subjectAltName = email:copy' -newkey rsa:4096 -keyout {dc_keyfile} \ -out {dc_csrfile} -config {dc_openssl_configfile}" , shell=True, check=True, executable='/bin/bash')) if os.path.isfile(dc_csrfile): print(f'Sign certificate for {dc_name}') print(subprocess.run(f'openssl ca -config {dc_openssl_configfile} -extensions usr_cert_mskdc \ -days 3650 -notext -md sha512 -create_serial -in {dc_csrfile} -out {dc_certfile}', shell=True, check=True, executable='/bin/bash')) if os.path.isfile(dc_certfile): print('Concatenation of DC and Root cert') subprocess.run(f'cat {dc_certfile} {TisPKI.root_ca_certfile()} > {dc_certfile}_full',shell=True) Printing.success(f'Certificat is stored in : {dc_certfile}') Printing.success(f'Key is stored in : {dc_keyfile}') def generate_user_certificate(): default_bits_user = config.get('openssl_config','default_bits_user') username = input('Enter username') upn_suffix = config.get('samba_ad','upn_suffix') upn = f'{username}@{upn_suffix}' if TisPKI.intermediate_ca: crl_file = os.path.join(TisPKI.intermediate_crl_path(),'intermediate_ca.crl') dc_ca_keyfile = os.path.join(TisPKI.intermediate_keyout_path(),'intermediate_ca.key') dc_ca_certfile = os.path.join(TisPKI.intermediate_cert_path(),'intermediate_ca.crt') crl_uri = config.get('openssl_config','intermediate_crl_uri') pki_dir = TisPKI.pki_intermediate_dir() openssl_user_file = os.path.join(TisPKI.intermediate_config_path(),'openssl_user.ini') emailAddress = upn commonName = username else: crl_file = os.path.join(TisPKI.root_crl_path(),'root_ca.crl') dc_ca_keyfile = os.path.join(TisPKI.root_keyout_path(),'root_ca.key') dc_ca_certfile = os.path.join(TisPKI.root_cert_path(),'root_ca.crt') crl_uri = config.get('openssl_config','crl_uri') pki_dir = TisPKI.pki_dir() openssl_user_file = os.path.join(TisPKI.root_config_path(),'openssl_user.ini') emailAddress = upn commonName = username template_dir = ('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) user_tmpl = jinja_env.get_template('openssl_user_cert.tmpl') user_tmpl_var = { 'crl_uri': crl_uri, 'pki_dir': pki_dir, 'default_cert_duration': config.get('openssl_config','default_cert_duration'), 'country': config.get('openssl_config','country'), 'state': config.get('openssl_config','state'), 'city': config.get('openssl_config','city'), 'organization_name': config.get('openssl_config','organization_name'), 'organization_ou': config.get('openssl_config','organization_ou'), 'dc_ca_keyfile': dc_ca_keyfile, 'dc_ca_certfile': dc_ca_certfile, 'default_bits_user': config.get('openssl_config','default_bits_user'), 'emailAddress' : emailAddress, 'commonName' : commonName } config_string = user_tmpl.render(user_tmpl_var) with open(openssl_user_file,'wt') as file: file.write(config_string) if os.path.isfile(openssl_user_file): print(f'User OpenSSL configfile is correctly generated !') print(f'Generate private key for {upn}') print(subprocess.run(f"openssl req -new -newkey rsa:{default_bits_user} -keyout {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_csr_path()}/{username}.csr -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)",shell=True,check=True, executable='/bin/bash')) print(f'Sign certificate') print(subprocess.run(f'openssl ca -extensions usr_cert_scarduser -days 730 -notext -md sha512 -create_serial -in {TisPKI.root_csr_path()}/{username}.csr -out {TisPKI.root_cert_path()}/{username}.crt -config <(cat {openssl_user_file} <(cat <<-EOF\n[ sanuser ]\notherName=msUPN;UTF8:{upn}\nemail=copy\nEOF\n)\n)',shell=True,check=True, executable='/bin/bash')) print('Remove password in rsa key') print(subprocess.run(f'openssl rsa -in {TisPKI.root_keyout_path()}/{username}.key -out {TisPKI.root_keyout_path()}/{username}-nopasswd.key',shell=True,check=True, executable='/bin/bash')) print('Create p12') print(subprocess.run(f'openssl pkcs12 -export -inkey {TisPKI.root_keyout_path()}/{username}-nopasswd.key -in {TisPKI.root_cert_path()}/{username}.crt -out {TisPKI.root_p12_path()}/{username}.p12', shell=True,check=True, executable='/bin/bash')) def main(): if config.get('general','pki_dir'): check_directories(root_path=config.get('general','pki_dir')) else: print('No pki_dir set in samba-pki-tools.ini') sys.exit(1) if config.get('openssl_config','root_name'): create_openssl_config() else: print('No root_name set in samba-pki-tools.ini') sys.exit(1) if config.getboolean('openssl_config','create_intermediate'): create_openssl_intermediate() if config.get('samba_ad','dc_list'): generate_dc_certificate() else: print('No dc_list set in samba-pki-tools.ini') sys.exit(1) generate_user_certificate() if __name__ == '__main__': main()