Created
June 10, 2022 14:16
-
-
Save hbobenicio/cf47bf7d1627ca15ae1f2f9edb4c5662 to your computer and use it in GitHub Desktop.
Download server's certificate chain with Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import socket | |
import ssl | |
import logging | |
import os | |
import sys | |
import re | |
import argparse | |
def sanitize_alpha_only(s: str) -> str: | |
return re.sub(r'[^a-zA-Z]+', '', s) | |
def log_create() -> logging.Logger: | |
"""Creates the logging instance for this module. | |
""" | |
level_str = os.getenv('LOG_LEVEL', 'INFO') | |
level_str = sanitize_alpha_only(level_str).upper() | |
fmt = '[%(asctime)s] %(levelname)s: %(message)s' | |
# pypy dont recognize the encoding parameter | |
logging.basicConfig(stream=sys.stderr, format=fmt, | |
encoding='utf-8', level=level_str) | |
#logging.basicConfig(stream=sys.stderr, format=fmt, level=level_str) | |
return logging.getLogger(__name__) | |
# The global logging instance for this module | |
log = log_create() | |
def cli_create_parser() -> argparse.ArgumentParser: | |
"""Creates the CLI argparse instance. | |
""" | |
parser = argparse.ArgumentParser(prog='crtget', | |
description='A simple cli utility to download certificates from a server', | |
allow_abbrev=False) | |
parser.add_argument('--servername', | |
required=False, type=str, | |
help="Server Name used for the SNI extension of TLS. Defaults to the connection host") | |
parser.add_argument('-k', '--insecure', | |
required=False, action='store_true', | |
help='Skip TLS verification (Be carefull!)') | |
parser.add_argument('host', | |
type=str, | |
help='Host/IP used to connect to the server') | |
parser.add_argument('port', | |
nargs='?', default=443, type=int, | |
help='Port used to connect to the server') | |
return parser | |
def main(): | |
cli_parser: argparse.ArgumentParser = cli_create_parser() | |
args = cli_parser.parse_args() | |
host: str = args.host | |
port: int = args.port | |
servername: str = args.servername if args.servername else host | |
# TODO Not working ATM. Can I get peer certs with insecure mode? | |
insecure: bool = args.insecure | |
log.debug('OpenSSL version: %s', ssl.OPENSSL_VERSION) | |
# When calling the SSLContext constructor directly, CERT_NONE is the default | |
# https://docs.python.org/3/library/ssl.html#verifying-certificates | |
# ssl_ctx = ssl.SSLContext() if insecure else ssl.create_default_context() | |
ssl_ctx = ssl.create_default_context() | |
if insecure: | |
ssl_ctx.check_hostname = False | |
ssl_ctx.verify_mode = ssl.CERT_NONE | |
log.info('Connecting to server... host="%s" port=%d', host, port) | |
with socket.create_connection((host, port)) as sock: | |
log.info('Connected.') | |
log.info('Performing TLS Handshake... hostname="%s"', servername) | |
with ssl_ctx.wrap_socket(sock, server_hostname=servername) as ssock: | |
log.info('Secure connection established.') | |
print('Peer Name:', ssock.getpeername()) | |
print('Protocol:', ssock.version()) | |
print('Peer Cert:', ssl.ssock.getpeercert()) | |
# print('Peer Cert:', ssl.DER_cert_to_PEM_cert(ssock.getpeercert())) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment