1#!/usr/bin/env python
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 sys
10
11def fetch_server_certificate (host, port):
12
13    import re, tempfile, os, ssl
14
15    def subproc(cmd):
16        from subprocess import Popen, PIPE, STDOUT
17        proc = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True)
18        status = proc.wait()
19        output = proc.stdout.read()
20        return status, output
21
22    def strip_to_x509_cert(certfile_contents, outfile=None):
23        m = re.search(r"^([-]+BEGIN CERTIFICATE[-]+[\r]*\n"
24                      r".*[\r]*^[-]+END CERTIFICATE[-]+)$",
25                      certfile_contents, re.MULTILINE | re.DOTALL)
26        if not m:
27            return None
28        else:
29            tn = tempfile.mktemp()
30            fp = open(tn, "w")
31            fp.write(m.group(1) + "\n")
32            fp.close()
33            try:
34                tn2 = (outfile or tempfile.mktemp())
35                status, output = subproc(r'openssl x509 -in "%s" -out "%s"' %
36                                         (tn, tn2))
37                if status != 0:
38                    raise OperationError(status, tsig, output)
39                fp = open(tn2, 'rb')
40                data = fp.read()
41                fp.close()
42                os.unlink(tn2)
43                return data
44            finally:
45                os.unlink(tn)
46
47    if sys.platform.startswith("win"):
48        tfile = tempfile.mktemp()
49        fp = open(tfile, "w")
50        fp.write("quit\n")
51        fp.close()
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 OSError(status)
64    certtext = strip_to_x509_cert(output)
65    if not certtext:
66        raise ValueError("Invalid response received from server at %s:%s" %
67                         (host, port))
68    return certtext
69
70if __name__ == "__main__":
71    if len(sys.argv) < 2:
72        sys.stderr.write(
73            "Usage:  %s HOSTNAME:PORTNUMBER [, HOSTNAME:PORTNUMBER...]\n" %
74            sys.argv[0])
75        sys.exit(1)
76    for arg in sys.argv[1:]:
77        host, port = arg.split(":")
78        sys.stdout.write(fetch_server_certificate(host, int(port)))
79    sys.exit(0)
80