diff --git a/cert_deets.py b/cert_deets.py index 0a96e95..3f5be7a 100644 --- a/cert_deets.py +++ b/cert_deets.py @@ -19,6 +19,28 @@ from tabulate import tabulate SAN_GROUPING = 4 +def get_cert_with_servername(addr: tuple[str, int], servername: str = "") -> bytes: + """ + Get TLS certificate from an address with an explicit servername override + + Args: + addr (tuple[str, int]): adress in tuple form (address, port) + servername (str): SNI servername + + Returns: + bytes: PEM bytes + """ + context = ssl.create_default_context() + context.check_hostname = False + + with socket.create_connection((addr[0], addr[1]), timeout=10) as sock: + with context.wrap_socket(sock, server_hostname=servername) as sslsock: + if der_cert := sslsock.getpeercert(True): + return ssl.DER_cert_to_PEM_cert(der_cert).encode("utf=8") + + return bytes() + + def format_fingerprint(fingerprint: bytes | str) -> str: """ Print a fingerprint as a colon-separated hex string @@ -58,6 +80,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("site", help="site to lookup") + parser.add_argument("-a", "--address", help="explicit address to connect to") return parser.parse_args() @@ -89,17 +112,22 @@ if __name__ == "__main__": endpoint = f"{parts.hostname}:{parts.port}" try: - pem_data = ssl.get_server_certificate( - (parts.hostname, parts.port), - timeout=10, - ).encode("utf-8") + if args.address: + pem_data = get_cert_with_servername( + (args.address, parts.port), + servername=parts.hostname, + ) + else: + 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, ConnectionResetError,