| 20 | |
| 21 | |
| 22 | class MDCertUtil(object): |
| 23 | # Utility class for inspecting certificates in test cases |
| 24 | # Uses PyOpenSSL: https://pyopenssl.org/en/stable/index.html |
| 25 | |
| 26 | @classmethod |
| 27 | def load_server_cert(cls, host_ip, host_port, host_name, tls=None, ciphers=None): |
| 28 | ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) |
| 29 | if tls is not None and tls != 1.0: |
| 30 | ctx.set_options(OpenSSL.SSL.OP_NO_TLSv1) |
| 31 | if tls is not None and tls != 1.1: |
| 32 | ctx.set_options(OpenSSL.SSL.OP_NO_TLSv1_1) |
| 33 | if tls is not None and tls != 1.2: |
| 34 | ctx.set_options(OpenSSL.SSL.OP_NO_TLSv1_2) |
| 35 | if tls is not None and tls != 1.3 and hasattr(OpenSSL.SSL, "OP_NO_TLSv1_3"): |
| 36 | ctx.set_options(OpenSSL.SSL.OP_NO_TLSv1_3) |
| 37 | if ciphers is not None: |
| 38 | ctx.set_cipher_list(ciphers) |
| 39 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 40 | connection = OpenSSL.SSL.Connection(ctx, s) |
| 41 | connection.connect((host_ip, int(host_port))) |
| 42 | connection.setblocking(1) |
| 43 | connection.set_tlsext_host_name(host_name.encode('utf-8')) |
| 44 | connection.do_handshake() |
| 45 | peer_cert = connection.get_peer_certificate() |
| 46 | return MDCertUtil(None, cert=peer_cert) |
| 47 | |
| 48 | @classmethod |
| 49 | def parse_pem_cert(cls, text): |
| 50 | cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, text.encode('utf-8')) |
| 51 | return MDCertUtil(None, cert=cert) |
| 52 | |
| 53 | @classmethod |
| 54 | def get_plain(cls, url, timeout): |
| 55 | server = urlparse(url) |
| 56 | try_until = time.time() + timeout |
| 57 | while time.time() < try_until: |
| 58 | # noinspection PyBroadException |
| 59 | try: |
| 60 | c = HTTPConnection(server.hostname, server.port, timeout=timeout) |
| 61 | c.request('GET', server.path) |
| 62 | resp = c.getresponse() |
| 63 | data = resp.read() |
| 64 | c.close() |
| 65 | return data |
| 66 | except IOError: |
| 67 | log.debug("connect error:", sys.exc_info()[0]) |
| 68 | time.sleep(.1) |
| 69 | except: |
| 70 | log.error("Unexpected error:", sys.exc_info()[0]) |
| 71 | log.error("Unable to contact server after %d sec" % timeout) |
| 72 | return None |
| 73 | |
| 74 | def __init__(self, cert_path, cert=None): |
| 75 | if cert_path is not None: |
| 76 | self.cert_path = cert_path |
| 77 | # load certificate and private key |
| 78 | if cert_path.startswith("http"): |
| 79 | cert_data = self.get_plain(cert_path, 1) |
no outgoing calls