#!/usr/bin/env python3
"""
Crea un CA bundle corretto per il server target.
Uso: python3 build_ca_bundle_py.py areariservata.dgpservizi.com 443 ./custom_ca_bundle_fixed.crt
Requisiti: openssl, python requests
"""
import sys
import re
import subprocess
import requests
from pathlib import Path

def run(cmd):
    p = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    return p.stdout + p.stderr

def extract_pems(openssl_output):
    blocks = re.findall(r'-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----', openssl_output)
    return blocks

def get_aia_urls(pem_text):
    # estrai URI dall'output di openssl x509 -noout -text
    uu = []
    out = run("printf %s | openssl x509 -noout -text" % (sh_quote(pem_text)))
    for m in re.findall(r'CA Issuers - URI:(\S+)', out):
        uu.append(m.strip())
    # fallback generico
    for m in re.findall(r'URI:([^\\s]+)', out):
        if m.strip() not in uu:
            uu.append(m.strip())
    return uu

def download_and_normalize(url):
    r = requests.get(url, timeout=15)
    r.raise_for_status()
    data = r.content
    # tenta convertire DER->PEM se necessario
    try:
        txt = data.decode('ascii')
        if '-----BEGIN CERTIFICATE-----' in txt:
            return txt
    except Exception:
        pass
    # assume DER
    p = subprocess.run(['openssl','x509','-inform','der','-outform','pem'], input=data, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return p.stdout.decode('utf-8')

def sh_quote(s):
    return "'" + s.replace("'", "'\\''") + "'"

def main():
    if len(sys.argv) < 4:
        print("Usage: build_ca_bundle_py.py <host> <port> <out_path>")
        sys.exit(1)
    host = sys.argv[1]
    port = sys.argv[2]
    out_path = Path(sys.argv[3])

    print(f"Fetching chain using openssl s_client for {host}:{port} ...")
    s = run(f"openssl s_client -showcerts -servername {host} -connect {host}:{port} </dev/null")
    pems = extract_pems(s)
    print(f"Found {len(pems)} PEM blocks from s_client.")

    bundle_parts = []
    # if more than 1 block, collect intermediates (skip first = leaf)
    if len(pems) > 1:
        for pem in pems[1:]:
            bundle_parts.append(pem)
    else:
        print("Server non ha inviato intermedi. Provo a scaricare tramite AIA dal leaf.")
        leaf = pems[0]
        aia = get_aia_urls(leaf)
        if not aia:
            print("Nessun AIA trovato nella cert. Esci.")
            sys.exit(2)
        print("AIA URLs:", aia)
        for url in aia:
            try:
                print("  download:", url)
                pem = download_and_normalize(url)
                if '-----BEGIN CERTIFICATE-----' in pem:
                    bundle_parts.append(pem)
            except Exception as e:
                print("  errore download:", e)

    if not bundle_parts:
        print("Nessun intermediate recuperato. Non posso creare bundle valido.")
        sys.exit(3)

    # concatena intermedi + (opzionale) root se sono stati scaricati
    out_path.parent.mkdir(parents=True, exist_ok=True)
    with out_path.open('w') as f:
        for part in bundle_parts:
            f.write(part.strip() + "\n")
    print("Bundle salvato in:", out_path)

    # verifica rapida con openssl
    print("Verifica openssl (Verify return code):")
    v = run(f"openssl s_client -connect {host}:{port} -servername {host} -CAfile {sh_quote(str(out_path))} </dev/null")
    for line in v.splitlines():
        if 'Verify return code' in line or line.startswith('subject='):
            print(line)
    print("Fatto.")

if __name__ == '__main__':
    main()