From 851d696417681fb957e3ce0c1b2c84b3e4128cd4 Mon Sep 17 00:00:00 2001 From: Kevin Guerineau Date: Tue, 7 May 2024 15:53:30 +0200 Subject: [PATCH] [IMP] Add new files --- common.py | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++ manage_pki.py | 30 ++++ 2 files changed, 418 insertions(+) create mode 100644 common.py create mode 100644 manage_pki.py diff --git a/common.py b/common.py new file mode 100644 index 0000000..2d48acb --- /dev/null +++ b/common.py @@ -0,0 +1,388 @@ +#!/usr/bin/python3 + +import subprocess +import jinja2 +import os +import configparser +import sys + +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 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 keyout_path(): + return os.path.join(TisPKI.pki_dir(),'private') + + def csr_path(): + return os.path.join(TisPKI.pki_dir(),'csr') + + def cert_path(): + return os.path.join(TisPKI.pki_dir(),'certs') + + def p12_path(): + return os.path.join(TisPKI.pki_dir(),'p12') + + def config_path(): + return os.path.join(TisPKI.pki_dir(),'config') + + def 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(): + return os.path.join(config.get('general','pki_dir'),'intermediate_ca') + + def intermediate_ca_certfile(): + return os.path.join(TisPKI.pki_intermediate_dir(),'certs','intermediate_ca.crt') + + def intermediate_keyout_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'private') + + def intermediate_csr_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'csr') + + def intermediate_cert_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'certs') + + def intermediate_p12_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'p12') + + def intermediate_config_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'config') + + def intermediate_crl_path(): + return os.path.join(TisPKI.pki_intermediate_dir(),'crl') + + + +def check_directories(root_path): + print('Check directories') + + directories_list = ['certs','config','crl','newcerts','private','csr','crl','p12'] + + if not os.path.isdir(root_path): + print(f'Create { root_path } directory') + os.makedirs(root_path) + + for directory in directories_list: + directory_path = os.path.join(root_path,directory) + if not os.path.isdir(directory_path): + print(f'Create { directory_path } directory') + os.makedirs(directory_path) + + if not os.path.isfile(os.path.join(root_path,'index.txt')): + with open(os.path.join(root_path,'index.txt'),'w') as file: + pass + + +def create_openssl_config(): + print('Check Root CA OpenSSL Config') + + root_ca_config = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca.ini') + + + if not os.path.isfile(root_ca_config): + print('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): + print('Root CA OpenSSL config file is correctly generated !') + else: + print('Root CA OpenSSL config already exist. Skip.') + + # Generate privkey and cert for Root CA + if not os.path.isfile(TisPKI.root_ca_keyfile()) and not os.path.isfile(TisPKI.root_ca_certfile()): + print('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=True, executable='/bin/bash') + + if gen_root_ca.returncode == 0: + print(f'Root CA Certfile is stored in {TisPKI.root_ca_certfile()}') + print(subprocess.run(f'openssl x509 -in {TisPKI.root_ca_certfile()} -text', shell=True, check=True, executable='/bin/bash')) + input("Press Enter to continue...") + else: + print('Error on generating Root CA private key') + sys.exit(1) + else: + print('Root CA private key and certificate already exist. Skip.') + + +def create_openssl_intermediate(): + print('Create intermediate CA') + check_directories(TisPKI.pki_intermediate_dir()) + intermediate_ca_config = os.path.join(TisPKI.pki_intermediate_dir(),'config','create_intermediate_ca.ini') + intermediate_ca_keyfile = os.path.join(TisPKI.pki_intermediate_dir(),'private','intermediate_ca.key') + crl_file = os.path.join(TisPKI.pki_intermediate_dir(),'crl','intermediate_ca.crl') + root_ca_sign_intermediate = os.path.join(TisPKI.pki_dir(),'config','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): + print('Root CA OpenSSL sign intermediate config file is correctly generated !') + + if not os.path.isfile(intermediate_ca_config): + print('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': config.get('openssl_config','intermediate_organization_cn'), + '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): + print('Intermediate CA OpenSSL configfile is correctly generated !') + else: + print('Intermediate CA OpenSSL config already exist. Skip.') + + if not os.path.isfile(intermediate_ca_keyfile) and not os.path.isfile(TisPKI.intermediate_ca_certfile()): + print('Generate Intermediate CA private key and CSR') + gen_intermediate_ca = subprocess.run(f'/usr/bin/openssl req -config {intermediate_ca_config} -new -sha512 -keyout {intermediate_ca_keyfile} -out {TisPKI.intermediate_csr_path()}/intermediate_ca.csr', shell=True, check=True, executable='/bin/bash') + print('Sign Intermediate CA with Root CA') + sign_intermediate_ca = subprocess.run(f"/usr/bin/openssl ca -config {os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca_sign_intermediate.ini')} -extensions v3_intermediate_ca -days 1825 -notext -md sha512 -create_serial -in {TisPKI.intermediate_csr_path()}/intermediate_ca.csr -out {TisPKI.intermediate_ca_certfile()}", shell=True, check=True, executable='/bin/bash' ) + if sign_intermediate_ca.returncode == 0: + print(f'Intermediate CA Certfile is stored in : {TisPKI.intermediate_ca_certfile()}') + print(subprocess.run(f'openssl x509 -in {TisPKI.intermediate_ca_certfile()} -text -noout', shell=True, check=True, executable='/bin/bash')) + input("Press Enter to continue...") + else: + print('Error on generating Intermediate CA private key') + sys.exit(1) + else: + print('Intermediate CA private key and certificate already exist. Skip.') + +## if not os.path.isfile(crl_file): +## print('Generate CRL') +## subprocess.run(f'openssl ca -config {intermediate_ca_config} -gencrl -out {crl_file}',shell=True) + + +def generate_dc_certificate(): + dc_list = config.get('samba_ad','dc_list') + + for dc in dc_list.split(','): + if TisPKI.intermediate_ca: + dc_certfile = os.path.join(TisPKI.intermediate_cert_path(),f'{dc}.crt') + dc_keyfile = os.path.join(TisPKI.intermediate_keyout_path(),f'{dc}.key') + dc_csrfile = os.path.join(TisPKI.intermediate_csr_path(),f'{dc}.csr') + dc_openssl_configfile = os.path.join(TisPKI.intermediate_config_path(),f'openssl_{dc}.ini') + 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() + else: + dc_certfile = os.path.join(TisPKI.cert_path(),f'{dc}.crt') + dc_keyfile = os.path.join(TisPKI.keyout_path(),f'{dc}.key') + dc_csrfile = os.path.join(TisPKI.csr_path(),f'{dc}.csr') + dc_openssl_configfile = os.path.join(TisPKI.config_path(),f'openssl_{dc}.ini') + crl_file = os.path.join(TisPKI.crl_path(),'root_ca.crl') + dc_ca_keyfile = os.path.join(TisPKI.keyout_path(),'root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.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): + print(f'Generate certificate for {dc}') + input("Press Enter to continue...") + dc_guid = subprocess.run('/bin/bash get_guid.sh',shell=True, check=True, executable='/bin/bash') + if dc_guid.returncode != 0: + print('Unable to find dc_guid') + sys.exit(1) + else: + dc_guid = str(subprocess.check_output(f'/bin/bash get_guid.sh',shell=True).decode("utf-8")).strip() + print(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': f"{dc}", + '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 + } + + 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): + print(f'{dc} OpenSSL configfile is correctly generated !') + + print(f'Generate private key and CSR for {dc}') + 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}') + 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) + + +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.crl_path(),'root_ca.crl') + dc_ca_keyfile = os.path.join(TisPKI.keyout_path(),'root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.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.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.keyout_path()}/{username}.key -out {TisPKI.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.csr_path()}/{username}.csr -out {TisPKI.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.keyout_path()}/{username}.key -out {TisPKI.keyout_path()}/{username}-nopasswd.key',shell=True,check=True, executable='/bin/bash')) + + print('Create p12') + print(subprocess.run(f'openssl pkcs12 -export -inkey {TisPKI.keyout_path()}/{username}-nopasswd.key -in {TisPKI.cert_path()}/{username}.crt -out {TisPKI.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() diff --git a/manage_pki.py b/manage_pki.py new file mode 100644 index 0000000..162067e --- /dev/null +++ b/manage_pki.py @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------- +# Name: Samba Manage PKI +# Purpose: Generate and manage Samba PKI +# +# Author: Kevin Guerineau +# +# Created: 07/05/2024 +# Copyright: (c) kevin 2024 +# Licence: +#------------------------------------------------------------------------------- + +from optparse import OptionParser + +from common import * + +def main(): + parser = OptionParser() + + parser.add_option('--init', dest="init", action="store_false", help="Create PKI") + parser.add_option('--create-intermediate', dest="create_intermediate", action="store_false", help="Create an intermediate CA") + parser.add_option('--create-dc-cert', dest="create_dc_cert", action="store_false", help="Create a certificat for a Domain Controler") + + (options, args) = parser.parse_args() + + if options.init: + create_openssl_config() + + +if __name__ == '__main__': + main()