diff --git a/cert_deets.py b/cert_deets.py index b597b69..745242f 100644 --- a/cert_deets.py +++ b/cert_deets.py @@ -6,9 +6,11 @@ import argparse import socket import ssl import sys +import urllib.error from typing import Any from urllib.parse import urlparse +from cert_chain_resolver.api import CertificateChain, resolve from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes @@ -17,6 +19,21 @@ from tabulate import tabulate SAN_GROUPING = 4 +def format_fingerprint(fingerprint: bytes | str) -> str: + """ + Print a fingerprint as a colon-separated hex string + + Args: + fingerprint (bytes | str): fingerprint to format + + Returns: + str: formatted fingerprint + """ + if isinstance(fingerprint, str): + fingerprint = bytearray.fromhex(fingerprint) + return ":".join([format(i, "02x") for i in fingerprint]) + + def display_error( site: str, error: Any = None, @@ -53,6 +70,10 @@ if __name__ == "__main__": """ args = parseargs() + url = args.site + if "://" not in url: + url = f"https://{url}" + parts = urlparse(args.site, scheme="https") if not parts.netloc: @@ -70,7 +91,15 @@ if __name__ == "__main__": try: pem_data = ssl.get_server_certificate( (parts.hostname, parts.port), + timeout=10, ).encode("utf-8") + + cert_chain = CertificateChain() + try: + cert_chain = resolve(pem_data) + except urllib.error.URLError: + pass + except ( ConnectionRefusedError, ssl.CertificateError, @@ -113,11 +142,28 @@ if __name__ == "__main__": ["Valid to", cert.not_valid_after_utc], ["Issuer", cert.issuer.rfc4514_string()], [ - "SHA1 fingerprint", - ":".join([format(i, "02x") for i in cert.fingerprint(hashes.SHA1())]), + "Fingerprint", + f"{format_fingerprint(cert.fingerprint(hashes.SHA1()))} (SHA1)", ], ] + if cert_chain: + table.append( + [ + "CA chain", + "\n".join( + [ + f"{cert.common_name} " + f"(Issuer: {cert.issuer})\n" + "Fingerprint: " + f"{format_fingerprint(cert.get_fingerprint(hashes.SHA1))} (SHA1)" + for cert in list(cert_chain.intermediates) + + [cert_chain.root] + ] + ), + ] + ) + print(tabulate(table, tablefmt="plain")) return 0 diff --git a/pyproject.toml b/pyproject.toml index f6b0e25..91ea479 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ description = "Show TLS certificate details for a given endpoint" keywords = ["tls", "certificate", "python"] classifiers = ["Programming Language :: Python :: 3"] readme = "README.md" -dependencies = ["cryptography", "tabulate"] +dependencies = ["cert-chain-resolver", "cryptography", "tabulate"] requires-python = ">=3.11" [project.urls]