def make_server_conn(tls_start: tls.TlsStartData) -> None: ssl_context = SSL.Context(SSL.SSLv23_METHOD) ssl_context.load_verify_locations(cafile=tlsdata.path( "../../net/data/verificationcerts/trusted-root.crt")) if alpn is not None: ssl_context.set_alpn_protos([alpn]) ssl_context.set_verify(SSL.VERIFY_PEER) tls_start.ssl_conn = SSL.Connection(ssl_context) tls_start.ssl_conn.set_connect_state() # Set SNI tls_start.ssl_conn.set_tlsext_host_name(tls_start.conn.sni.encode()) # Manually enable hostname verification. # Recent OpenSSL versions provide slightly nicer ways to do this, but they are not exposed in # cryptography and likely a PITA to add. # https://wiki.openssl.org/index.php/Hostname_validation param = SSL._lib.SSL_get0_param(tls_start.ssl_conn._ssl) # Common Name matching is disabled in both Chrome and Firefox, so we should disable it, too. # https://www.chromestatus.com/feature/4981025180483584 SSL._lib.X509_VERIFY_PARAM_set_hostflags( param, SSL._lib.X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | SSL._lib.X509_CHECK_FLAG_NEVER_CHECK_SUBJECT) SSL._openssl_assert( SSL._lib.X509_VERIFY_PARAM_set1_host( param, tls_start.conn.sni.encode(), 0) == 1)
def create_proxy_server_context( *, min_version: Version, max_version: Version, cipher_list: Optional[Iterable[str]], verify: Verify, sni: Optional[str], ca_path: Optional[str], ca_pemfile: Optional[str], client_cert: Optional[str], alpn_protos: Optional[Iterable[bytes]], ) -> SSL.Context: context: SSL.Context = _create_ssl_context( method=Method.TLS_CLIENT_METHOD, min_version=min_version, max_version=max_version, cipher_list=cipher_list, ) if verify is not Verify.VERIFY_NONE and sni is None: raise ValueError("Cannot validate certificate hostname without SNI") context.set_verify(verify.value, None) if sni is not None: assert isinstance(sni, str) # Manually enable hostname verification on the context object. # https://wiki.openssl.org/index.php/Hostname_validation param = SSL._lib.SSL_CTX_get0_param(context._context) # Matching on the CN is disabled in both Chrome and Firefox, so we disable it, too. # https://www.chromestatus.com/feature/4981025180483584 SSL._lib.X509_VERIFY_PARAM_set_hostflags( param, SSL._lib.X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | SSL._lib.X509_CHECK_FLAG_NEVER_CHECK_SUBJECT) SSL._openssl_assert( SSL._lib.X509_VERIFY_PARAM_set1_host(param, sni.encode(), 0) == 1) if ca_path is None and ca_pemfile is None: ca_pemfile = certifi.where() try: context.load_verify_locations(ca_pemfile, ca_path) except SSL.Error as e: raise RuntimeError( f"Cannot load trusted certificates ({ca_pemfile=}, {ca_path=})." ) from e # Client Certs if client_cert: try: context.use_privatekey_file(client_cert) context.use_certificate_chain_file(client_cert) except SSL.Error as e: raise RuntimeError( f"Cannot load TLS client certificate: {e}") from e if alpn_protos is not None: # advertise application layer protocols context.set_alpn_protos(alpn_protos) return context
def create_client_context( cert: str = None, sni: str = None, address: str = None, verify: int = SSL.VERIFY_NONE, **sslctx_kwargs ) -> SSL.Context: """ Args: cert: Path to a file containing both client cert and private key. sni: Server Name Indication. Required for VERIFY_PEER address: server address, used for expressive error messages only verify: A bit field consisting of OpenSSL.SSL.VERIFY_* values """ if sni is None and verify != SSL.VERIFY_NONE: raise exceptions.TlsException("Cannot validate certificate hostname without SNI") def verify_callback( conn: SSL.Connection, x509: SSL.X509, errno: int, depth: int, is_cert_verified: bool ) -> bool: if is_cert_verified and depth == 0 and not sni: conn.cert_error = exceptions.InvalidCertificateException( f"Certificate verification error for {address}: Cannot validate hostname, SNI missing." ) is_cert_verified = False elif is_cert_verified: pass else: conn.cert_error = exceptions.InvalidCertificateException( "Certificate verification error for {}: {} (errno: {}, depth: {})".format( sni, SSL._ffi.string(SSL._lib.X509_verify_cert_error_string(errno)).decode(), errno, depth ) ) # SSL_VERIFY_NONE: The handshake will be continued regardless of the verification result. return is_cert_verified context = _create_ssl_context( verify=verify, verify_callback=verify_callback, **sslctx_kwargs, ) if sni: # Manually enable hostname verification on the context object. # https://wiki.openssl.org/index.php/Hostname_validation param = SSL._lib.SSL_CTX_get0_param(context._context) # Matching on the CN is disabled in both Chrome and Firefox, so we disable it, too. # https://www.chromestatus.com/feature/4981025180483584 SSL._lib.X509_VERIFY_PARAM_set_hostflags( param, SSL._lib.X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | SSL._lib.X509_CHECK_FLAG_NEVER_CHECK_SUBJECT ) SSL._openssl_assert( SSL._lib.X509_VERIFY_PARAM_set1_host(param, sni.encode("idna"), 0) == 1 ) # Client Certs if cert: try: context.use_privatekey_file(cert) context.use_certificate_chain_file(cert) except SSL.Error as v: raise exceptions.TlsException("SSL client certificate error: %s" % str(v)) return context