mirror of https://github.com/scrapy/scrapy.git
62 lines
2.5 KiB
Python
62 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
import socket
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, cast
|
|
|
|
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
from cryptography.x509 import load_pem_x509_certificate
|
|
from OpenSSL import SSL
|
|
from OpenSSL.crypto import FILETYPE_PEM, load_certificate, load_privatekey
|
|
from twisted.internet.ssl import CertificateOptions, ContextFactory
|
|
|
|
from scrapy.core.downloader.tls import _TWISTED_VERSION_MAP
|
|
from scrapy.utils._deps_compat import PYOPENSSL_X509_DEPRECATED
|
|
from scrapy.utils.python import to_bytes
|
|
from scrapy.utils.ssl import _get_cert_options_version_kwargs
|
|
|
|
if TYPE_CHECKING:
|
|
from twisted.internet.interfaces import IOpenSSLContextFactory
|
|
|
|
|
|
def _free_port() -> int:
|
|
# racy but should be fine for tests
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
s.bind(("127.0.0.1", 0))
|
|
return cast("int", s.getsockname()[1])
|
|
|
|
|
|
def ssl_context_factory(
|
|
keyfile: str = "keys/localhost.key",
|
|
certfile: str = "keys/localhost.crt",
|
|
*,
|
|
cipher_string: str | None = None,
|
|
tls_min_version: str | None = None,
|
|
tls_max_version: str | None = None,
|
|
) -> IOpenSSLContextFactory:
|
|
keyfile_path = Path(__file__).parent.parent / keyfile
|
|
certfile_path = Path(__file__).parent.parent / certfile
|
|
|
|
if PYOPENSSL_X509_DEPRECATED:
|
|
cert = load_pem_x509_certificate(certfile_path.read_bytes())
|
|
key = load_pem_private_key(keyfile_path.read_bytes(), password=None)
|
|
else:
|
|
cert = load_certificate(FILETYPE_PEM, certfile_path.read_bytes()) # type: ignore[assignment]
|
|
key = load_privatekey(FILETYPE_PEM, keyfile_path.read_bytes()) # type: ignore[assignment]
|
|
|
|
tls_min = _TWISTED_VERSION_MAP.get(tls_min_version) if tls_min_version else None
|
|
tls_max = _TWISTED_VERSION_MAP.get(tls_max_version) if tls_max_version else None
|
|
tls_version_kwargs = _get_cert_options_version_kwargs(tls_min, tls_max)
|
|
# https://github.com/twisted/twisted/issues/12638
|
|
factory: CertificateOptions = CertificateOptions(
|
|
privateKey=key, # type: ignore[arg-type]
|
|
certificate=cert, # type: ignore[arg-type]
|
|
**tls_version_kwargs,
|
|
)
|
|
if cipher_string:
|
|
ctx = factory.getContext()
|
|
# disabling TLS1.3 because it unconditionally enables some strong ciphers
|
|
ctx.set_options(SSL.OP_CIPHER_SERVER_PREFERENCE | SSL.OP_NO_TLSv1_3)
|
|
ctx.set_cipher_list(to_bytes(cipher_string))
|
|
return cast("ContextFactory", factory)
|