""" 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()