Remove simple-ca module and its associated files
/ test-python (push) Successful in 49s
/ test-go (push) Failing after 4m44s

- Deleted go.mod and go.sum files for the simple-ca module.
- Removed main.go file containing the implementation of the simple-ca tool.
This commit is contained in:
2026-05-24 16:47:43 +02:00
parent 0d61919162
commit cb858a96b1
3 changed files with 271 additions and 104 deletions
-39
View File
@@ -56,45 +56,6 @@ make_pfx --ca-dir <ca_directory> [--issuing-ca <file_prefix>] --path <pfx_file_p
- `--path <pfx_file_path>`: The path where the generated PFX file will be saved.
- `--password <pfx_password>`: Optional. The custom password to protect the PFX, instead of the default `changeit`.
## Go binary
A Go port with the same feature set lives in [`src/simple-ca`](src/simple-ca). It compiles to a single self-contained binary (~56 MB) with no runtime dependencies, and exposes `make-ca`, `make-cert`, and `make-pfx` as subcommands mirroring the Bash flag names.
### Build for the host platform
```bash
cd src/simple-ca
go build -o simple-ca .
./simple-ca --help
```
### Cross-compile
Go builds statically linked binaries for any target from any host:
```bash
cd src/simple-ca
# Linux
GOOS=linux GOARCH=amd64 go build -o simple-ca-linux-amd64 .
GOOS=linux GOARCH=arm64 go build -o simple-ca-linux-arm64 .
# macOS
GOOS=darwin GOARCH=amd64 go build -o simple-ca-darwin-amd64 .
GOOS=darwin GOARCH=arm64 go build -o simple-ca-darwin-arm64 .
# Windows
GOOS=windows GOARCH=amd64 go build -o simple-ca-windows-amd64.exe .
```
### Usage
```bash
simple-ca make-ca [--days N] [--issuing-ca PREFIX] [--aia-base-url URL] CA_DIR CA_NAME
simple-ca make-cert [--ca-dir DIR] [--days N] [--issuing-ca PREFIX] CERT_DIR SUBJECT [SAN...]
simple-ca make-pfx --ca-dir DIR [--issuing-ca PREFIX] --path CERT_PATH [--password PASS]
```
## generate-mobileconfig.py
`generate-mobileconfig.py` generates Apple `.mobileconfig` profiles for distributing CA certificates and optionally client certificates and IKEv2 VPN configuration to Apple devices (macOS / iOS / iPadOS).
+66 -13
View File
@@ -21,8 +21,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This script runs integration tests against one or more simple-ca implementations.
# Usage: run-tests.sh [python|go|all] (default: all)
# This script runs integration tests against the simple-ca Python implementation.
# Usage: run-tests.sh [python|all] (default: all)
set -e
TEST_TARGET="${1:-all}"
@@ -149,6 +149,67 @@ run_pfx_algorithm_tests() {
fi
}
# run_crl_tests NAME MAKE_CA_CMD MAKE_CERT_CMD MAKE_PFX_CMD REVOKE_CMD MAKE_CRL_CMD
run_crl_tests() {
local NAME="$1"
local MAKE_CA_CMD="$2"
local MAKE_CERT_CMD="$3"
local REVOKE_CMD="$4"
local MAKE_CRL_CMD="$5"
echo
echo "--- [$NAME] CRL tests ---"
clean_up_test_dir
# Build a two-level CA hierarchy
$MAKE_CA_CMD --ca-dir "$CA_DIR" "CRL Test CA"
$MAKE_CA_CMD --ca-dir "$CA_DIR" --issuing-ca "issuing_ca" "Issuing CA"
# Issue two certs; revoke the first; keep the second active
$MAKE_CERT_CMD --ca-dir "$CA_DIR" --cert-dir "$CERT_DIR" --issuing-ca "issuing_ca" \
"alice" "alice.example.com"
$MAKE_CERT_CMD --ca-dir "$CA_DIR" --cert-dir "$CERT_DIR" --issuing-ca "issuing_ca" \
"bob" "bob.example.com"
$REVOKE_CMD --ca-dir "$CA_DIR" --issuing-ca "issuing_ca" "$CERT_DIR/alice_cert.pem"
# Generate CRL for the issuing CA
$MAKE_CRL_CMD --ca-dir "$CA_DIR" --issuing-ca "issuing_ca"
[[ -f "$CA_DIR/issuing_ca/crl.pem" ]] || { echo "ERROR: issuing_ca/crl.pem not created" >&2; exit 1; }
# alice's serial must appear in the issuing CA CRL
CRL_TEXT=$(openssl crl -in "$CA_DIR/issuing_ca/crl.pem" -noout -text 2>/dev/null)
ALICE_SERIAL=$(openssl x509 -in "$CERT_DIR/alice_cert.pem" -noout -serial | cut -d= -f2)
if echo "$CRL_TEXT" | grep -qi "$ALICE_SERIAL"; then
echo "CRL [issuing_ca]: alice's serial found — OK"
else
echo "ERROR: CRL [issuing_ca]: alice's serial not found in CRL" >&2
echo "$CRL_TEXT" >&2
exit 1
fi
# bob's serial must NOT appear in the issuing CA CRL
BOB_SERIAL=$(openssl x509 -in "$CERT_DIR/bob_cert.pem" -noout -serial | cut -d= -f2)
if echo "$CRL_TEXT" | grep -qi "$BOB_SERIAL"; then
echo "ERROR: CRL [issuing_ca]: bob's serial unexpectedly found in CRL" >&2
exit 1
else
echo "CRL [issuing_ca]: bob's serial absent — OK"
fi
# Root CA CRL should be empty (no revoctions at root level)
$MAKE_CRL_CMD --ca-dir "$CA_DIR"
[[ -f "$CA_DIR/crl.pem" ]] || { echo "ERROR: root crl.pem not created" >&2; exit 1; }
ROOT_CRL_TEXT=$(openssl crl -in "$CA_DIR/crl.pem" -noout -text 2>/dev/null)
if echo "$ROOT_CRL_TEXT" | grep -q "No Revoked Certificates"; then
echo "CRL [root]: empty — OK"
else
echo "ERROR: CRL [root]: expected empty CRL" >&2
echo "$ROOT_CRL_TEXT" >&2
exit 1
fi
}
# Uses ;;& to fall through to subsequent patterns so 'all' matches every block.
case "$TEST_TARGET" in
python|all)
@@ -156,20 +217,12 @@ case "$TEST_TARGET" in
PY_PREFIX="python3 $SCRIPT_DIR/simple-ca.py"
run_flow "python" "$PY_PREFIX make-ca" "$PY_PREFIX make-cert" "$PY_PREFIX make-pfx"
run_pfx_algorithm_tests "python" "$PY_PREFIX make-ca" "$PY_PREFIX make-cert" "$PY_PREFIX make-pfx"
run_crl_tests "python" "$PY_PREFIX make-ca" "$PY_PREFIX make-cert" "$PY_PREFIX revoke-cert" "$PY_PREFIX make-crl"
;;&
go|all)
command -v go >/dev/null || { echo "ERROR: go not found" >&2; exit 1; }
GO_SRC="$SCRIPT_DIR/src/simple-ca"
GO_BIN="$GO_SRC/simple-ca"
echo "Building Go binary..."
(cd "$GO_SRC" && go build -o simple-ca .)
run_flow "go" "$GO_BIN make-ca" "$GO_BIN make-cert" "$GO_BIN make-pfx"
run_pfx_algorithm_tests "go" "$GO_BIN make-ca" "$GO_BIN make-cert" "$GO_BIN make-pfx"
;;&
python|go|all)
python|all)
;;
*)
echo "ERROR: unknown target '$TEST_TARGET' (expected: python|go|all)" >&2
echo "ERROR: unknown target '$TEST_TARGET' (expected: python|all)" >&2
exit 1
;;
esac
+205 -52
View File
@@ -24,14 +24,17 @@
# This module requires Python 3.8+ and OpenSSL to be installed on the system.
import argparse
import datetime
import json
import os
import re
import subprocess
import sys
import tempfile
OPENSSL = "openssl"
class Config:
FILE = "simple-ca.json"
@@ -56,6 +59,26 @@ class Config:
self._data.update(patch)
self._save()
def append_history(self, ca_key: str, entry: dict):
history = self._data.get("history", {})
history.setdefault(ca_key, []).append(entry)
self._data["history"] = history
self._save()
def revoke_in_history(self, ca_key: str, serial: str, revoked_at: str):
"""Mark a certificate as revoked. Returns True if newly revoked,
False if already revoked, None if serial not found."""
history = self._data.get("history", {})
for entry in history.get(ca_key, []):
if entry.get("serial") == serial:
if "revoked" in entry:
return False
entry["revoked"] = revoked_at
self._data["history"] = history
self._save()
return True
return None
def _save(self):
with open(self._path, "w") as f:
json.dump(self._data, f, indent=2)
@@ -65,12 +88,15 @@ class Config:
_config: Config
def _now() -> str:
return datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _err(msg):
print(f"ERROR: {msg}", file=sys.stderr)
def _rebuild_ca_bundle(ca_dir):
"""Write ca_bundle.pem = root cert + registered subordinate CA certs."""
bundle_path = os.path.join(ca_dir, "ca_bundle.pem")
parts = []
root = os.path.join(ca_dir, "ca_cert.pem")
@@ -86,7 +112,54 @@ def _rebuild_ca_bundle(ca_dir):
f.write(b"".join(parts))
def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
def _read_serial(cert_path) -> str:
result = subprocess.run(
[OPENSSL, "x509", "-in", cert_path, "-noout", "-serial"],
capture_output=True, text=True,
)
return result.stdout.strip().split("=", 1)[-1]
def _read_expiry(cert_path) -> str:
"""Return the certificate notAfter date as an ISO 8601 UTC string."""
result = subprocess.run(
[OPENSSL, "x509", "-in", cert_path, "-noout", "-enddate"],
capture_output=True, text=True,
)
raw = result.stdout.strip().split("=", 1)[-1]
raw = " ".join(raw.split()) # normalize whitespace ("May 4" → "May 4")
dt = datetime.datetime.strptime(raw, "%b %d %H:%M:%S %Y GMT")
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
def _iso_to_asn1(iso: str) -> str:
"""Convert "2026-05-24T14:28:10Z""260524142810Z" for OpenSSL index.txt."""
dt = datetime.datetime.strptime(iso, "%Y-%m-%dT%H:%M:%SZ")
return dt.strftime("%y%m%d%H%M%SZ")
_IP_RE = re.compile(r"^[0-9]{1,3}(\.[0-9]{1,3}){3}$")
_DNS_RE = re.compile(r"^[a-z0-9-]+(\.[a-z0-9-]+)*$")
def _is_ip(value):
return bool(_IP_RE.match(value))
def _is_dns(value):
return bool(_DNS_RE.match(value))
def _pipe(cmd1, cmd2):
p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
p2 = subprocess.Popen(cmd2, stdin=p1.stdout)
p1.stdout.close()
p2.communicate()
p1.wait()
return p1.returncode == 0 and p2.returncode == 0
def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, ca_publish_base_url=None):
if issuing_ca == "ca":
_err("--issuing-ca cannot be 'ca' as it is reserved for the root CA.")
return False
@@ -111,9 +184,7 @@ def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
return False
print(f"Generating CA certificate '{ca_name}' and key...")
# Path length constraint of 1 is set for the root CA to allow creating
# one level of issuing CAs, but prevent a longer chain which is not
# supported by this script.
# Path length constraint of 1: allows one level of issuing CAs.
cmd = [
OPENSSL, "req",
"-x509",
@@ -132,7 +203,10 @@ def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
return False
_rebuild_ca_bundle(ca_dir)
_config.update({"aia_base_url": aia_base_url} if aia_base_url else {})
patch = {"name": ca_name, "created": _now()}
if ca_publish_base_url:
patch["ca_publish_base_url"] = ca_publish_base_url
_config.update(patch)
return True
issuing_ca_dir = os.path.join(ca_dir, issuing_ca)
@@ -151,10 +225,10 @@ def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
"-addext", "basicConstraints=critical,CA:TRUE,pathlen:0",
"-addext", "keyUsage=critical,keyCertSign,cRLSign",
]
if aia_base_url:
if ca_publish_base_url:
req_cmd += [
"-addext",
f"authorityInfoAccess=caIssuers;URI:{aia_base_url}/ca_cert.crt",
"-addext", f"authorityInfoAccess=caIssuers;URI:{ca_publish_base_url}/ca_cert.crt",
"-addext", f"crlDistributionPoints=URI:{ca_publish_base_url}/crl.pem",
]
x509_cmd = [
OPENSSL, "x509",
@@ -170,7 +244,7 @@ def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
_err("Failed to generate issuing CA certificate and key.")
return False
patch = {"aia_base_url": aia_base_url} if aia_base_url else {}
patch = {"ca_publish_base_url": ca_publish_base_url} if ca_publish_base_url else {}
subs = _config.get("subordinates", [])
if issuing_ca not in subs:
patch["subordinates"] = subs + [issuing_ca]
@@ -179,29 +253,8 @@ def make_ca(ca_dir, ca_name, days=3650, issuing_ca=None, aia_base_url=None):
return True
_IP_RE = re.compile(r"^[0-9]{1,3}(\.[0-9]{1,3}){3}$")
_DNS_RE = re.compile(r"^[a-z0-9-]+(\.[a-z0-9-]+)*$")
def _is_ip(value):
return bool(_IP_RE.match(value))
def _is_dns(value):
return bool(_DNS_RE.match(value))
def _pipe(cmd1, cmd2):
p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
p2 = subprocess.Popen(cmd2, stdin=p1.stdout)
p1.stdout.close()
p2.communicate()
p1.wait()
return p1.returncode == 0 and p2.returncode == 0
def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
issuing_ca=None, days=365, aia_base_url=None):
issuing_ca=None, days=365, ca_publish_base_url=None):
if issuing_ca == "ca":
_err("--issuing-ca cannot be 'ca' as it is reserved for the root CA.")
return False
@@ -224,7 +277,7 @@ def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
_err(f"Invalid subject name '{cert_subject_name}'. Must be a valid DNS name.")
return False
signing_dir = os.path.join(ca_dir, issuing_ca) if issuing_ca else ca_dir
signing_dir = os.path.join(ca_dir, issuing_ca) if issuing_ca else ca_dir
ca_cert_path = os.path.join(signing_dir, "ca_cert.pem")
ca_key_path = os.path.join(signing_dir, "ca_key.pem")
if not os.path.isfile(ca_cert_path) or not os.path.isfile(ca_key_path):
@@ -234,12 +287,15 @@ def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
)
return False
if aia_base_url:
aia_url = f"{aia_base_url}/{issuing_ca}/ca_cert.crt" if issuing_ca else f"{aia_base_url}/ca_cert.crt"
else:
aia_url = ""
aia_url = cdp_url = ""
if ca_publish_base_url:
if issuing_ca:
aia_url = f"{ca_publish_base_url}/{issuing_ca}/ca_cert.crt"
cdp_url = f"{ca_publish_base_url}/{issuing_ca}/crl.pem"
else:
aia_url = f"{ca_publish_base_url}/ca_cert.crt"
cdp_url = f"{ca_publish_base_url}/crl.pem"
# "account" name from the subject: hostname part before the first dot
cert_name = cert_subject_name.split(".", 1)[0]
san_entries = [f"DNS:{cert_subject_name}"]
@@ -259,7 +315,7 @@ def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
print(f" - {san}")
cert_out = os.path.join(cert_dir, f"{cert_name}_cert.pem")
key_out = os.path.join(cert_dir, f"{cert_name}_key.pem")
key_out = os.path.join(cert_dir, f"{cert_name}_key.pem")
if not os.path.isfile(cert_out) or not os.path.isfile(key_out):
print("Generating server certificate and key...")
@@ -276,6 +332,8 @@ def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
]
if aia_url:
req_cmd += ["-addext", f"authorityInfoAccess=caIssuers;URI:{aia_url}"]
if cdp_url:
req_cmd += ["-addext", f"crlDistributionPoints=URI:{cdp_url}"]
x509_cmd = [
OPENSSL, "x509",
"-req",
@@ -290,6 +348,13 @@ def make_cert(cert_dir, cert_subject_name, sans=None, ca_dir=None,
_err("Failed to generate server certificate and key.")
return False
_config.append_history(issuing_ca or "ca", {
"name": cert_subject_name,
"serial": _read_serial(cert_out),
"created": _now(),
"expires": _read_expiry(cert_out),
})
return True
@@ -298,13 +363,10 @@ def make_pfx(cert_path, ca_dir, issuing_ca=None, password=None, apple_openssl=Fa
_err("--issuing-ca cannot be 'ca' as it is reserved for the root CA.")
return False
cert_dir = os.path.dirname(cert_path)
cert_dir = os.path.dirname(cert_path)
cert_basename = os.path.basename(cert_path)
if cert_basename.endswith("_cert.pem"):
cert_name = cert_basename[: -len("_cert.pem")]
else:
cert_name = cert_basename
key_path = os.path.join(cert_dir, f"{cert_name}_key.pem")
cert_name = cert_basename[:-len("_cert.pem")] if cert_basename.endswith("_cert.pem") else cert_basename
key_path = os.path.join(cert_dir, f"{cert_name}_key.pem")
if not cert_dir or not os.path.isdir(cert_dir):
_err(f"Certificate directory {cert_dir} does not exist.")
@@ -347,7 +409,6 @@ def make_pfx(cert_path, ca_dir, issuing_ca=None, password=None, apple_openssl=Fa
with open(issuing_ca_cert_path, "rb") as f:
chain_bytes += f.read()
import tempfile
chain_fd, chain_file = tempfile.mkstemp()
try:
with os.fdopen(chain_fd, "wb") as f:
@@ -371,6 +432,82 @@ def make_pfx(cert_path, ca_dir, issuing_ca=None, password=None, apple_openssl=Fa
return True
def make_crl(ca_dir, issuing_ca=None, days=30):
signing_dir = os.path.join(ca_dir, issuing_ca) if issuing_ca else ca_dir
ca_cert = os.path.join(signing_dir, "ca_cert.pem")
ca_key = os.path.join(signing_dir, "ca_key.pem")
if not os.path.isfile(ca_cert) or not os.path.isfile(ca_key):
_err(f"CA certificate or key not found in {signing_dir}.")
return False
crl_path = os.path.join(signing_dir, "crl.pem")
history_key = issuing_ca or "ca"
revoked_entries = [
e for e in _config.get("history", {}).get(history_key, [])
if "revoked" in e and "expires" in e
]
with tempfile.TemporaryDirectory() as tmp:
index_txt = os.path.join(tmp, "index.txt")
crlnumber = os.path.join(tmp, "crlnumber")
cnf_path = os.path.join(tmp, "openssl.cnf")
with open(index_txt, "w") as f:
for e in revoked_entries:
expires_asn1 = _iso_to_asn1(e["expires"])
revoked_asn1 = _iso_to_asn1(e["revoked"])
f.write(f"R\t{expires_asn1}\t{revoked_asn1}\t{e['serial']}\tunknown\t/CN={e['name']}\n")
with open(crlnumber, "w") as f:
f.write("01\n")
with open(cnf_path, "w") as f:
f.write(
"[ ca ]\ndefault_ca = CA_default\n\n"
"[ CA_default ]\n"
f"database = {index_txt}\n"
f"crlnumber = {crlnumber}\n"
f"certificate = {ca_cert}\n"
f"private_key = {ca_key}\n"
f"default_crl_days = {days}\n"
"default_md = sha256\n\n"
"[ crl_ext ]\n"
"authorityKeyIdentifier = keyid:always\n"
)
if subprocess.run([OPENSSL, "ca", "-gencrl", "-config", cnf_path, "-out", crl_path]).returncode != 0:
_err("Failed to generate CRL.")
return False
print(f"CRL written to {crl_path}")
return True
def revoke_cert(cert_path, ca_dir, issuing_ca=None):
if not os.path.isfile(cert_path):
_err(f"Certificate not found: {cert_path}")
return False
signing_dir = os.path.join(ca_dir, issuing_ca) if issuing_ca else ca_dir
if not os.path.isdir(signing_dir):
_err(f"CA directory not found: {signing_dir}")
return False
ca_key = issuing_ca or "ca"
serial = _read_serial(cert_path)
result = _config.revoke_in_history(ca_key, serial, _now())
if result is None:
_err(f"Certificate with serial {serial} not found in history for CA '{ca_key}'.")
return False
if result is False:
print(f"Certificate {cert_path} (serial {serial}) is already revoked.")
return True
print(f"Certificate {cert_path} (serial {serial}) marked as revoked.")
return True
def _build_parser():
parser = argparse.ArgumentParser(
description="Simple CA for creating and managing test certificates."
@@ -380,7 +517,8 @@ def _build_parser():
p_ca = sub.add_parser("make-ca", help="Create a root or issuing CA.")
p_ca.add_argument("--days", type=int, default=None, help="Validity period in days (default: 3650)")
p_ca.add_argument("--issuing-ca", default=None, help="Specify the issuing CA")
p_ca.add_argument("--aia-base-url", default=None, help="Specify the AIA base URL")
p_ca.add_argument("--ca-publish-base-url", default=None,
help="Base URL for AIA and CRL distribution point extensions")
p_ca.add_argument("--ca-dir", help="Directory to store the CA files")
p_ca.add_argument("--openssl", default=None, metavar="PATH",
help=f"Path to the openssl binary (default: {OPENSSL})")
@@ -404,6 +542,16 @@ def _build_parser():
help="Use Apple's bundled /usr/bin/openssl for PKCS12 generation")
p_pfx.add_argument("path", help="Path to the certificate file")
p_crl = sub.add_parser("make-crl", help="Generate a CRL for a CA.")
p_crl.add_argument("--ca-dir", help="Directory of the CA")
p_crl.add_argument("--issuing-ca", default=None, help="Generate CRL for this issuing CA")
p_crl.add_argument("--days", type=int, default=None, help="CRL validity in days (default: 30)")
p_rev = sub.add_parser("revoke-cert", help="Revoke a certificate.")
p_rev.add_argument("--ca-dir", help="Directory of the CA")
p_rev.add_argument("--issuing-ca", default=None, help="Issuing CA that signed the certificate")
p_rev.add_argument("cert_path", help="Path to the certificate file to revoke")
return parser
@@ -416,9 +564,9 @@ def main(argv=None):
_config = Config(ca_dir)
OPENSSL = getattr(args, "openssl", None) or _config.get("openssl", OPENSSL)
issuing_ca = args.issuing_ca or _config.get("issuing_ca")
aia_base_url = getattr(args, "aia_base_url", None) or _config.get("aia_base_url")
OPENSSL = getattr(args, "openssl", None) or _config.get("openssl", OPENSSL)
issuing_ca = args.issuing_ca or _config.get("issuing_ca")
ca_publish_base_url = getattr(args, "ca_publish_base_url", None) or _config.get("ca_publish_base_url")
days_cfg = _config.get("days", {})
@@ -428,7 +576,7 @@ def main(argv=None):
ca_dir, args.ca_name,
days=days,
issuing_ca=issuing_ca,
aia_base_url=aia_base_url,
ca_publish_base_url=ca_publish_base_url,
)
elif args.command == "make-cert":
days = args.days or days_cfg.get("cert", 365)
@@ -438,7 +586,7 @@ def main(argv=None):
ca_dir=ca_dir,
issuing_ca=issuing_ca,
days=days,
aia_base_url=aia_base_url,
ca_publish_base_url=ca_publish_base_url,
)
elif args.command == "make-pfx":
ok = make_pfx(
@@ -447,6 +595,11 @@ def main(argv=None):
password=args.password,
apple_openssl=args.apple_openssl,
)
elif args.command == "make-crl":
days = args.days or days_cfg.get("crl", 30)
ok = make_crl(ca_dir, issuing_ca=issuing_ca, days=days)
elif args.command == "revoke-cert":
ok = revoke_cert(args.cert_path, ca_dir, issuing_ca=issuing_ca)
else:
parser.error(f"Unknown command: {args.command}")
ok = False