Skip to content

Instantly share code, notes, and snippets.

@hbobenicio
Created June 10, 2022 14:16
Show Gist options
  • Save hbobenicio/cf47bf7d1627ca15ae1f2f9edb4c5662 to your computer and use it in GitHub Desktop.
Save hbobenicio/cf47bf7d1627ca15ae1f2f9edb4c5662 to your computer and use it in GitHub Desktop.
Download server's certificate chain with Python
#!/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