Skip to content

Commit 5a3b106

Browse files
authored
Update crl_regen.py
Define a main() so you can run it via -m digitalpy.core.security.crl_regen Wrapped everything in a main() function and added the if __name__ == "__main__": main() so that python3 -m digitalpy.core.security.crl_regen … works as expected. Added --validity-days so you can control next_update. If you pass a .json path, it will emit a JSON structure with the CRL fields; otherwise it writes PEM (and also appends into your CA bundle). Prints both last_update and next_update in ISO 8601 format. Accept --ca-pem-path, --ca-key-path, --crl-path (PEM or JSON) and --validity-days Default validity-days to 365 if not provided Print both Last Update and Next Update timestamps
1 parent 4712579 commit 5a3b106

1 file changed

Lines changed: 115 additions & 45 deletions

File tree

Lines changed: 115 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,127 @@
1-
from OpenSSL import crypto
1+
#!/usr/bin/env python3
2+
"""
3+
Module: digitalpy.core.security.crl_regen
4+
Description: Regenerate a Certificate Revocation List (CRL) and optionally append it to the CA bundle.
5+
Version: 1.1.1
6+
"""
7+
28
import argparse
9+
import json
10+
import sys
11+
from datetime import datetime, timedelta
12+
13+
from cryptography import x509
14+
from cryptography.hazmat.primitives import hashes, serialization
15+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
316

4-
class CertficateRevocationListController:
5-
def __init__(self, ca_pem_path: str, ca_key_path: str, crl_file_path: str):
6-
"""CertficateRevocationListController constructor
17+
18+
class CertificateRevocationListController:
19+
def __init__(self, ca_pem_path: str, ca_key_path: str, crl_path: str, validity_days: int):
20+
"""
21+
Controller for regenerating a Certificate Revocation List (CRL).
722
823
Args:
9-
ca_pem_path (str): path to the ca pem file of the crl
10-
ca_key_path (str): path to the ca key file of the crl
11-
crl_file_path (str): path to the crl file
24+
ca_pem_path: Path to the CA certificate PEM file
25+
ca_key_path: Path to the CA private key PEM file
26+
crl_path: Output path for the CRL (PEM or JSON)
27+
validity_days: Number of days until the next CRL update
1228
"""
1329
self.ca_pem_path = ca_pem_path
1430
self.ca_key_path = ca_key_path
15-
self.ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, open(ca_key_path).read())
16-
self.ca_pem = crypto.load_certificate(crypto.FILETYPE_PEM, open(ca_pem_path, 'rb').read())
17-
self.crl_file_path = crl_file_path
31+
self.crl_path = crl_path
32+
self.validity_days = validity_days
33+
34+
# Load CA certificate
35+
with open(ca_pem_path, "rb") as f:
36+
self.ca_cert = x509.load_pem_x509_certificate(f.read())
37+
38+
# Load CA private key (unencrypted PEM)
39+
with open(ca_key_path, "rb") as f:
40+
self.ca_key = load_pem_private_key(f.read(), password=None)
1841

1942
def regenerate_crl(self):
20-
"""regenerate the configured crl"""
21-
22-
# instantiate CRL object
23-
crl = crypto.CRL()
24-
crl.sign(self.ca_pem, self.ca_key, b"sha256")
25-
26-
# open CRL file and write CRL contents
27-
with open(self.crl_file_path, 'wb') as f:
28-
f.write(crl.export(cert=self.ca_pem, key=self.ca_key, digest=b"sha256"))
29-
30-
31-
delete = 0
32-
# read the contents of the ca pem
33-
with open(self.ca_pem_path, "r") as f:
34-
lines = f.readlines()
35-
# re-write the contents of the ca pem until x509 crl is reached
36-
with open(self.ca_pem_path, "w") as f:
37-
for line in lines:
38-
if delete:
39-
continue
40-
elif line.strip("\n") != "-----BEGIN X509 CRL-----":
41-
f.write(line)
42-
else:
43-
delete = 1
44-
45-
# add the updated x509 crl to the end of the ca pem
46-
with open(self.ca_pem_path, "ab") as f:
47-
f.write(crl.export(cert=self.ca_pem, key=self.ca_key, digest=b"sha256"))
43+
now = datetime.utcnow()
44+
next_update = now + timedelta(days=self.validity_days)
4845

49-
if __name__ == "__main__":
50-
parser = argparse.ArgumentParser(description='command line arguments')
51-
parser.add_argument('--ca-pem-path', dest='ca_pem_path', type=str, help='path to the certificate authority pem')
52-
parser.add_argument('--ca-key-path', dest='ca_key_path', type=str, help='path to the certificate authority key')
53-
parser.add_argument('--crl-path', dest='crl_path', type=str, help='Path to the CRL')
46+
# Build an empty CRL (add revoked certs here if needed)
47+
builder = (
48+
x509.CertificateRevocationListBuilder()
49+
.issuer_name(self.ca_cert.subject)
50+
.last_update(now)
51+
.next_update(next_update)
52+
)
53+
crl_obj = builder.sign(private_key=self.ca_key, algorithm=hashes.SHA256())
54+
55+
# If JSON output requested
56+
if self.crl_path.lower().endswith(".json"):
57+
out = {
58+
"issuer": self.ca_cert.subject.rfc4514_string(),
59+
"last_update": now.isoformat() + "Z",
60+
"next_update": next_update.isoformat() + "Z",
61+
"revoked": [
62+
{
63+
"serial_number": rc.serial_number,
64+
"revocation_date": rc.revocation_date.isoformat() + "Z",
65+
}
66+
for rc in crl_obj
67+
],
68+
}
69+
with open(self.crl_path, "w") as f:
70+
json.dump(out, f, indent=2)
71+
else:
72+
# PEM output
73+
data = crl_obj.public_bytes(serialization.Encoding.PEM)
74+
with open(self.crl_path, "wb") as f:
75+
f.write(data)
76+
# Append to CA bundle
77+
with open(self.ca_pem_path, "ab") as f:
78+
f.write(data)
79+
80+
# Print results
81+
print(f"✅ CRL regenerated: {self.crl_path}")
82+
print(f" Last Update : {now.isoformat()}Z")
83+
print(f" Next Update : {next_update.isoformat()}Z")
84+
85+
86+
def main():
87+
# Header output
88+
print("digitalpy.core.security.crl_regen v1.1.0 - Regenerate a CRL and optionally append to the CA bundle.")
89+
90+
parser = argparse.ArgumentParser(
91+
description="Regenerate a CRL and optionally append to your CA bundle."
92+
)
93+
parser.add_argument(
94+
"--ca-pem-path", required=True,
95+
help="Path to the CA certificate PEM file"
96+
)
97+
parser.add_argument(
98+
"--ca-key-path", required=True,
99+
help="Path to the CA private key PEM file"
100+
)
101+
parser.add_argument(
102+
"--crl-path", required=True,
103+
help="Output path for the CRL (supports .pem or .json extensions)"
104+
)
105+
parser.add_argument(
106+
"--validity-days", "--validity_days", type=int, default=365,
107+
dest="validity_days",
108+
help="Days until next_update (default: 365)"
109+
)
54110

55111
args = parser.parse_args()
56-
57-
CertficateRevocationListController(args.ca_pem_path, args.ca_key_path, args.crl_path).regenerate_crl()
112+
113+
try:
114+
ctl = CertificateRevocationListController(
115+
args.ca_pem_path,
116+
args.ca_key_path,
117+
args.crl_path,
118+
args.validity_days,
119+
)
120+
ctl.regenerate_crl()
121+
except Exception as e:
122+
print(f"❌ Error generating CRL: {e}", file=sys.stderr)
123+
sys.exit(1)
124+
125+
126+
if __name__ == "__main__":
127+
main()

0 commit comments

Comments
 (0)