diff --git a/cert_deets.py b/cert_deets.py index 3f5be7a..a6801f5 100644 --- a/cert_deets.py +++ b/cert_deets.py @@ -68,135 +68,135 @@ def display_error( print(str(error)) -if __name__ == "__main__": +def parseargs() -> argparse.Namespace: + """ + Parse the CLI - def parseargs() -> argparse.Namespace: - """ - Parse the CLI + Returns: + argparse.Namespace: parsed arguments + """ + parser = argparse.ArgumentParser() - Returns: - argparse.Namespace: parsed arguments - """ - parser = argparse.ArgumentParser() + parser.add_argument("site", help="site to lookup") + parser.add_argument("-a", "--address", help="explicit address to connect to") - parser.add_argument("site", help="site to lookup") - parser.add_argument("-a", "--address", help="explicit address to connect to") + return parser.parse_args() - return parser.parse_args() - def main() -> int: - """ - Main entrypoint +def main() -> int: + """ + Main entrypoint - Returns: - int: return value - """ - args = parseargs() + Returns: + int: return value + """ + args = parseargs() - url = args.site - if "://" not in url: - url = f"https://{url}" + url = args.site + if "://" not in url: + url = f"https://{url}" - parts = urlparse(args.site, scheme="https") + parts = urlparse(args.site, scheme="https") - if not parts.netloc: - parts = parts._replace(netloc=args.site) + if not parts.netloc: + parts = parts._replace(netloc=args.site) - if not parts.port: - parts = parts._replace(netloc=f"{parts.netloc}:443") + if not parts.port: + parts = parts._replace(netloc=f"{parts.netloc}:443") - if not parts.hostname or not parts.port: - display_error(args.site, "Cannot parse hostname") - return 1 + if not parts.hostname or not parts.port: + display_error(args.site, "Cannot parse hostname") + return 1 - endpoint = f"{parts.hostname}:{parts.port}" + endpoint = f"{parts.hostname}:{parts.port}" + try: + 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: - 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 = resolve(pem_data) + except urllib.error.URLError: + pass + except ( + ConnectionRefusedError, + ConnectionResetError, + socket.gaierror, + ssl.CertificateError, + ssl.SSLError, + TimeoutError, + ) as error: + display_error(endpoint, error) + return 2 - cert_chain = CertificateChain() - try: - cert_chain = resolve(pem_data) - except urllib.error.URLError: - pass - except ( - ConnectionRefusedError, - ConnectionResetError, - socket.gaierror, - ssl.CertificateError, - ssl.SSLError, - TimeoutError, - ) as error: - display_error(endpoint, error) - return 2 + if not pem_data: + display_error(endpoint, "Cannot fetch PEM data") + return 3 - if not pem_data: - display_error(endpoint, "Cannot fetch PEM data") - return 3 + cert = x509.load_pem_x509_certificate(pem_data, default_backend()) - cert = x509.load_pem_x509_certificate(pem_data, default_backend()) - - sans = [ - f"DNS:{dns}" - for dns in cert.extensions.get_extension_for_class( + sans = [ + f"DNS:{dns}" + for dns in cert.extensions.get_extension_for_class( + x509.SubjectAlternativeName + ).value.get_values_for_type(x509.DNSName) + ] + sans.extend( + [ + f"IP:{ip}" + for ip in cert.extensions.get_extension_for_class( x509.SubjectAlternativeName - ).value.get_values_for_type(x509.DNSName) + ).value.get_values_for_type(x509.IPAddress) ] - sans.extend( + ) + + sangroups = [ + sans[group : group + SAN_GROUPING] + for group in range(0, len(sans), SAN_GROUPING) + ] + + table = [ + ["Common name", cert.subject.rfc4514_string()], + [f"SANs ({len(sans)})", tabulate(sangroups, tablefmt="plain")], + ["Valid from", cert.not_valid_before_utc], + ["Valid to", cert.not_valid_after_utc], + ["Issuer", cert.issuer.rfc4514_string()], + [ + "Fingerprint", + f"{format_fingerprint(cert.fingerprint(hashes.SHA1()))} (SHA1)", + ], + ] + + if cert_chain: + table.append( [ - f"IP:{ip}" - for ip in cert.extensions.get_extension_for_class( - x509.SubjectAlternativeName - ).value.get_values_for_type(x509.IPAddress) + "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] + if cert + ] + ), ] ) - sangroups = [ - sans[group : group + SAN_GROUPING] - for group in range(0, len(sans), SAN_GROUPING) - ] + print(tabulate(table, tablefmt="plain")) - table = [ - ["Common name", cert.subject.rfc4514_string()], - [f"SANs ({len(sans)})", tabulate(sangroups, tablefmt="plain")], - ["Valid from", cert.not_valid_before_utc], - ["Valid to", cert.not_valid_after_utc], - ["Issuer", cert.issuer.rfc4514_string()], - [ - "Fingerprint", - f"{format_fingerprint(cert.fingerprint(hashes.SHA1()))} (SHA1)", - ], - ] + return 0 - 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] - if cert - ] - ), - ] - ) - - print(tabulate(table, tablefmt="plain")) - - return 0 +if __name__ == "__main__": sys.exit(main())