1#!/usr/bin/env python3 2# 3# fetch the certificate that the server(s) are providing in PEM form 4# 5# args are HOST:PORT [, HOST:PORT...] 6# 7# By Bill Janssen. 8 9import re 10import os 11import sys 12import tempfile 13 14 15def fetch_server_certificate (host, port): 16 17 def subproc(cmd): 18 from subprocess import Popen, PIPE, STDOUT 19 proc = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) 20 status = proc.wait() 21 output = proc.stdout.read() 22 return status, output 23 24 def strip_to_x509_cert(certfile_contents, outfile=None): 25 m = re.search(br"^([-]+BEGIN CERTIFICATE[-]+[\r]*\n" 26 br".*[\r]*^[-]+END CERTIFICATE[-]+)$", 27 certfile_contents, re.MULTILINE | re.DOTALL) 28 if not m: 29 return None 30 else: 31 tn = tempfile.mktemp() 32 with open(tn, "wb") as fp: 33 fp.write(m.group(1) + b"\n") 34 try: 35 tn2 = (outfile or tempfile.mktemp()) 36 status, output = subproc(r'openssl x509 -in "%s" -out "%s"' % 37 (tn, tn2)) 38 if status != 0: 39 raise RuntimeError('OpenSSL x509 failed with status %s and ' 40 'output: %r' % (status, output)) 41 with open(tn2, 'rb') as fp: 42 data = fp.read() 43 os.unlink(tn2) 44 return data 45 finally: 46 os.unlink(tn) 47 48 if sys.platform.startswith("win"): 49 tfile = tempfile.mktemp() 50 with open(tfile, "w") as fp: 51 fp.write("quit\n") 52 try: 53 status, output = subproc( 54 'openssl s_client -connect "%s:%s" -showcerts < "%s"' % 55 (host, port, tfile)) 56 finally: 57 os.unlink(tfile) 58 else: 59 status, output = subproc( 60 'openssl s_client -connect "%s:%s" -showcerts < /dev/null' % 61 (host, port)) 62 if status != 0: 63 raise RuntimeError('OpenSSL connect failed with status %s and ' 64 'output: %r' % (status, output)) 65 certtext = strip_to_x509_cert(output) 66 if not certtext: 67 raise ValueError("Invalid response received from server at %s:%s" % 68 (host, port)) 69 return certtext 70 71 72if __name__ == "__main__": 73 if len(sys.argv) < 2: 74 sys.stderr.write( 75 "Usage: %s HOSTNAME:PORTNUMBER [, HOSTNAME:PORTNUMBER...]\n" % 76 sys.argv[0]) 77 sys.exit(1) 78 for arg in sys.argv[1:]: 79 host, port = arg.split(":") 80 sys.stdout.buffer.write(fetch_server_certificate(host, int(port))) 81 sys.exit(0) 82