You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
2.9 KiB
Python

"""
Script to compare a set of X.509 certificate files
"""
import argparse
import typing
from cryptography import x509
import helpers
DEFAULT_OIDS = [
x509.OID_BASIC_CONSTRAINTS,
x509.OID_KEY_USAGE,
x509.OID_EXTENDED_KEY_USAGE,
x509.OID_AUTHORITY_KEY_IDENTIFIER,
x509.OID_SUBJECT_KEY_IDENTIFIER,
x509.OID_AUTHORITY_KEY_IDENTIFIER,
x509.OID_AUTHORITY_INFORMATION_ACCESS,
x509.OID_CRL_DISTRIBUTION_POINTS,
x509.OID_CERTIFICATE_POLICIES,
]
def table_line(key, values):
print(f"| {key} |", " | ".join(values), "|")
def format_duration(cert: x509.Certificate) -> str:
return f"{cert.not_valid_before} - {cert.not_valid_after} = {(cert.not_valid_after - cert.not_valid_before).days}d"
def compare_certificates(certificates: typing.List[str]):
parsed = {}
for cert_filename in certificates:
parsed[cert_filename] = helpers.parse_certificate(cert_filename)
print("| |", " | ".join([f"`{name}`" for name in certificates]), "|")
print("|---|" + "---|" * len(certificates))
table_line("Subject", [parsed[fn].subject.rfc4514_string() for fn in certificates])
table_line(
"Issuer",
[
parsed[fn].issuer.rfc4514_string(
attr_name_overrides={
x509.ObjectIdentifier("1.2.840.113549.1.9.1"): "emailAddress"
}
)
for fn in certificates
],
)
table_line(
"Public Key algorithm",
[helpers.format_key_type(parsed[fn]) for fn in certificates],
)
table_line(
"Hash algorithm",
[helpers.format_hash_algorithm(parsed[fn]) for fn in certificates],
)
table_line(
"Validity duration", [format_duration(parsed[fn]) for fn in certificates]
)
extra_oids = set()
for cert in certificates:
for oid in [
ext.oid for ext in parsed[cert].extensions if ext.oid not in DEFAULT_OIDS
]:
extra_oids.add(oid)
for oid in DEFAULT_OIDS:
table_line(
helpers.extension_label(oid),
[
helpers.format_extension(parsed[fn].extensions, oid)
for fn in certificates
],
)
for oid in extra_oids:
table_line(
helpers.extension_label(oid),
[
helpers.format_extension(parsed[fn].extensions, oid)
for fn in certificates
],
)
def main():
parser = argparse.ArgumentParser(
prog="compare certs",
description="compare a certificate to a set of other certificates",
)
parser.add_argument("first", help="first certificates")
parser.add_argument("others", nargs="+", help="other certificates")
args = parser.parse_args()
cert_list = [args.first]
cert_list.extend(args.others)
compare_certificates(cert_list)
if __name__ == "__main__":
main()