From 7a4fe704bf79e1acd59b83f78069e21fd8ee7559 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 May 2024 22:22:56 +0200 Subject: [PATCH] [IMP] Add intermediate CA --- samba-pki-tools.ini.sample | 4 ++ tis-pki.py | 133 ++++++++++++++++++++++++++++++++----- 2 files changed, 120 insertions(+), 17 deletions(-) diff --git a/samba-pki-tools.ini.sample b/samba-pki-tools.ini.sample index 62421dc..8a94567 100644 --- a/samba-pki-tools.ini.sample +++ b/samba-pki-tools.ini.sample @@ -15,5 +15,9 @@ default_cert_duration = 730 crl_uri = URI:http://crl.kg.tranquil.it/crl/root_ca.crl +create_intermediate = True +intermediate_organization_cn = INFOLIX Authentication CA +intermediate_crl_uri = URI:http://crl.infolix.lan/crl/infolix_authentication.crl + [samba_ad] dc_list = diff --git a/tis-pki.py b/tis-pki.py index 6beef41..ba1449e 100644 --- a/tis-pki.py +++ b/tis-pki.py @@ -35,23 +35,44 @@ class TisPKI: def p12_path(): return os.path.join(TisPKI.pki_dir(),'p12') -def check_directories(): + + 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 check_directories(root_path): print('Check directories') directories_list = ['certs','config','crl','newcerts','private','csr','crl','p12'] - if not os.path.isdir(TisPKI.pki_dir()): - print(f'Create { TisPKI.pki_dir() } directory') - os.makedirs(TisPKI.pki_dir()) + 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(TisPKI.pki_dir(),directory) + 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(TisPKI.pki_dir(),'index.txt')): - with open(os.path.join(TisPKI.pki_dir(),'index.txt'),'w') as file: + 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 @@ -60,15 +81,15 @@ def create_openssl_config(): root_ca_config = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca.ini') root_ca_keyfile = os.path.join(TisPKI.pki_dir(),'private','root_ca.key') + root_ca_sign_intermediate = os.path.join(TisPKI.pki_dir(),'config','openssl_root_ca_sign_intermediate.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', + 'organization_ou': config.get('openssl_config','organization_name') + ' CA', 'default_cert_duration': config.get('openssl_config','default_cert_duration'), 'pki_dir': TisPKI.pki_dir(), 'country': config.get('openssl_config','country'), @@ -79,16 +100,31 @@ def create_openssl_config(): '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 !') + + if config.getboolean('openssl_config','create_intermediate'): + 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': root_ca_keyfile, + 'root_ca_certfile': TisPKI.root_ca_certfile(), + 'intermediate_crl_uri': config.get('openssl_config','intermediate_crl_uri') + } + + 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 !') else: print('Root CA OpenSSL config already exist. Skip.') - # Generate privkey and cert for Root CA if not os.path.isfile(root_ca_keyfile) and not os.path.isfile(TisPKI.root_ca_certfile()): print('Generate CA private key') @@ -102,6 +138,47 @@ def create_openssl_config(): 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') + 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(subprocess.run(f'openssl x509 -in {TisPKI.intermediate_ca_certfile()} -text -noout', shell=True, check=True, executable='/bin/bash')) + else: + print('Error on generating Intermediate CA private key') + sys.exit(1) + else: + print('Intermediate CA private key and certificate already exist. Skip.') + def generate_dc_certificate(): dc_list = config.get('samba_ad','dc_list') @@ -112,10 +189,18 @@ def generate_dc_certificate(): dc_csrfile = os.path.join(TisPKI.pki_dir(),'csr',f'{dc}.csr') dc_openssl_configfile = os.path.join(TisPKI.pki_dir(),'config',f'openssl_{dc}.ini') crl_file = os.path.join(TisPKI.pki_dir(),'crl','root_ca.crl') + + if config.getboolean('openssl_config','create_intermediate'): + dc_ca_keyfile = os.path.join(TisPKI.pki_dir(),'intermediate_ca','private','intermediate_ca.key') + dc_ca_certfile = os.path.join(TisPKI.pki_dir(),'intermediate_ca','certs','intermediate_ca.crt') + else: + dc_ca_keyfile = os.path.join(TisPKI.pki_dir(),'private','root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.pki_dir(),'certs','root_ca.crt') + if not os.path.isfile(dc_certfile) and not os.path.isfile(dc_keyfile): print(f'Generate certificate for {dc}') #dc_guid = subprocess.run(f'get_guid.sh {dc}',shell=True) - dc_guid = '51a10f19f84f8d4abda4dee35fe096c6' + dc_guid = '3375a41d7acaa545994ffe4d94bda8ce' print(dc_guid) template_dir = ('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) @@ -130,7 +215,9 @@ def generate_dc_certificate(): '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_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) @@ -162,6 +249,13 @@ def generate_user_certificate(): openssl_user_file = os.path.join(TisPKI.pki_dir(),'config','openssl_user.ini') + if config.getboolean('openssl_config','create_intermediate'): + dc_ca_keyfile = os.path.join(TisPKI.pki_dir(),'intermediate_ca','private','intermediate_ca.key') + dc_ca_certfile = os.path.join(TisPKI.pki_dir(),'intermediate_ca','certs','intermediate_ca.crt') + else: + dc_ca_keyfile = os.path.join(TisPKI.pki_dir(),'private','root_ca.key') + dc_ca_certfile = os.path.join(TisPKI.pki_dir(),'certs','root_ca.crt') + template_dir = ('templates') jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir)) user_tmpl = jinja_env.get_template('openssl_user_cert.tmpl') @@ -173,7 +267,9 @@ def generate_user_certificate(): '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_ou': config.get('openssl_config','organization_ou'), + 'dc_ca_keyfile': dc_ca_keyfile, + 'dc_ca_certfile': dc_ca_certfile } config_string = user_tmpl.render(user_tmpl_var) @@ -187,7 +283,7 @@ def generate_user_certificate(): print('Enter username') username = input() - upn = f'{username}@kg.tranquil.it' + upn = f'{username}@infolix.lan' print(f'Generate private key for {upn}') print(subprocess.run(f"openssl req -new -newkey rsa:4096 -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')) @@ -206,7 +302,7 @@ def generate_user_certificate(): def main(): if config.get('general','pki_dir'): - check_directories() + check_directories(root_path=config.get('general','pki_dir')) else: print('No pki_dir set in samba-pki-tools.ini') sys.exit(1) @@ -217,6 +313,9 @@ def main(): 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: