Add CA chain and fingerprints
This commit is contained in:
parent
e1f7fa0989
commit
575bceec25
|
@ -6,9 +6,11 @@ import argparse
|
||||||
import socket
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.error
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from cert_chain_resolver.api import CertificateChain, resolve
|
||||||
from cryptography import x509
|
from cryptography import x509
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
@ -17,6 +19,21 @@ from tabulate import tabulate
|
||||||
SAN_GROUPING = 4
|
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(
|
def display_error(
|
||||||
site: str,
|
site: str,
|
||||||
error: Any = None,
|
error: Any = None,
|
||||||
|
@ -53,6 +70,10 @@ if __name__ == "__main__":
|
||||||
"""
|
"""
|
||||||
args = parseargs()
|
args = parseargs()
|
||||||
|
|
||||||
|
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:
|
if not parts.netloc:
|
||||||
|
@ -70,7 +91,15 @@ if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
pem_data = ssl.get_server_certificate(
|
pem_data = ssl.get_server_certificate(
|
||||||
(parts.hostname, parts.port),
|
(parts.hostname, parts.port),
|
||||||
|
timeout=10,
|
||||||
).encode("utf-8")
|
).encode("utf-8")
|
||||||
|
|
||||||
|
cert_chain = CertificateChain()
|
||||||
|
try:
|
||||||
|
cert_chain = resolve(pem_data)
|
||||||
|
except urllib.error.URLError:
|
||||||
|
pass
|
||||||
|
|
||||||
except (
|
except (
|
||||||
ConnectionRefusedError,
|
ConnectionRefusedError,
|
||||||
ssl.CertificateError,
|
ssl.CertificateError,
|
||||||
|
@ -113,11 +142,28 @@ if __name__ == "__main__":
|
||||||
["Valid to", cert.not_valid_after_utc],
|
["Valid to", cert.not_valid_after_utc],
|
||||||
["Issuer", cert.issuer.rfc4514_string()],
|
["Issuer", cert.issuer.rfc4514_string()],
|
||||||
[
|
[
|
||||||
"SHA1 fingerprint",
|
"Fingerprint",
|
||||||
":".join([format(i, "02x") for i in cert.fingerprint(hashes.SHA1())]),
|
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"))
|
print(tabulate(table, tablefmt="plain"))
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -10,7 +10,7 @@ description = "Show TLS certificate details for a given endpoint"
|
||||||
keywords = ["tls", "certificate", "python"]
|
keywords = ["tls", "certificate", "python"]
|
||||||
classifiers = ["Programming Language :: Python :: 3"]
|
classifiers = ["Programming Language :: Python :: 3"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
dependencies = ["cryptography", "tabulate"]
|
dependencies = ["cert-chain-resolver", "cryptography", "tabulate"]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|
Loading…
Reference in a new issue