Example #1
0
    def setUp(self):
        """
        Create a L{PantheonHTTPChecker} pointed at a mock authentication service
        with some simple site and user information.
        """
        self.site = 'example.com'
        self.cwd = '/some/path'
        self.uid = 1542
        self.username = '******'
        self.password = '******'
        keyString = FilePath(__file__).sibling('id_rsa').getContent()
        self.privateKey = Key.fromString(keyString)

        caKeyString = FilePath(__file__).sibling('cakey.pem').getContent()
        self.caKey = KeyPair.load(caKeyString, FILETYPE_PEM)
        caCertString = FilePath(__file__).sibling('cacert.pem').getContent()
        self.caCert = PrivateCertificate.load(
            caCertString, self.caKey, FILETYPE_PEM)

        self.resource = MockPantheonAuthResource(
            sites={self.site: [self.username]},
            authorizations={self.site: dict(cwd=self.cwd, uid=self.uid)},
            passwords={self.username: self.password},
            keys={self.username: self.privateKey},
            )
        self.server = MockPantheonAuthServer(
            reactor, self.resource, self.caCert)
        self.server.startService()
        self.addCleanup(self.server.stopService)
Example #2
0
def get_context_factory(cert_path, pkey_path):
    """OpenSSL context factory.

    Generates an OpenSSL context factory using Twisted's CertificateOptions class.
    This will keep a server cipher order.

    Args:
        cert_path (string): The path to the certificate file
        pkey_path (string): The path to the private key file

    Returns:
        twisted.internet.ssl.CertificateOptions: An OpenSSL context factory
    """

    with open(cert_path) as cert:
        certificate = Certificate.loadPEM(cert.read()).original
    with open(pkey_path) as pkey:
        private_key = KeyPair.load(pkey.read(), FILETYPE_PEM).original
    ciphers = AcceptableCiphers.fromOpenSSLCipherString(TLS_CIPHERS)
    cert_options = CertificateOptions(
        privateKey=private_key,
        certificate=certificate,
        raiseMinimumTo=TLSVersion.TLSv1_2,
        acceptableCiphers=ciphers,
    )
    ctx = cert_options.getContext()
    ctx.use_certificate_chain_file(cert_path)
    ctx.set_options(SSL_OP_NO_RENEGOTIATION)

    return cert_options
Example #3
0
 def getServerContext(self):
     """
     Return a new SSL context suitable for use in a test server.
     """
     pem = self._pem.getContent()
     cert = PrivateCertificate.load(
         pem, KeyPair.load(pem, FILETYPE_PEM), FILETYPE_PEM)
     return cert.options()
Example #4
0
 def getServerContext(self):
     """
     Return a new SSL context suitable for use in a test server.
     """
     cert = PrivateCertificate.load(
         self._certificateText,
         KeyPair.load(self._privateKeyText, FILETYPE_PEM), FILETYPE_PEM)
     return cert.options()
 def getServerContext(self):
     """
     Return a new SSL context suitable for use in a test server.
     """
     pem = self._pem.getContent()
     cert = PrivateCertificate.load(
         pem, KeyPair.load(pem, FILETYPE_PEM), FILETYPE_PEM)
     return cert.options()
Example #6
0
 def getServerContext(self):
     """
     Return a new SSL context suitable for use in a test server.
     """
     cert = PrivateCertificate.load(
         self._certificateText,
         KeyPair.load(self._privateKeyText, FILETYPE_PEM),
         FILETYPE_PEM)
     return cert.options()
Example #7
0
    def start_ssl(self):
        log.debug("Enabling SSL with PKey: %s, Cert: %s", self.pkey, self.cert)
        check_ssl_keys()

        with open(configmanager.get_config_dir(self.cert)) as cert:
            certificate = Certificate.loadPEM(cert.read()).original
        with open(configmanager.get_config_dir(self.pkey)) as pkey:
            private_key = KeyPair.load(pkey.read(), FILETYPE_PEM).original
        options = CertificateOptions(privateKey=private_key, certificate=certificate, method=SSL.SSLv23_METHOD)
        options.getContext().set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)

        self.socket = reactor.listenSSL(self.port, self.site, options)
        log.info("Serving on %s:%s view at https://127.0.0.1:%s", "0.0.0.0", self.port, self.port)
Example #8
0
def _create_tls_client_context(config, cbdir, log):
    """
    Create a CertificateOptions object for use with TLS listening endpoints.
    """
    # server hostname: The expected name of the remote host.
    hostname = config['hostname']

    # explicit trust (certificate) root
    ca_certs = None
    if 'ca_certificates' in config:
        log.info("TLS client using explicit trust ({cnt_certs} certificates)", cnt_certs=len(config['ca_certificates']))
        ca_certs = []
        for cert_fname in [os.path.abspath(os.path.join(cbdir, x)) for x in (config['ca_certificates'])]:
            cert = crypto.load_certificate(
                crypto.FILETYPE_PEM,
                six.u(open(cert_fname, 'r').read())
            )
            log.info("TLS client trust root CA certificate loaded from '{fname}'", fname=cert_fname)
            ca_certs.append(cert)
        ca_certs = OpenSSLCertificateAuthorities(ca_certs)
    else:
        log.info("TLS client using platform trust")

    # client key/cert to use
    client_cert = None
    if 'key' in config:
        if 'certificate' not in config:
            raise Exception('TLS client key present, but certificate missing')

        key_fname = os.path.abspath(os.path.join(cbdir, config['key']))
        with open(key_fname, 'r') as f:
            private_key = KeyPair.load(f.read(), format=crypto.FILETYPE_PEM)
            log.info("Loaded client TLS key from '{key_fname}'", key_fname=key_fname)

        cert_fname = os.path.abspath(os.path.join(cbdir, config['certificate']))
        with open(cert_fname, 'r') as f:
            cert = Certificate.loadPEM(f.read(),)
            log.info("Loaded client TLS certificate from '{cert_fname}' (cn='{cert_cn}', sha256={cert_sha256}..)",
                     cert_fname=cert_fname,
                     cert_cn=cert.getSubject().CN,
                     cert_sha256=cert.digest('sha256')[:12])

        client_cert = PrivateCertificate.fromCertificateAndKeyPair(cert, private_key)
    else:
        if 'certificate' in config:
            log.warn('TLS client certificate present, but key is missing')

    # create TLS client context
    ctx = optionsForClientTLS(hostname, trustRoot=ca_certs, clientCertificate=client_cert)

    return ctx
Example #9
0
def _create_tls_client_context(config, cbdir, log):
    """
    Create a CertificateOptions object for use with TLS listening endpoints.
    """
    # server hostname: The expected name of the remote host.
    hostname = config['hostname']

    # explicit trust (certificate) root
    ca_certs = None
    if 'ca_certificates' in config:
        log.info("TLS client using explicit trust ({cnt_certs} certificates)", cnt_certs=len(config['ca_certificates']))
        ca_certs = []
        for cert_fname in [os.path.abspath(os.path.join(cbdir, x)) for x in (config['ca_certificates'])]:
            cert = crypto.load_certificate(
                crypto.FILETYPE_PEM,
                six.u(open(cert_fname, 'r').read())
            )
            log.info("TLS client trust root CA certificate loaded from '{fname}'", fname=cert_fname)
            ca_certs.append(cert)
        ca_certs = OpenSSLCertificateAuthorities(ca_certs)
    else:
        log.info("TLS client using platform trust")

    # client key/cert to use
    client_cert = None
    if 'key' in config:
        if 'certificate' not in config:
            raise Exception('TLS client key present, but certificate missing')

        key_fname = os.path.abspath(os.path.join(cbdir, config['key']))
        with open(key_fname, 'r') as f:
            private_key = KeyPair.load(f.read(), format=crypto.FILETYPE_PEM)
            log.info("Loaded client TLS key from '{key_fname}'", key_fname=key_fname)

        cert_fname = os.path.abspath(os.path.join(cbdir, config['certificate']))
        with open(cert_fname, 'r') as f:
            cert = Certificate.loadPEM(f.read(),)
            log.info("Loaded client TLS certificate from '{cert_fname}' (cn='{cert_cn}', sha256={cert_sha256}..)",
                     cert_fname=cert_fname,
                     cert_cn=cert.getSubject().CN,
                     cert_sha256=cert.digest('sha256')[:12])

        client_cert = PrivateCertificate.fromCertificateAndKeyPair(cert, private_key)
    else:
        if 'certificate' in config:
            log.warn('TLS client certificate present, but key is missing')

    # create TLS client context
    ctx = optionsForClientTLS(hostname, trustRoot=ca_certs, clientCertificate=client_cert)

    return ctx
Example #10
0
    def from_path(cls, path):
        """
        :param FilePath path: Directory where private key and certificate are
            stored.
        """
        if not path.isdir():
            raise PathError(
                b"Path {path} is not a directory.".format(path=path.path)
            )

        certPath = path.child(certificate_filename)
        keyPath = path.child(key_filename)

        if not certPath.isfile():
            raise PathError(
                b"Certificate file {path} does not exist.".format(
                    path=certPath.path)
            )

        if not keyPath.isfile():
            raise PathError(
                b"Private key file {path} does not exist.".format(
                    path=keyPath.path)
            )

        try:
            certFile = certPath.open()
        except IOError:
            raise PathError(
                (b"Certificate file {path} could not be opened. "
                 b"Check file permissions.").format(
                    path=certPath.path)
            )

        try:
            keyFile = keyPath.open()
        except IOError:
            raise PathError(
                (b"Private key file {path} could not be opened. "
                 b"Check file permissions.").format(
                    path=keyPath.path)
            )

        certificate = Certificate.load(
            certFile.read(), format=crypto.FILETYPE_PEM)
        keypair = FlockerKeyPair(
            keypair=KeyPair.load(keyFile.read(), format=crypto.FILETYPE_PEM)
        )

        return cls(path=path, certificate=certificate, keypair=keypair)
Example #11
0
    def start_ssl(self):
        log.debug("Enabling SSL with PKey: %s, Cert: %s", self.pkey, self.cert)
        check_ssl_keys()

        with open(configmanager.get_config_dir(self.cert)) as cert:
            certificate = Certificate.loadPEM(cert.read()).original
        with open(configmanager.get_config_dir(self.pkey)) as pkey:
            private_key = KeyPair.load(pkey.read(), FILETYPE_PEM).original
        options = CertificateOptions(privateKey=private_key, certificate=certificate, method=SSL.SSLv23_METHOD)
        ctx = options.getContext()
        ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
        ctx.use_certificate_chain_file(configmanager.get_config_dir(self.cert))

        self.socket = reactor.listenSSL(self.port, self.site, options, interface=self.interface)
        log.info("Serving on %s:%s view at https://%s:%s", self.interface, self.port, self.interface, self.port)
Example #12
0
    def start_ssl(self):
        check_ssl_keys()
        log.debug('Enabling SSL with PKey: %s, Cert: %s', self.pkey, self.cert)

        with open(configmanager.get_config_dir(self.cert)) as cert:
            certificate = Certificate.loadPEM(cert.read()).original
        with open(configmanager.get_config_dir(self.pkey)) as pkey:
            private_key = KeyPair.load(pkey.read(), FILETYPE_PEM).original
        options = CertificateOptions(privateKey=private_key, certificate=certificate, method=SSL.SSLv23_METHOD)
        ctx = options.getContext()
        ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
        ctx.use_certificate_chain_file(configmanager.get_config_dir(self.cert))

        self.socket = reactor.listenSSL(self.port, self.site, options, interface=self.interface)
        ip = self.socket.getHost().host
        ip = '[%s]' % ip if is_ipv6(ip) else ip
        log.info('Serving at https://%s:%s%s', ip, self.port, self.base)
Example #13
0
def load_key_file(path):
    """
    Load a private key from a specified path.

    :param FilePath path: Absolute path to certificate file.

    :return: A ``ComparableKeyPair`` instance representing the parsed
        key data.
    """
    try:
        key_file = path.open()
    except IOError as e:
        code, failure = e
        raise PathError(b"Private key file could not be opened.", e.filename,
                        code, failure)
    keypair = ComparableKeyPair(
        keypair=KeyPair.load(key_file.read(), format=crypto.FILETYPE_PEM))
    return keypair
Example #14
0
def objectsFromPEM(pemdata):
    """
    Load some objects from a PEM.
    """
    certificates = []
    keys = []
    for line in pemdata.split("\n"):
        if line.startswith('-----BEGIN'):
            if 'CERTIFICATE' in line:
                blobs = certificates
            else:
                blobs = keys
            blobs.append('')
        blobs[-1] += line
        blobs[-1] += '\n'
    keys = [KeyPair.load(key, FILETYPE_PEM) for key in keys]
    certificates = [
        Certificate.loadPEM(certificate) for certificate in certificates
    ]
    return PEMObjects(keys=keys, certificates=certificates)
def objectsFromPEM(pemdata):
    """
    Load some objects from a PEM.
    """
    certificates = []
    keys = []
    blobs = [b""]
    for line in pemdata.split(b"\n"):
        if line.startswith(b'-----BEGIN'):
            if b'CERTIFICATE' in line:
                blobs = certificates
            else:
                blobs = keys
            blobs.append(b'')
        blobs[-1] += line
        blobs[-1] += b'\n'
    keys = [KeyPair.load(key, FILETYPE_PEM) for key in keys]
    certificates = [Certificate.loadPEM(certificate)
                    for certificate in certificates]
    return PEMObjects(keys=keys, certificates=certificates)
Example #16
0
def load_key_file(path):
    """
    Load a private key from a specified path.

    :param FilePath path: Absolute path to certificate file.

    :return: A ``ComparableKeyPair`` instance representing the parsed
        key data.
    """
    try:
        key_file = path.open()
    except IOError as e:
        code, failure = e
        raise PathError(
            b"Private key file could not be opened.",
            e.filename, code, failure
        )
    keypair = ComparableKeyPair(
        keypair=KeyPair.load(key_file.read(), format=crypto.FILETYPE_PEM)
    )
    return keypair
Example #17
0
def load_certificate_from_path(path, key_filename, cert_filename):
    """
    Load a certificate and keypair from a specified path.

    :param FilePath path: Directory where certificate and key files
        are stored.
    :param bytes key_filename: The file name of the private key.
    :param bytes cert_filename: The file name of the certificate.

    :return: A ``tuple`` containing the loaded key and certificate
        instances.
    """
    certPath = path.child(cert_filename)
    keyPath = path.child(key_filename)

    try:
        certFile = certPath.open()
    except IOError as e:
        code, failure = e
        raise PathError(
            b"Certificate file could not be opened.",
            e.filename, code, failure
        )

    try:
        keyFile = keyPath.open()
    except IOError as e:
        code, failure = e
        raise PathError(
            b"Private key file could not be opened.",
            e.filename, code, failure
        )

    certificate = Certificate.load(
        certFile.read(), format=crypto.FILETYPE_PEM)
    keypair = ComparableKeyPair(
        keypair=KeyPair.load(keyFile.read(), format=crypto.FILETYPE_PEM)
    )
    return (keypair, certificate)
Example #18
0
def load_certificate_from_path(path, key_filename, cert_filename):
    """
    Load a certificate and keypair from a specified path.

    :param FilePath path: Directory where certificate and key files
        are stored.
    :param bytes key_filename: The file name of the private key.
    :param bytes cert_filename: The file name of the certificate.

    :return: A ``tuple`` containing the loaded key and certificate
        instances.
    """
    certPath = path.child(cert_filename)
    keyPath = path.child(key_filename)

    try:
        certFile = certPath.open()
    except IOError as e:
        code, failure = e
        raise PathError(
            b"Certificate file could not be opened.",
            e.filename, code, failure
        )

    try:
        keyFile = keyPath.open()
    except IOError as e:
        code, failure = e
        raise PathError(
            b"Private key file could not be opened.",
            e.filename, code, failure
        )

    certificate = Certificate.load(
        certFile.read(), format=crypto.FILETYPE_PEM)
    keypair = ComparableKeyPair(
        keypair=KeyPair.load(keyFile.read(), format=crypto.FILETYPE_PEM)
    )
    return (keypair, certificate)
Example #19
0
    def start_ssl(self):
        check_ssl_keys()
        log.debug('Enabling SSL with PKey: %s, Cert: %s', self.pkey, self.cert)

        with open(configmanager.get_config_dir(self.cert)) as cert:
            certificate = Certificate.loadPEM(cert.read()).original
        with open(configmanager.get_config_dir(self.pkey)) as pkey:
            private_key = KeyPair.load(pkey.read(), FILETYPE_PEM).original
        options = CertificateOptions(privateKey=private_key,
                                     certificate=certificate,
                                     method=SSL.SSLv23_METHOD)
        ctx = options.getContext()
        ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
        ctx.use_certificate_chain_file(configmanager.get_config_dir(self.cert))

        self.socket = reactor.listenSSL(self.port,
                                        self.site,
                                        options,
                                        interface=self.interface)
        ip = self.socket.getHost().host
        ip = '[%s]' % ip if is_ipv6(ip) else ip
        log.info('Serving at https://%s:%s%s', ip, self.port, self.base)
Example #20
0
def _create_tls_server_context(config, cbdir, log):
    """
    Create a CertificateOptions object for use with TLS listening endpoints.
    """
    # server private key
    key_filepath = abspath(join(cbdir, config['key']))
    log.info("Loading server TLS key from {key_filepath}", key_filepath=key_filepath)
    with open(key_filepath) as key_file:
        # server certificate (but only the server cert, no chain certs)
        cert_filepath = abspath(join(cbdir, config['certificate']))
        log.info("Loading server TLS certificate from {cert_filepath}", cert_filepath=cert_filepath)
        with open(cert_filepath) as cert_file:
            key = KeyPair.load(key_file.read(), crypto.FILETYPE_PEM).original
            cert = Certificate.loadPEM(cert_file.read()).original

    # list of certificates that complete your verification chain
    extra_certs = None
    if 'chain_certificates' in config:
        extra_certs = []
        for fname in config['chain_certificates']:
            extra_cert_filepath = abspath(join(cbdir, fname))
            with open(extra_cert_filepath, 'r') as f:
                extra_certs.append(Certificate.loadPEM(f.read()).original)
            log.info("Loading server TLS chain certificate from {extra_cert_filepath}", extra_cert_filepath=extra_cert_filepath)

    # list of certificate authority certificate objects to use to verify the peer's certificate
    ca_certs = None
    if 'ca_certificates' in config:
        ca_certs = []
        for fname in config['ca_certificates']:
            ca_cert_filepath = abspath(join(cbdir, fname))
            with open(ca_cert_filepath, 'r') as f:
                ca_certs.append(Certificate.loadPEM(f.read()).original)
            log.info("Loading server TLS CA certificate from {ca_cert_filepath}", ca_cert_filepath=ca_cert_filepath)

    # ciphers we accept
    #
    # We prefer to make every single cipher (6 in total) _explicit_ (to reduce chances either we or the pattern-matching
    # language inside OpenSSL messes up) and drop support for Windows XP (we do WebSocket anyway).
    #
    # We don't use AES256 and SHA384, to reduce number of ciphers and since the additional
    # security gain seems not worth the additional performance drain.
    #
    # We also don't use ECDSA, since EC certificates a rare in the wild.
    #
    # The effective list of ciphers determined from an OpenSSL cipher string:
    #
    #   openssl ciphers -v 'ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:'
    #
    # References:
    #
    #  * https://www.ssllabs.com/ssltest/analyze.html?d=myserver.com
    #  * http://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
    #  * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
    #  * https://wiki.mozilla.org/Talk:Security/Server_Side_TLS
    #
    if 'ciphers' in config:
        log.info("Using explicit TLS ciphers from config")
        crossbar_ciphers = AcceptableCiphers.fromOpenSSLCipherString(config['ciphers'])
    else:
        log.info("Using secure default TLS ciphers")
        crossbar_ciphers = AcceptableCiphers.fromOpenSSLCipherString(
            # AEAD modes (GCM)
            # 'ECDHE-ECDSA-AES128-GCM-SHA256:'
            'ECDHE-RSA-AES128-GCM-SHA256:'
            # 'ECDHE-ECDSA-AES256-GCM-SHA384:'
            # 'ECDHE-RSA-AES256-GCM-SHA384:'
            'DHE-RSA-AES128-GCM-SHA256:'
            # 'DHE-RSA-AES256-GCM-SHA384:'

            # CBC modes
            'ECDHE-RSA-AES128-SHA256:'
            'DHE-RSA-AES128-SHA256:'
            'ECDHE-RSA-AES128-SHA:'
            'DHE-RSA-AES128-SHA:'
        )

    # DH modes require a parameter file
    if 'dhparam' in config:
        dhpath = FilePath(abspath(join(cbdir, config['dhparam'])))
        dh_params = DiffieHellmanParameters.fromFile(dhpath)
    else:
        dh_params = None
        log.warn("No OpenSSL DH parameter file set - DH cipher modes will be deactive!")

    # create a TLS context factory
    # see: https://twistedmatrix.com/documents/current/api/twisted.internet.ssl.CertificateOptions.html
    ctx = CertificateOptions(
        privateKey=key,
        certificate=cert,
        extraCertChain=extra_certs,
        verify=(ca_certs is not None),
        caCerts=ca_certs,
        dhParameters=dh_params,
        acceptableCiphers=crossbar_ciphers,

        # TLS hardening
        method=TLSv1_2_METHOD,
        enableSingleUseKeys=True,
        enableSessions=False,
        enableSessionTickets=False,
        fixBrokenPeers=False,
    )

    # Without a curve being set, ECDH won't be available even if listed
    # in acceptable ciphers!
    #
    # The curves available in OpenSSL can be listed:
    #
    #   openssl ecparam -list_curves
    #
    # prime256v1: X9.62/SECG curve over a 256 bit prime field
    #
    # This is elliptic curve "NIST P-256" from here
    # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
    #
    # This seems to be the most widely used curve
    #
    # http://crypto.stackexchange.com/questions/11310/with-openssl-and-ecdhe-how-to-show-the-actual-curve-being-used
    #
    # and researchers think it is "ok" (other than wrt timing attacks etc)
    #
    # https://twitter.com/hyperelliptic/status/394258454342148096
    #
    if ctx._ecCurve is None:
        log.warn("No OpenSSL elliptic curve set - EC cipher modes will be deactive!")
    else:
        if ctx._ecCurve.snName != "prime256v1":
            log.info("OpenSSL is using elliptic curve {curve}", curve=ctx._ecCurve.snName)
        else:
            log.info("OpenSSL is using elliptic curve prime256v1 (NIST P-256)")

    return ctx
Example #21
0
def _create_tls_server_context(config, cbdir, log):
    """
    Create a CertificateOptions object for use with TLS listening endpoints.
    """
    # server private key
    key_filepath = abspath(join(cbdir, config['key']))
    log.info("Loading server TLS key from {key_filepath}",
             key_filepath=key_filepath)
    with open(key_filepath) as key_file:
        # server certificate (but only the server cert, no chain certs)
        cert_filepath = abspath(join(cbdir, config['certificate']))
        log.info("Loading server TLS certificate from {cert_filepath}",
                 cert_filepath=cert_filepath)
        with open(cert_filepath) as cert_file:
            key = KeyPair.load(key_file.read(), crypto.FILETYPE_PEM).original
            cert = Certificate.loadPEM(cert_file.read()).original

    # list of certificates that complete your verification chain
    extra_certs = None
    if 'chain_certificates' in config:
        extra_certs = []
        for fname in config['chain_certificates']:
            extra_cert_filepath = abspath(join(cbdir, fname))
            with open(extra_cert_filepath, 'r') as f:
                extra_certs.append(Certificate.loadPEM(f.read()).original)
            log.info(
                "Loading server TLS chain certificate from {extra_cert_filepath}",
                extra_cert_filepath=extra_cert_filepath)

    # list of certificate authority certificate objects to use to verify the peer's certificate
    ca_certs = None
    if 'ca_certificates' in config:
        ca_certs = []
        for fname in config['ca_certificates']:
            ca_cert_filepath = abspath(join(cbdir, fname))
            with open(ca_cert_filepath, 'r') as f:
                ca_certs.append(Certificate.loadPEM(f.read()).original)
            log.info(
                "Loading server TLS CA certificate from {ca_cert_filepath}",
                ca_cert_filepath=ca_cert_filepath)

    # ciphers we accept
    #
    # We prefer to make every single cipher (6 in total) _explicit_ (to reduce chances either we or the pattern-matching
    # language inside OpenSSL messes up) and drop support for Windows XP (we do WebSocket anyway).
    #
    # We don't use AES256 and SHA384, to reduce number of ciphers and since the additional
    # security gain seems not worth the additional performance drain.
    #
    # We also don't use ECDSA, since EC certificates a rare in the wild.
    #
    # The effective list of ciphers determined from an OpenSSL cipher string:
    #
    #   openssl ciphers -v 'ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:'
    #
    # References:
    #
    #  * https://www.ssllabs.com/ssltest/analyze.html?d=myserver.com
    #  * http://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
    #  * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
    #  * https://wiki.mozilla.org/Talk:Security/Server_Side_TLS
    #
    if 'ciphers' in config:
        log.info("Using explicit TLS ciphers from config")
        crossbar_ciphers = AcceptableCiphers.fromOpenSSLCipherString(
            config['ciphers'])
    else:
        log.info("Using secure default TLS ciphers")
        crossbar_ciphers = AcceptableCiphers.fromOpenSSLCipherString(
            # AEAD modes (GCM)
            # 'ECDHE-ECDSA-AES128-GCM-SHA256:'
            'ECDHE-RSA-AES128-GCM-SHA256:'
            # 'ECDHE-ECDSA-AES256-GCM-SHA384:'
            # 'ECDHE-RSA-AES256-GCM-SHA384:'
            'DHE-RSA-AES128-GCM-SHA256:'
            # 'DHE-RSA-AES256-GCM-SHA384:'

            # CBC modes
            'ECDHE-RSA-AES128-SHA256:'
            'DHE-RSA-AES128-SHA256:'
            'ECDHE-RSA-AES128-SHA:'
            'DHE-RSA-AES128-SHA:')

    # DH modes require a parameter file
    if 'dhparam' in config:
        dhpath = FilePath(abspath(join(cbdir, config['dhparam'])))
        dh_params = DiffieHellmanParameters.fromFile(dhpath)
    else:
        dh_params = None
        log.warn(
            "No OpenSSL DH parameter file set - DH cipher modes will be deactive!"
        )

    ctx = CertificateOptions(
        privateKey=key,
        certificate=cert,
        extraCertChain=extra_certs,
        verify=(ca_certs is not None),
        caCerts=ca_certs,
        dhParameters=dh_params,
        acceptableCiphers=crossbar_ciphers,

        # Disable SSLv3 and TLSv1 -- only allow TLSv1.1 or higher
        #
        # We are using Twisted private stuff (from twisted.internet._sslverify import TLSVersion),
        # as OpenSSL.SSL.TLSv1_1_METHOD wont work:
        #
        # [ERROR] File "../twisted/internet/_sslverify.py", line 1530, in __init__
        #    if raiseMinimumTo > self._defaultMinimumTLSVersion:
        #       builtins.TypeError: '>' not supported between instances of 'int' and 'NamedConstant'
        #
        raiseMinimumTo=TLSVersion.TLSv1_1,

        # TLS hardening
        enableSingleUseKeys=True,
        enableSessions=False,
        enableSessionTickets=False,
        fixBrokenPeers=False,
    )

    # Without a curve being set, ECDH won't be available even if listed
    # in acceptable ciphers!
    #
    # The curves available in OpenSSL can be listed:
    #
    #   openssl ecparam -list_curves
    #
    # prime256v1: X9.62/SECG curve over a 256 bit prime field
    #
    # This is elliptic curve "NIST P-256" from here
    # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
    #
    # This seems to be the most widely used curve
    #
    # http://crypto.stackexchange.com/questions/11310/with-openssl-and-ecdhe-how-to-show-the-actual-curve-being-used
    #
    # and researchers think it is "ok" (other than wrt timing attacks etc)
    #
    # https://twitter.com/hyperelliptic/status/394258454342148096
    #
    if False:
        # FIXME: this doesn't work anymore with Twisted 18.4. (there now seems more complex machinery
        # in self._ecChooser)
        if ctx._ecCurve is None:
            log.warn(
                "No OpenSSL elliptic curve set - EC cipher modes will be deactive!"
            )
        else:
            if ctx._ecCurve.snName != "prime256v1":
                log.info("OpenSSL is using elliptic curve {curve}",
                         curve=ctx._ecCurve.snName)
            else:
                log.info(
                    "OpenSSL is using elliptic curve prime256v1 (NIST P-256)")

    return ctx
Example #22
0
def create_connecting_endpoint_from_config(config, cbdir, reactor):
    """
    Create a Twisted stream client endpoint from a Crossbar.io transport configuration.

    See: https://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IStreamClientEndpoint.html

    :param config: The transport configuration.
    :type config: dict
    :param cbdir: Crossbar.io node directory (we need this for Unix domain socket paths and TLS key/certificates).
    :type cbdir: str
    :param reactor: The reactor to use for endpoint creation.
    :type reactor: obj

    :returns obj -- An instance implementing IStreamClientEndpoint
    """
    endpoint = None
    log = make_logger()

    # a TCP endpoint
    #
    if config['type'] == 'tcp':

        # the TCP protocol version (v4 or v6)
        #
        version = int(config.get('version', 4))

        # the host to connect to
        #
        host = str(config['host'])

        # the port to connect to
        #
        port = int(config['port'])

        # connection timeout in seconds
        #
        timeout = int(config.get('timeout', 10))

        if 'tls' in config:
            if _HAS_TLS:
                # if the config specified any CA certificates, we use those (only!)
                if 'ca_certificates' in config['tls']:
                    ca_certs = []
                    for cert_fname in config['tls']['ca_certificates']:
                        cert = crypto.load_certificate(
                            crypto.FILETYPE_PEM,
                            six.u(open(cert_fname, 'r').read()))
                        log.info("Loaded CA certificate '{fname}'",
                                 fname=cert_fname)
                        ca_certs.append(cert)

                    client_cert = None
                    if 'key' in config['tls']:
                        with open(config['tls']['certificate'], 'r') as f:
                            cert = Certificate.loadPEM(f.read(), )
                            log.info(
                                "{fname}: CN={subj.CN}, sha={sha}",
                                fname=config['tls']['certificate'],
                                subj=cert.getSubject(),
                                sha=cert.digest('sha'),
                            )

                        with open(config['tls']['key'], 'r') as f:
                            private_key = KeyPair.load(
                                f.read(),
                                format=crypto.FILETYPE_PEM,
                            )

                            log.info(
                                "{fname}: {key}",
                                fname=config['tls']['key'],
                                key=private_key.inspect(),
                            )

                        client_cert = PrivateCertificate.fromCertificateAndKeyPair(
                            cert, private_key)

                    # XXX OpenSSLCertificateAuthorities is a "private"
                    # class, in _sslverify, so we shouldn't really be
                    # using it. However, while you can pass a single
                    # Certificate as trustRoot= there's no way to pass
                    # multiple ones.
                    # XXX ...but maybe the config should only allow
                    # the user to configure a single cert to trust
                    # here anyway?
                    options = optionsForClientTLS(
                        config['tls']['hostname'],
                        trustRoot=OpenSSLCertificateAuthorities(ca_certs),
                        clientCertificate=client_cert,
                    )
                else:
                    options = optionsForClientTLS(config['tls']['hostname'])

                # create a TLS client endpoint
                #
                if version == 4:
                    endpoint = SSL4ClientEndpoint(
                        reactor,
                        host,
                        port,
                        options,
                        timeout=timeout,
                    )
                elif version == 6:
                    raise Exception("TLS on IPv6 not implemented")
                else:
                    raise Exception(
                        "invalid TCP protocol version {}".format(version))

            else:
                raise Exception(
                    "TLS transport requested, but TLS packages not available:\n{}"
                    .format(_LACKS_TLS_MSG))

        else:
            # create a non-TLS client endpoint
            #
            if version == 4:
                endpoint = TCP4ClientEndpoint(reactor,
                                              host,
                                              port,
                                              timeout=timeout)
            elif version == 6:
                endpoint = TCP6ClientEndpoint(reactor,
                                              host,
                                              port,
                                              timeout=timeout)
            else:
                raise Exception(
                    "invalid TCP protocol version {}".format(version))

    # a Unix Domain Socket endpoint
    #
    elif config['type'] == 'unix':

        # the path
        #
        path = abspath(join(cbdir, config['path']))

        # connection timeout in seconds
        #
        timeout = int(config.get('timeout', 10))

        # create the endpoint
        #
        endpoint = UNIXClientEndpoint(reactor, path, timeout=timeout)

    else:
        raise Exception("invalid endpoint type '{}'".format(config['type']))

    return endpoint
Example #23
0
def certoptions_factory(cert_file=None, cert_key=None, cert_ca=None):
    """
    Factory pour un gestionnaire de paramètre de contextes SSL.

    @param cert_file: Emplacement du fichier du certificat client à présenter
        au serveur lors de la connexion, si souhaité.
    @type cert_file: C{str}
    @param cert_key: Emplacement de la clé privée correspondant au certificat
        spécifié par le paramètre C{cert_file} si celui-ci ne contient pas
        déjà la clé privée.
    @type cert_key: C{str}
    @param cert_ca: Liste des emplacements des certificats des autorités
        de confiance.
    @type cert_ca: C{list}
    @return: Instance contenant les réglages qui seront appliqués
        lors de la création du contexte SSL.
    @rtype: L{CertificateOptions}
    """
    # Construction des objets associés à la gestion des certificats.
    # Tout d'abord, est-ce qu'on a un certificat client ?
    if cert_file:
        if cert_key:
            # Le certificat et la clé privée sont
            # dans 2 fichiers distincts.
            cert_handle = file(cert_file, 'r')
            pkey_handle = file(cert_key, 'r')
            try:
                cert = PrivateCertificate.fromCertificateAndKeyPair(
                    Certificate.loadPEM(cert_handle.read()),
                    KeyPair.load(
                        pkey_handle.read(),
                        crypto.FILETYPE_PEM
                    )
                )
            finally:
                cert_handle.close()
                pkey_handle.close()
        else:
            # Le fichier contient à la fois
            # le certificat et sa clé privée.
            cert_handle = file(cert_file, 'r')
            try:
                cert = PrivateCertificate.loadPEM(
                            cert_handle.read())
            finally:
                cert_handle.close()

    # Construction de la liste des CA de confiance.
    cert_ca_objects = []
    if cert_ca:
        # Une ou plusieurs CA ont été données en paramètre.
        for ca in cert_ca:
            ca_handle = file(ca, 'r')
            try:
                cert_ca_objects.append(Certificate.loadPEM(
                                        ca_handle.read()))
            finally:
                ca_handle.close()

    # Application des CA au certificat client.
    if cert_file:
        return cert.options(*cert_ca_objects)

    # Ou création d'un contexte qui ne valide le certificat du serveur
    # que si des CA ont été spécifiées.
    return CertificateOptions(
        method=SSL.SSLv23_METHOD, # SSLv2, SSLv3 ou TLSv1.
        verify=bool(cert_ca_objects), # Uniquement s'il y a des CAs.
        caCerts=[auth.original for auth in cert_ca_objects],
        requireCertificate=True,
    )
Example #24
0
def create_listening_endpoint_from_config(config, cbdir, reactor):
    """
    Create a Twisted stream server endpoint from a Crossbar.io transport configuration.

    See: https://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IStreamServerEndpoint.html

    :param config: The transport configuration.
    :type config: dict
    :param cbdir: Crossbar.io node directory (we need this for TLS key/certificates).
    :type cbdir: str
    :param reactor: The reactor to use for endpoint creation.
    :type reactor: obj

    :returns obj -- An instance implementing IStreamServerEndpoint
    """
    log = make_logger()
    endpoint = None

    # a TCP endpoint
    #
    if config['type'] == 'tcp':

        # the TCP protocol version (v4 or v6)
        #
        version = int(config.get('version', 4))

        # the listening port
        #
        if type(config['port']) is six.text_type:
            # read port from environment variable ..
            try:
                port = int(environ[config['port'][1:]])
            except Exception as e:
                print(
                    "Could not read listening port from env var: {}".format(e))
                raise e
        else:
            port = config['port']

        # the listening interface
        #
        interface = str(config.get('interface', '').strip())

        # the TCP accept queue depth
        #
        backlog = int(config.get('backlog', 50))

        if 'tls' in config:
            if _HAS_TLS:
                key_filepath = abspath(join(cbdir, config['tls']['key']))
                cert_filepath = abspath(
                    join(cbdir, config['tls']['certificate']))

                with open(key_filepath) as key_file:
                    with open(cert_filepath) as cert_file:

                        if 'dhparam' in config['tls']:
                            dhpath = FilePath(
                                abspath(join(cbdir, config['tls']['dhparam'])))
                            dh_params = DiffieHellmanParameters.fromFile(
                                dhpath)
                        else:
                            # XXX won't be doing ANY EDH
                            # curves... maybe make dhparam required?
                            # or do "whatever tlxctx was doing"
                            dh_params = None
                            log.warn(
                                "OpenSSL DH modes not active (no 'dhparam')")

                        # create a TLS context factory
                        #
                        key = key_file.read()
                        cert = cert_file.read()
                        ca_certs = None
                        if 'ca_certificates' in config['tls']:
                            ca_certs = []
                            for fname in config['tls']['ca_certificates']:
                                with open(fname, 'r') as f:
                                    ca_certs.append(
                                        Certificate.loadPEM(f.read()).original)

                        crossbar_ciphers = AcceptableCiphers.fromOpenSSLCipherString(
                            'ECDHE-RSA-AES128-GCM-SHA256:'
                            'DHE-RSA-AES128-GCM-SHA256:'
                            'ECDHE-RSA-AES128-SHA256:'
                            'DHE-RSA-AES128-SHA256:'
                            'ECDHE-RSA-AES128-SHA:'
                            'DHE-RSA-AES128-SHA')

                        ctx = CertificateOptions(
                            privateKey=KeyPair.load(
                                key, crypto.FILETYPE_PEM).original,
                            certificate=Certificate.loadPEM(cert).original,
                            verify=(ca_certs is not None),
                            caCerts=ca_certs,
                            dhParameters=dh_params,
                            acceptableCiphers=crossbar_ciphers,
                        )
                        if ctx._ecCurve is None:
                            log.warn(
                                "OpenSSL failed to set ECDH default curve")
                        else:
                            log.info(
                                "Ok, OpenSSL is using ECDH elliptic curve {curve}",
                                curve=ctx._ecCurve.snName,
                            )

                # create a TLS server endpoint
                #
                if version == 4:
                    endpoint = SSL4ServerEndpoint(reactor,
                                                  port,
                                                  ctx,
                                                  backlog=backlog,
                                                  interface=interface)
                elif version == 6:
                    raise Exception("TLS on IPv6 not implemented")
                else:
                    raise Exception(
                        "invalid TCP protocol version {}".format(version))
            else:
                raise Exception(
                    "TLS transport requested, but TLS packages not available:\n{}"
                    .format(_LACKS_TLS_MSG))

        else:
            # create a non-TLS server endpoint
            #
            if version == 4:
                endpoint = TCP4ServerEndpoint(reactor,
                                              port,
                                              backlog=backlog,
                                              interface=interface)
            elif version == 6:
                endpoint = TCP6ServerEndpoint(reactor,
                                              port,
                                              backlog=backlog,
                                              interface=interface)
            else:
                raise Exception(
                    "invalid TCP protocol version {}".format(version))

    # a Unix Domain Socket endpoint
    #
    elif config['type'] == 'unix':

        # the accept queue depth
        #
        backlog = int(config.get('backlog', 50))

        # the path
        #
        path = FilePath(join(cbdir, config['path']))

        # if there is already something there, delete it.
        #
        if path.exists():
            log.info(("{path} exists, attempting to remove before using as a "
                      "UNIX socket"),
                     path=path)
            path.remove()

        # create the endpoint
        #
        endpoint = UNIXServerEndpoint(reactor, path.path, backlog=backlog)

    else:
        raise Exception("invalid endpoint type '{}'".format(config['type']))

    return endpoint