935167ca8c
- Removed the JSON configuration structure and related functions. - Introduced plain text file for AIA base URL management. - Updated CA and certificate creation functions to directly read/write AIA URL. - Simplified CA bundle rebuilding logic by directly reading subdirectories. - Enhanced test coverage for CA and certificate creation, including PFX generation. - Adjusted test cases to reflect changes in directory structure and file handling.
159 lines
5.8 KiB
Python
159 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
# MIT License
|
|
# Copyright (c) 2026 Sławomir Koszewski
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
SCRIPT = Path(__file__).parent / "simple-ca.py"
|
|
|
|
|
|
def py(*args, check=True):
|
|
return subprocess.run(
|
|
[sys.executable, str(SCRIPT), *args],
|
|
capture_output=True, text=True, check=check,
|
|
)
|
|
|
|
|
|
def openssl(*args, check=True):
|
|
return subprocess.run(["openssl", *args], capture_output=True, text=True, check=check)
|
|
|
|
|
|
def cert_serial(cert_path):
|
|
out = openssl("x509", "-in", str(cert_path), "-noout", "-serial").stdout
|
|
return out.strip().split("=", 1)[-1].upper()
|
|
|
|
|
|
def verify_cert(cert_path, bundle_path):
|
|
result = openssl("verify", "-CAfile", str(bundle_path), str(cert_path), check=False)
|
|
assert result.returncode == 0, f"Certificate verification failed:\n{result.stderr}"
|
|
|
|
|
|
@pytest.fixture
|
|
def dirs(tmp_path):
|
|
ca = tmp_path / "ca"
|
|
ca.mkdir()
|
|
return ca
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Standalone CA
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_standalone_ca(dirs):
|
|
ca = dirs
|
|
py("make-ca", "--ca-dir", str(ca), "Test CA")
|
|
|
|
assert (ca / "ca_cert.pem").exists()
|
|
assert (ca / "ca_bundle.pem").exists()
|
|
assert (ca / "simple-ca.json").exists()
|
|
verify_cert(ca / "ca_cert.pem", ca / "ca_bundle.pem")
|
|
|
|
py("make-cert", "--ca-dir", str(ca), "test", "test.example.com", "127.0.0.1")
|
|
|
|
assert (ca / "test_cert.pem").exists()
|
|
verify_cert(ca / "test_cert.pem", ca / "ca_bundle.pem")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Two-level CA
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_two_level_ca(dirs):
|
|
ca = dirs
|
|
py("make-ca", "--ca-dir", str(ca), "Test Root CA")
|
|
py("make-ca", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca", "Issuing CA")
|
|
|
|
assert (ca / "issuing_ca" / "ca_cert.pem").exists()
|
|
verify_cert(ca / "issuing_ca" / "ca_cert.pem", ca / "ca_bundle.pem")
|
|
|
|
py("make-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"test", "test.example.com", "127.0.0.1")
|
|
|
|
verify_cert(ca / "issuing_ca" / "test_cert.pem", ca / "ca_bundle.pem")
|
|
|
|
py("make-pfx", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"--password", "s3cr3t", str(ca / "issuing_ca" / "test_cert.pem"))
|
|
|
|
assert (ca / "issuing_ca" / "test.pfx").exists()
|
|
result = openssl("pkcs12", "-in", str(ca / "issuing_ca" / "test.pfx"), "-noout", "-info",
|
|
"-password", "pass:s3cr3t")
|
|
assert result.returncode == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# PFX algorithm variants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_pfx_modern(dirs):
|
|
ca = dirs
|
|
py("make-ca", "--ca-dir", str(ca), "PFX Test CA")
|
|
py("make-ca", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca", "Issuing CA")
|
|
py("make-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"test", "test.example.com", "127.0.0.1")
|
|
py("make-pfx", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"--password", "s3cr3t", str(ca / "issuing_ca" / "test_cert.pem"))
|
|
|
|
info = openssl("pkcs12", "-in", str(ca / "issuing_ca" / "test.pfx"), "-noout", "-info",
|
|
"-password", "pass:s3cr3t")
|
|
assert "PBES2" in (info.stdout + info.stderr), "Expected modern PBES2 encryption"
|
|
|
|
|
|
@pytest.mark.skipif(sys.platform != "darwin", reason="macOS only")
|
|
def test_pfx_apple_openssl(dirs):
|
|
ca = dirs
|
|
py("make-ca", "--ca-dir", str(ca), "PFX Test CA")
|
|
py("make-ca", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca", "Issuing CA")
|
|
py("make-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"test", "test.example.com", "127.0.0.1")
|
|
py("make-pfx", "--apple-openssl", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"--password", "s3cr3t", str(ca / "issuing_ca" / "test_cert.pem"))
|
|
|
|
result = subprocess.run(
|
|
["/usr/bin/openssl", "pkcs12", "-in", str(ca / "issuing_ca" / "test.pfx"),
|
|
"-noout", "-info", "-password", "pass:s3cr3t"],
|
|
capture_output=True, text=True,
|
|
)
|
|
assert result.returncode == 0
|
|
combined = result.stdout + result.stderr
|
|
assert "PBES2" not in combined, "Expected Apple-compatible (non-PBES2) format"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CRL and revocation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_crl(dirs):
|
|
ca = dirs
|
|
py("make-ca", "--ca-dir", str(ca), "CRL Test CA")
|
|
py("make-ca", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca", "Issuing CA")
|
|
|
|
py("make-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"alice", "alice.example.com")
|
|
py("make-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
"bob", "bob.example.com")
|
|
|
|
issuing_dir = ca / "issuing_ca"
|
|
alice_serial = cert_serial(issuing_dir / "alice_cert.pem")
|
|
bob_serial = cert_serial(issuing_dir / "bob_cert.pem")
|
|
|
|
py("revoke-cert", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca",
|
|
str(issuing_dir / "alice_cert.pem"))
|
|
|
|
py("make-crl", "--ca-dir", str(ca), "--issuing-ca", "issuing_ca")
|
|
issuing_crl = issuing_dir / "crl.pem"
|
|
assert issuing_crl.exists()
|
|
|
|
crl_text = openssl("crl", "-in", str(issuing_crl), "-noout", "-text").stdout.upper()
|
|
assert alice_serial in crl_text, f"Alice's serial {alice_serial} not found in issuing CA CRL"
|
|
assert bob_serial not in crl_text, f"Bob's serial {bob_serial} unexpectedly found in issuing CA CRL"
|
|
|
|
py("make-crl", "--ca-dir", str(ca))
|
|
root_crl = ca / "crl.pem"
|
|
assert root_crl.exists()
|
|
root_crl_text = openssl("crl", "-in", str(root_crl), "-noout", "-text").stdout
|
|
assert "No Revoked Certificates" in root_crl_text, "Root CA CRL should be empty"
|