feat: Bring Go and Python implementations to feature parity
Go: - Read and write simple-ca.json shared with the Python implementation - Add make-crl and revoke-cert commands and record certificate history - Rename --aia-base-url to --ca-publish-base-url - Add CRL Distribution Point extension to issuing CA and leaf certificates - Take days and issuing_ca defaults from simple-ca.json - Include email SANs in server certificates - Reject IPv4 SANs with out-of-range octets - Record existing issuing CAs and rebuild the CA bundle - Print errors once Python: - Add make-cert --type server|user and email SANs - Reject IPv4 SANs with out-of-range octets - Skip make-ca when the root CA exists and no issuing CA is given - Build ca_bundle.pem from issuing CA subdirectories - Accept a bare filename in make-pfx and drop the root key requirement - Honour explicit --days values and reject values below 1 - Persist and increment CRL numbers in simple-ca.json Update README and add tests for the new behaviour.
This commit is contained in:
1 parent
27461a0fbe
commit
9c84e82672
5 files changed
+757
-170
No files matched your search
@@ -156,3 +156,62 @@ def test_crl(dirs):
|
||||
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"
|
||||
|
||||
|
||||
def test_crl_number_increments(dirs):
|
||||
ca = dirs
|
||||
py("make-ca", "--ca-dir", str(ca), "CRL Test CA")
|
||||
|
||||
numbers = []
|
||||
for _ in range(2):
|
||||
py("make-crl", "--ca-dir", str(ca))
|
||||
numbers.append(openssl("crl", "-in", str(ca / "crl.pem"), "-noout", "-crlnumber").stdout.strip())
|
||||
assert numbers == ["crlNumber=0x01", "crlNumber=0x02"], numbers
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# User certificate and SAN handling
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_user_cert(dirs):
|
||||
ca = dirs
|
||||
py("make-ca", "--ca-dir", str(ca), "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", "--type", "user",
|
||||
"Alice Example", "alice@example.com")
|
||||
|
||||
cert = ca / "issuing_ca" / "Alice Example_cert.pem"
|
||||
verify_cert(cert, ca / "ca_bundle.pem")
|
||||
text = openssl("x509", "-in", str(cert), "-noout", "-text").stdout
|
||||
for eku in ("TLS Web Client Authentication", "E-mail Protection", "Code Signing"):
|
||||
assert eku in text, f"EKU {eku!r} not found"
|
||||
assert "email:alice@example.com" in text
|
||||
assert "TLS Web Server Authentication" not in text
|
||||
|
||||
|
||||
def test_invalid_ip_san(dirs):
|
||||
ca = dirs
|
||||
py("make-ca", "--ca-dir", str(ca), "Test CA")
|
||||
result = py("make-cert", "--ca-dir", str(ca), "test", "999.1.1.1", check=False)
|
||||
assert result.returncode != 0
|
||||
assert not (ca / "test_cert.pem").exists()
|
||||
|
||||
|
||||
def test_make_ca_root_exists(dirs):
|
||||
ca = dirs
|
||||
py("make-ca", "--ca-dir", str(ca), "Test CA")
|
||||
result = py("make-ca", "--ca-dir", str(ca), "Test CA", check=False)
|
||||
assert result.returncode == 0
|
||||
assert "Traceback" not in result.stderr
|
||||
|
||||
|
||||
def test_pfx_bare_filename(dirs):
|
||||
ca = dirs
|
||||
py("make-ca", "--ca-dir", str(ca), "Test CA")
|
||||
py("make-cert", "--ca-dir", str(ca), "test", "test.example.com")
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(SCRIPT), "make-pfx", "--ca-dir", str(ca), "test_cert.pem"],
|
||||
capture_output=True, text=True, cwd=ca,
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
assert (ca / "test.pfx").exists()
|
||||
Reference in new issue
Block a user