예제 #1
0
def build_key_and_cert(subject_name, *, ca=False, ca_key=None, issuer_name=''):
    if not issuer_name:
        issuer_name = subject_name

    # DDS-Security section 9.3.1 calls for prime256v1, for which SECP256R1 is an alias
    private_key = ec.generate_private_key(ec.SECP256R1, cryptography_backend())
    if not ca_key:
        ca_key = private_key

    if ca:
        extension = x509.BasicConstraints(ca=True, path_length=1)
    else:
        extension = x509.BasicConstraints(ca=False, path_length=None)

    utcnow = datetime.datetime.utcnow()
    builder = x509.CertificateBuilder().issuer_name(issuer_name).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        # Using a day earlier here to prevent Connext (5.3.1) from complaining
        # when extracting it from the permissions file and thinking it's in the future
        # https://github.com/ros2/ci/pull/436#issuecomment-624874296
        utcnow - datetime.timedelta(days=1)).not_valid_after(
            # TODO: This should not be hard-coded
            utcnow + datetime.timedelta(days=3650)).public_key(
                private_key.public_key()).subject_name(
                    subject_name).add_extension(extension, critical=ca)
    cert = builder.sign(ca_key, hashes.SHA256(), cryptography_backend())

    return (cert, private_key)
예제 #2
0
def _build_key_and_cert(subject_name,
                        *,
                        ca=False,
                        ca_key=None,
                        issuer_name=''):
    if not issuer_name:
        issuer_name = subject_name

    # DDS-Security section 9.3.1 calls for prime256v1, for which SECP256R1 is an alias
    private_key = ec.generate_private_key(ec.SECP256R1, cryptography_backend())
    if not ca_key:
        ca_key = private_key

    if ca:
        extension = x509.BasicConstraints(ca=True, path_length=1)
    else:
        extension = x509.BasicConstraints(ca=False, path_length=None)

    utcnow = datetime.datetime.utcnow()
    builder = x509.CertificateBuilder().issuer_name(issuer_name).serial_number(
        x509.random_serial_number()).not_valid_before(utcnow).not_valid_after(
            # TODO: This should not be hard-coded
            utcnow + datetime.timedelta(days=3650)).public_key(
                private_key.public_key()).subject_name(
                    subject_name).add_extension(extension, critical=ca)
    cert = builder.sign(ca_key, hashes.SHA256(), cryptography_backend())

    return (cert, private_key)
예제 #3
0
def test_ca_key(keystore_dir):
    with open(os.path.join(keystore_dir, 'ca.key.pem'), 'rb') as f:
        key = load_pem_private_key(f.read(),
                                   password=None,
                                   backend=cryptography_backend())
        public = key.public_key()
        assert public.curve.name == 'secp256r1'
예제 #4
0
 def check_ca_key_pem(path):
     with open(path, 'rb') as f:
         key = load_pem_private_key(f.read(),
                                    password=None,
                                    backend=cryptography_backend())
         public = key.public_key()
         assert public.curve.name == 'secp256r1'
예제 #5
0
def _create_key_and_cert(keystore_ca_cert_path, keystore_ca_key_path, identity,
                         cert_path, key_path):
    # Load the CA cert and key from disk
    with open(keystore_ca_cert_path, 'rb') as f:
        ca_cert = x509.load_pem_x509_certificate(f.read(),
                                                 cryptography_backend())

    with open(keystore_ca_key_path, 'rb') as f:
        ca_key = serialization.load_pem_private_key(f.read(), None,
                                                    cryptography_backend())

    cert, private_key = _build_key_and_cert(x509.Name(
        [x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, identity)]),
                                            issuer_name=ca_cert.subject,
                                            ca_key=ca_key)

    _write_key(private_key, key_path)
    _write_cert(cert, cert_path)
예제 #6
0
def _create_smime_signed_file(cert_path, key_path, unsigned_file_path,
                              signed_file_path):
    # Load the CA cert and key from disk
    with open(cert_path, 'rb') as cert_file:
        cert = x509.load_pem_x509_certificate(cert_file.read(),
                                              cryptography_backend())

    with open(key_path, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(), None, cryptography_backend())

    # Get the contents of the unsigned file, which we're about to sign
    with open(unsigned_file_path, 'rb') as f:
        content = f.read()

    # Sign the contents, and write the result to the appropriate place
    with open(signed_file_path, 'wb') as f:
        f.write(_sign_bytes(cert, private_key, content))
예제 #7
0
def test_ca_cert(keystore_dir):
    with open(os.path.join(keystore_dir, 'ca.cert.pem'), 'rb') as f:
        cert = x509.load_pem_x509_certificate(f.read(), cryptography_backend())
        names = cert.subject.get_attributes_for_oid(
            x509.oid.NameOID.COMMON_NAME)
        assert len(names) == 1
        assert names[0].value == _DEFAULT_COMMON_NAME
        names = cert.subject.get_attributes_for_oid(
            x509.oid.NameOID.ORGANIZATION_NAME)
        assert len(names) == 0
예제 #8
0
 def check_ca_cert_pem(path):
     with open(path, 'rb') as f:
         cert = x509.load_pem_x509_certificate(f.read(),
                                               cryptography_backend())
         names = cert.subject.get_attributes_for_oid(
             x509.oid.NameOID.COMMON_NAME)
         assert len(names) == 1
         assert names[0].value == u'sros2testCA'
         names = cert.subject.get_attributes_for_oid(
             x509.oid.NameOID.ORGANIZATION_NAME)
         assert len(names) == 0
예제 #9
0
def load_certificate_request(path, backend='pyopenssl'):
    """Load the specified certificate signing request."""
    try:
        with open(path, 'rb') as csr_fh:
            csr_content = csr_fh.read()
    except (IOError, OSError) as exc:
        raise OpenSSLObjectError(exc)
    if backend == 'pyopenssl':
        return crypto.load_certificate_request(crypto.FILETYPE_PEM,
                                               csr_content)
    elif backend == 'cryptography':
        return x509.load_pem_x509_csr(csr_content, cryptography_backend())
예제 #10
0
def load_certificate(path, content=None, backend='pyopenssl'):
    """Load the specified certificate."""

    try:
        if content is None:
            with open(path, 'rb') as cert_fh:
                cert_content = cert_fh.read()
        else:
            cert_content = content
        if backend == 'pyopenssl':
            return crypto.load_certificate(crypto.FILETYPE_PEM, cert_content)
        elif backend == 'cryptography':
            return x509.load_pem_x509_certificate(cert_content,
                                                  cryptography_backend())
    except (IOError, OSError) as exc:
        raise OpenSSLObjectError(exc)
예제 #11
0
def main():
    module = AnsibleModule(argument_spec=dict(
        ca_cert=dict(type='path'),
        host=dict(type='str', required=True),
        port=dict(type='int', required=True),
        proxy_host=dict(type='str'),
        proxy_port=dict(type='int', default=8080),
        timeout=dict(type='int', default=10),
        select_crypto_backend=dict(
            type='str',
            choices=['auto', 'pyopenssl', 'cryptography'],
            default='auto'),
    ), )

    ca_cert = module.params.get('ca_cert')
    host = module.params.get('host')
    port = module.params.get('port')
    proxy_host = module.params.get('proxy_host')
    proxy_port = module.params.get('proxy_port')
    timeout = module.params.get('timeout')

    backend = module.params.get('select_crypto_backend')
    if backend == 'auto':
        # Detection what is possible
        can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(
            MINIMAL_CRYPTOGRAPHY_VERSION)
        can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(
            MINIMAL_PYOPENSSL_VERSION)

        # First try cryptography, then pyOpenSSL
        if can_use_cryptography:
            backend = 'cryptography'
        elif can_use_pyopenssl:
            backend = 'pyopenssl'

        # Success?
        if backend == 'auto':
            module.fail_json(msg=(
                "Can't detect any of the required Python libraries "
                "cryptography (>= {0}) or PyOpenSSL (>= {1})"
            ).format(MINIMAL_CRYPTOGRAPHY_VERSION, MINIMAL_PYOPENSSL_VERSION))

    if backend == 'pyopenssl':
        if not PYOPENSSL_FOUND:
            module.fail_json(msg=missing_required_lib(
                'pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
                             exception=PYOPENSSL_IMP_ERR)
        module.deprecate(
            'The module is using the PyOpenSSL backend. This backend has been deprecated',
            version='2.13')
    elif backend == 'cryptography':
        if not CRYPTOGRAPHY_FOUND:
            module.fail_json(msg=missing_required_lib(
                'cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
                             exception=CRYPTOGRAPHY_IMP_ERR)

    result = dict(changed=False, )

    if not PYOPENSSL_FOUND:
        module.fail_json(msg=missing_required_lib('pyOpenSSL >= 0.15'),
                         exception=PYOPENSSL_IMP_ERR)

    if timeout:
        setdefaulttimeout(timeout)

    if ca_cert:
        if not isfile(ca_cert):
            module.fail_json(msg="ca_cert file does not exist")

    if proxy_host:
        if not HAS_CREATE_DEFAULT_CONTEXT:
            module.fail_json(
                msg=
                'To use proxy_host, you must run the get_certificate module with Python 2.7 or newer.',
                exception=CREATE_DEFAULT_CONTEXT_IMP_ERR)

        try:
            connect = "CONNECT %s:%s HTTP/1.0\r\n\r\n" % (host, port)
            sock = socket()
            atexit.register(sock.close)
            sock.connect((proxy_host, proxy_port))
            sock.send(connect.encode())
            sock.recv(8192)

            ctx = create_default_context()
            ctx.check_hostname = False
            ctx.verify_mode = CERT_NONE

            if ca_cert:
                ctx.verify_mode = CERT_OPTIONAL
                ctx.load_verify_locations(cafile=ca_cert)

            cert = ctx.wrap_socket(sock,
                                   server_hostname=host).getpeercert(True)
            cert = DER_cert_to_PEM_cert(cert)
        except Exception as e:
            module.fail_json(
                msg="Failed to get cert from port with error: {0}".format(e))

    else:
        try:
            cert = get_server_certificate((host, port), ca_certs=ca_cert)
        except Exception as e:
            module.fail_json(
                msg="Failed to get cert from port with error: {0}".format(e))

    result['cert'] = cert

    if backend == 'pyopenssl':
        x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
        result['subject'] = {}
        for component in x509.get_subject().get_components():
            result['subject'][component[0]] = component[1]

        result['expired'] = x509.has_expired()

        result['extensions'] = []
        extension_count = x509.get_extension_count()
        for index in range(0, extension_count):
            extension = x509.get_extension(index)
            result['extensions'].append({
                'critical': extension.get_critical(),
                'asn1_data': extension.get_data(),
                'name': extension.get_short_name(),
            })

        result['issuer'] = {}
        for component in x509.get_issuer().get_components():
            result['issuer'][component[0]] = component[1]

        result['not_after'] = x509.get_notAfter()
        result['not_before'] = x509.get_notBefore()

        result['serial_number'] = x509.get_serial_number()
        result['signature_algorithm'] = x509.get_signature_algorithm()

        result['version'] = x509.get_version()

    elif backend == 'cryptography':
        x509 = cryptography.x509.load_pem_x509_certificate(
            to_bytes(cert), cryptography_backend())
        result['subject'] = {}
        for attribute in x509.subject:
            result['subject'][crypto_utils.cryptography_oid_to_name(
                attribute.oid, short=True)] = attribute.value

        result['expired'] = x509.not_valid_after < datetime.datetime.utcnow()

        result['extensions'] = []
        for dotted_number, entry in crypto_utils.cryptography_get_extensions_from_cert(
                x509).items():
            oid = cryptography.x509.oid.ObjectIdentifier(dotted_number)
            result['extensions'].append({
                'critical':
                entry['critical'],
                'asn1_data':
                base64.b64decode(entry['value']),
                'name':
                crypto_utils.cryptography_oid_to_name(oid, short=True),
            })

        result['issuer'] = {}
        for attribute in x509.issuer:
            result['issuer'][crypto_utils.cryptography_oid_to_name(
                attribute.oid, short=True)] = attribute.value

        result['not_after'] = x509.not_valid_after.strftime('%Y%m%d%H%M%SZ')
        result['not_before'] = x509.not_valid_before.strftime('%Y%m%d%H%M%SZ')

        result['serial_number'] = x509.serial_number
        result['signature_algorithm'] = crypto_utils.cryptography_oid_to_name(
            x509.signature_algorithm_oid)

        # We need the -1 offset to get the same values as pyOpenSSL
        if x509.version == cryptography.x509.Version.v1:
            result['version'] = 1 - 1
        elif x509.version == cryptography.x509.Version.v3:
            result['version'] = 3 - 1
        else:
            result['version'] = "unknown"

    module.exit_json(**result)
예제 #12
0
def load_cert(cert_path: pathlib.Path):
    with open(cert_path, 'rb') as cert_file:
        return x509.load_pem_x509_certificate(cert_file.read(),
                                              cryptography_backend())
예제 #13
0
def load_privatekey(path,
                    passphrase=None,
                    check_passphrase=True,
                    backend='pyopenssl'):
    """Load the specified OpenSSL private key."""

    try:
        with open(path, 'rb') as b_priv_key_fh:
            priv_key_detail = b_priv_key_fh.read()

        if backend == 'pyopenssl':

            # First try: try to load with real passphrase (resp. empty string)
            # Will work if this is the correct passphrase, or the key is not
            # password-protected.
            try:
                result = crypto.load_privatekey(crypto.FILETYPE_PEM,
                                                priv_key_detail,
                                                to_bytes(passphrase or ''))
            except crypto.Error as e:
                if len(e.args) > 0 and len(e.args[0]) > 0:
                    if e.args[0][0][2] in ('bad decrypt', 'bad password read'):
                        # This happens in case we have the wrong passphrase.
                        if passphrase is not None:
                            raise OpenSSLBadPassphraseError(
                                'Wrong passphrase provided for private key!')
                        else:
                            raise OpenSSLBadPassphraseError(
                                'No passphrase provided, but private key is password-protected!'
                            )
                raise OpenSSLObjectError(
                    'Error while deserializing key: {0}'.format(e))
            if check_passphrase:
                # Next we want to make sure that the key is actually protected by
                # a passphrase (in case we did try the empty string before, make
                # sure that the key is not protected by the empty string)
                try:
                    crypto.load_privatekey(
                        crypto.FILETYPE_PEM, priv_key_detail,
                        to_bytes('y' if passphrase == 'x' else 'x'))
                    if passphrase is not None:
                        # Since we can load the key without an exception, the
                        # key isn't password-protected
                        raise OpenSSLBadPassphraseError(
                            'Passphrase provided, but private key is not password-protected!'
                        )
                except crypto.Error as e:
                    if passphrase is None and len(e.args) > 0 and len(
                            e.args[0]) > 0:
                        if e.args[0][0][2] in ('bad decrypt',
                                               'bad password read'):
                            # The key is obviously protected by the empty string.
                            # Don't do this at home (if it's possible at all)...
                            raise OpenSSLBadPassphraseError(
                                'No passphrase provided, but private key is password-protected!'
                            )
        elif backend == 'cryptography':
            try:
                result = load_pem_private_key(priv_key_detail, passphrase,
                                              cryptography_backend())
            except TypeError as dummy:
                raise OpenSSLBadPassphraseError(
                    'Wrong or empty passphrase provided for private key')
            except ValueError as dummy:
                raise OpenSSLBadPassphraseError(
                    'Wrong passphrase provided for private key')

        return result
    except (IOError, OSError) as exc:
        raise OpenSSLObjectError(exc)