Beispiel #1
0
    def _validate_cert_chain(self, cert_chain):
        # type: (bytes) -> None
        """Validate the certificate chain.

        This method checks if the passed in certificate chain is valid.
        A :py:class:`VerificationException` is raised if the certificate
        chain is not valid.

        The end certificate is read, using the
        :py:func:`cryptography.x509.load_pem_x509_certificate` method.
        The x509 backend is set as default to the
        :py:class:`cryptography.hazmat.backends.default_backend`
        instance.

        :param cert_chain: Certificate chain to be validated
        :type cert_chain: bytes
        :return: None
        :raises: :py:class:`VerificationException` if certificate chain is
            not valid
        """
        try:
            end_cert = None
            intermediate_certs = []
            for type_name, headers, der_bytes in pem.unarmor(
                    cert_chain, multiple=True):
                if end_cert is None:
                    end_cert = der_bytes
                else:
                    intermediate_certs.append(der_bytes)

            validator = CertificateValidator(end_cert, intermediate_certs)
            validator.validate_usage(key_usage={'digital_signature'})
        except (PathError, ValidationError) as e:
            raise VerificationException("Certificate chain is not valid", e)
Beispiel #2
0
    def _check_certificate(self, certificate):

        _, _, certificate_bytes = pem.unarmor(
            certificate.encode(), multiple=False)
        certificate = x509.Certificate.load(certificate_bytes)

        trust_roots = []
        with open(self.ca_crt, 'rb') as f:
            for _, _, der_bytes in pem.unarmor(f.read(), multiple=True):
                trust_roots.append(der_bytes)

        crls = []
        with open(settings.CA_CRL, 'rb') as f:
            crls.append(f.read())

        context = ValidationContext(crls=crls,
                                    trust_roots=trust_roots)

        try:
            validator = CertificateValidator(
                certificate, validation_context=context)
            result = validator.validate_usage(
                set(['digital_signature'])
            )
            dev = True
        except errors.PathValidationError as e:
            logger.debug("SimpleCA: validate PathValidationError %r" % (e))
            dev = False
        except errors.PathBuildingError as e:
            logger.debug("SimpleCA: validate PathBuildingError %r" % (e))
            dev = False
        logger.info("SimpleCA: validate cert %r == %r" %
                    (certificate.serial_number, dev))
        return dev
Beispiel #3
0
def test_cert_constraint_issuer(requests_mock):
    vc = live_testing_vc(requests_mock)
    signer_validation_path = CertificateValidator(
        FROM_CA.signing_cert, FROM_CA.cert_registry,
        validation_context=vc).validate_usage(set())
    tsa_validation_path = CertificateValidator(
        DUMMY_TS.tsa_cert, FROM_CA.cert_registry,
        validation_context=vc).validate_usage(set())

    scc = fields.SigCertConstraints(flags=fields.SigCertConstraintFlags.ISSUER,
                                    issuers=[ROOT_CERT])
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(FROM_CA.signing_cert, None)

    scc = fields.SigCertConstraints(flags=fields.SigCertConstraintFlags.ISSUER,
                                    issuers=[INTERM_CERT])
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    scc = fields.SigCertConstraints(
        flags=fields.SigCertConstraintFlags.ISSUER,
        issuers=[INTERM_CERT, SELF_SIGN.signing_cert])
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    scc = fields.SigCertConstraints(issuers=[INTERM_CERT])
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)
Beispiel #4
0
 def validate_vmc(self):
     try:
         end_entity_cert = None
         intermediates = []
         with open(self.vmc_file, 'rb') as f:
             for type_name, headers, der_bytes in pem.unarmor(
                     f.read(), multiple=True):
                 if end_entity_cert is None:
                     end_entity_cert = der_bytes
                 else:
                     intermediates.append(der_bytes)
         validator = CertificateValidator(end_entity_cert, intermediates)
         validated = validator.validate_usage(set(['digital_signature']))
         # print(intermediates)
     except errors.PathValidationError as PathValidationError:
         self.vmc_response["errors"].append("Error: " +
                                            str(PathValidationError))
         print(PathValidationError)
     except errors.RevokedError as RevokedError:
         self.vmc_response["errors"].append(
             "Error: Certificate Revoked.\n" + str(RevokedError))
         print(RevokedError)
     except errors.InvalidCertificateError as InvalidCertificateError:
         self.vmc_response["errors"].append(
             "Error: Certificate Is Invalid.\n" +
             str(InvalidCertificateError))
         print(InvalidCertificateError)
     except errors.PathBuildingError as PathBuildingError:
         self.vmc_response["errors"].append("Error: Cannot Build Path.\n" +
                                            str(PathBuildingError))
         print(PathBuildingError)
     except Exception as e:
         self.vmc_response["errors"].append(
             "Error: Validation Exception.\n" + str(e))
         print(e)
Beispiel #5
0
def test_cert_constraint_composite(requests_mock):
    vc = live_testing_vc(requests_mock)
    signer_validation_path = CertificateValidator(
        FROM_CA.signing_cert, FROM_CA.cert_registry,
        validation_context=vc).validate_usage(set())
    tsa_validation_path = CertificateValidator(
        DUMMY_TS.tsa_cert, FROM_CA.cert_registry,
        validation_context=vc).validate_usage(set())

    from asn1crypto import x509
    scc = fields.SigCertConstraints(flags=fields.SigCertConstraintFlags.ISSUER
                                    | fields.SigCertConstraintFlags.SUBJECT_DN,
                                    issuers=[INTERM_CERT],
                                    subject_dn=x509.Name.build({
                                        'common_name':
                                        'Lord Testerino',
                                        'country_name':
                                        'BE'
                                    }))
    scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(DUMMY_TS.tsa_cert, tsa_validation_path)

    from asn1crypto import x509
    scc = fields.SigCertConstraints(flags=fields.SigCertConstraintFlags.ISSUER
                                    | fields.SigCertConstraintFlags.SUBJECT_DN,
                                    issuers=[INTERM_CERT],
                                    subject_dn=x509.Name.build({
                                        'common_name':
                                        'Alice & Bob',
                                        'country_name':
                                        'BE'
                                    }))
    with pytest.raises(UnacceptableSignerError):
        scc.satisfied_by(FROM_CA.signing_cert, signer_validation_path)
    def test_basic_certificate_validator_tls_expired(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        validator = CertificateValidator(cert, other_certs)

        with self.assertRaisesRegexp(PathValidationError, 'expired'):
            validator.validate_tls('codexns.io')
Beispiel #7
0
    def test_basic_certificate_validator_tls_expired(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        validator = CertificateValidator(cert, other_certs)

        with self.assertRaisesRegexp(PathValidationError, 'expired'):
            validator.validate_tls('codexns.io')
Beispiel #8
0
    def verify(self, certificate):
        """Verifies the certificate, and its chain.

        :param Certificate certificate: The certificate to verify
        :return: A valid certificate chain for this certificate.
        :rtype: Iterable[Certificate]
        :raises AuthenticodeVerificationError: When the certificate could not be verified.
        """

        # we keep track of our asn1 objects to make sure we return Certificate objects when we're done
        to_check_asn1cert = certificate.to_asn1crypto
        all_certs = {to_check_asn1cert: certificate}

        # we need to get lists of our intermediates and trusted certificates
        intermediates, trust_roots = [], []
        for store in self.stores:
            for cert in store:
                asn1cert = cert.to_asn1crypto
                # we short-circuit the check here to ensure we do not check too much possibilities
                (trust_roots
                 if store.trusted else intermediates).append(asn1cert)
                all_certs[asn1cert] = cert

        # construct the context and validator for certvalidator
        timestamp = self.timestamp
        context = ValidationContext(
            trust_roots=list(trust_roots),
            moment=timestamp,
            weak_hash_algos=set() if self.allow_legacy else None,
            revocation_mode=self.revocation_mode,
            allow_fetching=self.allow_fetching,
            crl_fetch_params={'timeout': self.fetch_timeout},
            ocsp_fetch_params={'timeout': self.fetch_timeout},
            crls=self.crls,
            ocsps=self.ocsps)
        validator = CertificateValidator(
            end_entity_cert=to_check_asn1cert,
            intermediate_certs=list(intermediates),
            validation_context=context)

        # verify the chain
        try:
            chain = validator.validate_usage(
                key_usage=set(self.key_usages) if self.key_usages else set(),
                extended_key_usage=set(self.extended_key_usages)
                if self.extended_key_usages else set(),
                extended_optional=self.optional_eku)
        except Exception as e:
            raise VerificationError("Chain verification from %s failed: %s" %
                                    (certificate, e))

        signify_chain = [all_certs[x] for x in chain]
        self.verify_trust(signify_chain[0])
        return signify_chain
Beispiel #9
0
    def test_basic_certificate_validator_tls_invalid_hostname(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegexp(PathValidationError, 'not valid'):
            validator.validate_tls('google.com')
Beispiel #10
0
    def test_basic_certificate_validator_tls(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        path = validator.validate_tls('codexns.io')
        self.assertEqual(3, len(path))
Beispiel #11
0
 def test_crl_without_update_field(self):
     cert = self._load_cert_object('microsoft_armored.crt')
     root_certificates = self._load_trust_roots(
         os.path.join(fixtures_dir, 'root_certs'))
     moment = datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
     context = ValidationContext(trust_roots=root_certificates,
                                 moment=moment,
                                 allow_fetching=True)
     validator = CertificateValidator(cert, validation_context=context)
     validator.validate_usage(set(['digital_signature']),
                              set(['code_signing']), False)
Beispiel #12
0
    def test_basic_certificate_validator_tls_invalid_key_usage(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [self._load_cert_object('digicert-sha2-secure-server-ca.crt')]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegex(PathValidationError, 'for the purpose'):
            validator.validate_usage(set(['crl_sign']))
Beispiel #13
0
    def test_basic_certificate_validator_tls_invalid_hostname(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [self._load_cert_object('digicert-sha2-secure-server-ca.crt')]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegex(PathValidationError, 'not valid'):
            validator.validate_tls('google.com')
Beispiel #14
0
    def test_basic_certificate_validator_tls(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [self._load_cert_object('digicert-sha2-secure-server-ca.crt')]

        moment = datetime(2019, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        path = validator.validate_tls('www.mozilla.org')
        self.assertEqual(3, len(path))
    def test_basic_certificate_validator_tls(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        path = validator.validate_tls('codexns.io')
        self.assertEqual(3, len(path))
Beispiel #16
0
    def test_basic_certificate_validator_tls_invalid_key_usage(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegexp(PathValidationError, 'for the purpose'):
            validator.validate_usage(set(['crl_sign']))
    def test_basic_certificate_validator_tls_invalid_key_usage(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegexp(PathValidationError, 'for the purpose'):
            validator.validate_usage(set(['crl_sign']))
    def test_basic_certificate_validator_tls_invalid_hostname(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        moment = datetime(2015, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(moment=moment)
        validator = CertificateValidator(cert, other_certs, context)

        with self.assertRaisesRegexp(PathValidationError, 'not valid'):
            validator.validate_tls('google.com')
Beispiel #19
0
    def validate_certificate(self, ca_certs: List[bytes]) -> bool:

        if not ca_certs:
            raise ValueError("ca_certs should be set.")
        if not self._cert:
            return False

        ctx = ValidationContext(trust_roots=ca_certs)
        try:
            validator = CertificateValidator(self._cert, self._intermediates, validation_context=ctx)
            validator.validate_usage(set(["digital_signature"]), extended_optional=True)
        except Exception as err:
            raise VerifyError("Failed to validate the certificate bound to the key.") from err
        return True
Beispiel #20
0
 def test_crl_without_update_field_hard_fail(self):
     cert = self._load_cert_object('microsoft_armored.crt')
     root_certificates = self._load_trust_roots(
         os.path.join(fixtures_dir, 'root_certs'))
     moment = datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
     context = ValidationContext(trust_roots=root_certificates,
                                 moment=moment,
                                 allow_fetching=True,
                                 revocation_mode='hard-fail')
     validator = CertificateValidator(cert, validation_context=context)
     with self.assertRaisesRegexp(
             PathValidationError,
             'nextUpdate field is expected to be present in CRL'):
         validator.validate_usage(set(['digital_signature']),
                                  set(['code_signing']), False)
Beispiel #21
0
def _validate_cms_signature(sig_blob: SignatureBlob, cd_hash: bytes):
    assert sig_blob.cms
    signed_data = sig_blob.cms["content"]
    assert isinstance(signed_data, SignedData)
    assert len(signed_data["signer_infos"]) == 1

    # Get certificates
    cert_chain = []
    for cert in signed_data["certificates"]:
        c = cert.chosen
        assert isinstance(c, Certificate)
        cert_chain.append(c)

    # Get algorithms used
    signer_info = signed_data["signer_infos"][0]
    digest_alg = signer_info["digest_algorithm"]["algorithm"].native
    sig_alg = signer_info["signature_algorithm"]["algorithm"].native

    # Get message and signature
    signed_attrs = signer_info["signed_attrs"]
    sig = signer_info["signature"].contents

    # Check the hash of CodeDirectory matches what is in the signature
    message_digest = None
    for attr in sig_blob.cms["content"]["signer_infos"][0]["signed_attrs"]:
        if attr["type"].native == "message_digest":
            message_digest = attr["values"][0].native
            if message_digest != cd_hash:
                raise Exception(
                    f"CodeDirectory Hash mismatch. Expected {message_digest.hex()}, Calculated {cd_hash.hex()}"
                )
    if message_digest is None:
        raise Exception("message_digest not found in signature")

    # Validate the certificate chain
    validation_context = ValidationContext(
        trust_roots=APPLE_ROOTS,
        allow_fetching=False,
        additional_critical_extensions=APPLE_CERT_CRIT_EXTS,
    )
    validator = CertificateValidator(cert_chain[-1], cert_chain[0:-1],
                                     validation_context)
    validator.validate_usage({"digital_signature"}, {"code_signing"})

    # Check the signature
    pubkey = asymmetric.load_public_key(cert_chain[-1].public_key)
    signed_msg = _sort_attributes(signed_attrs).dump()
    asymmetric.rsa_pkcs1v15_verify(pubkey, sig, signed_msg, digest_alg)
Beispiel #22
0
    def validate_cert_usage(cls,
                            validator: CertificateValidator,
                            key_usage_settings: KeyUsageConstraints = None):
        key_usage_settings = key_usage_settings or KeyUsageConstraints()
        key_usage_settings = KeyUsageConstraints(
            key_usage=(cls.key_usage if key_usage_settings.key_usage is None
                       else key_usage_settings.key_usage),
            extd_key_usage=(cls.extd_key_usage
                            if key_usage_settings.extd_key_usage is None else
                            key_usage_settings.extd_key_usage))
        cert: x509.Certificate = validator._certificate

        revoked = trusted = False
        path = None

        try:
            # validate usage without going through certvalidator
            key_usage_settings.validate(cert)
            path = validator.validate_usage(key_usage=set())
            trusted = True
        except InvalidCertificateError as e:
            # TODO accumulate these somewhere
            logger.warning(e)
        except RevokedError:
            revoked = True
        except (PathValidationError, PathBuildingError) as e:
            logger.warning(e)
        if not trusted:
            subj = cert.subject.human_friendly
            logger.warning(f"Chain of trust validation for {subj} failed.")
        return trusted, revoked, path
Beispiel #23
0
    def validation_paths(self, validation_context):
        """
        Produce validation paths for the certificates gathered by this
        :class:`.TimeStamper`.

        This is internal API.

        :param validation_context:
            The validation context to apply.
        :return:
            A generator producing validation paths.
        """
        for cert in self._certs.values():
            validator = CertificateValidator(
                cert,
                intermediate_certs=self.cert_registry,
                validation_context=validation_context)
            yield validator.validate_usage(set(), {"time_stamping"})
Beispiel #24
0
    def verify(self, certificate):
        """Verifies the certificate, and its chain.

        :param Certificate certificate: The certificate to verify
        :return: A valid certificate chain for this certificate.
        :rtype: Iterable[Certificate]
        :raises AuthenticodeVerificationError: When the certificate could not be verified.
        """

        # we keep track of our asn1 objects to make sure we return Certificate objects when we're done
        to_check_asn1cert = certificate.to_asn1crypto
        all_certs = {to_check_asn1cert: certificate}

        # we need to get lists of our intermediates and trusted certificates
        intermediates, trust_roots = [], []
        for store in self.stores:
            for cert in store:
                asn1cert = cert.to_asn1crypto
                (trust_roots
                 if store.trusted else intermediates).append(asn1cert)
                all_certs[asn1cert] = cert

        # construct the context and validator for certvalidator
        context = ValidationContext(
            trust_roots=list(trust_roots),
            moment=self.timestamp,
            weak_hash_algos=set() if self.allow_legacy else None)
        validator = CertificateValidator(
            end_entity_cert=to_check_asn1cert,
            intermediate_certs=list(intermediates),
            validation_context=context)

        # verify the chain
        try:
            chain = validator.validate_usage(
                key_usage=set(self.key_usages) if self.key_usages else set(),
                extended_key_usage=set(self.extended_key_usages)
                if self.extended_key_usages else set(),
                extended_optional=self.optional_eku)
        except Exception as e:
            raise VerificationError("Chain verification from %s failed: %s" %
                                    (certificate, e))
        else:
            return [all_certs[x] for x in chain]
Beispiel #25
0
    def validate_vmc(self):
        try:
            end_entity_cert = None
            intermediates = []
            with open(self.vmc_file, 'rb') as f:
                readfile = f.read()
                # Create parsed data for expiry and embedded svg check
                self.parsed_vmc = self.parse_vmc_cert(readfile)
                for type_name, headers, der_bytes in pem.unarmor(
                        readfile, multiple=True):
                    if end_entity_cert is None:
                        end_entity_cert = der_bytes
                    else:
                        intermediates.append(der_bytes)

            validator = CertificateValidator(end_entity_cert, intermediates)
            validated = validator.validate_usage(
                set(['digital_signature'])
                # ,extended_key_usage=set(["server_auth", "client_auth"])
            )
            if validated:
                print("Certificate Validated")

        except errors.PathValidationError as PathValidationError:
            self.vmc_response["errors"].append("Warning: " +
                                               str(PathValidationError))
            print(PathValidationError)
        except errors.RevokedError as RevokedError:
            self.vmc_response["errors"].append(
                "Warning: Certificate Revoked.\n" + str(RevokedError))
            print(RevokedError)
        except errors.InvalidCertificateError as InvalidCertificateError:
            self.vmc_response["errors"].append(
                "Warning: Certificate Is Invalid.\n" +
                str(InvalidCertificateError))
            print(InvalidCertificateError)
        except errors.PathBuildingError as PathBuildingError:
            # self.vmc_response["errors"].append("Warning: Cannot Build Path.\n"+str(PathBuildingError))
            print(PathBuildingError)
        except Exception as e:
            self.vmc_response["errors"].append(
                "Warning: Validation Exception.\n" + str(e))
            print(e)
Beispiel #26
0
def verify_chain(certificate: Union[str, x509.Certificate], ca: str):
    if isinstance(certificate, x509.Certificate):
        pem = certificate.public_bytes(serialization.Encoding.PEM)
    else:
        if certificate.startswith("-----BEGIN CERTIFICATE-----"):
            pem = certificate.encode()
        else:
            with open(certificate, "rb") as my_file:
                pem = my_file.read()
        # cert = x509.load_pem_x509_certificate(pem, default_backend())

    with open(ca, "rb") as my_file:
        pem_ca = my_file.read()
    # ca = x509.load_pem_x509_certificate(pem_ca, default_backend())

    trust_roots = [pem_ca]
    context = ValidationContext(trust_roots=trust_roots)

    validator = CertificateValidator(pem, validation_context=context)
    return validator.validate_usage({"digital_signature"})
Beispiel #27
0
    def test_basic_certificate_validator_tls_whitelist(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        context = ValidationContext(whitelisted_certs=[cert.sha1_fingerprint])
        validator = CertificateValidator(cert, other_certs, context)

        # If whitelist does not work, this will raise exception for expiration
        validator.validate_tls('codexns.io')

        # If whitelist does not work, this will raise exception for hostname
        validator.validate_tls('google.com')

        # If whitelist does not work, this will raise exception for key usage
        validator.validate_usage(set(['crl_sign']))
    def test_basic_certificate_validator_tls_whitelist(self):
        cert = self._load_cert_object('codex.crt')
        other_certs = [self._load_cert_object('GeoTrust_EV_SSL_CA_-_G4.crt')]

        context = ValidationContext(whitelisted_certs=[cert.sha1_fingerprint])
        validator = CertificateValidator(cert, other_certs, context)

        # If whitelist does not work, this will raise exception for expiration
        validator.validate_tls('codexns.io')

        # If whitelist does not work, this will raise exception for hostname
        validator.validate_tls('google.com')

        # If whitelist does not work, this will raise exception for key usage
        validator.validate_usage(set(['crl_sign']))
Beispiel #29
0
    def test_basic_certificate_validator_tls_whitelist(self):
        cert = self._load_cert_object('mozilla.org.crt')
        other_certs = [self._load_cert_object('digicert-sha2-secure-server-ca.crt')]

        moment = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

        context = ValidationContext(
            whitelisted_certs=[cert.sha1_fingerprint],
            moment=moment
        )
        validator = CertificateValidator(cert, other_certs, context)

        # If whitelist does not work, this will raise exception for expiration
        validator.validate_tls('www.mozilla.org')

        # If whitelist does not work, this will raise exception for hostname
        validator.validate_tls('google.com')

        # If whitelist does not work, this will raise exception for key usage
        validator.validate_usage(set(['crl_sign']))
Beispiel #30
0
    def validate_cert_usage(cls, validator: CertificateValidator):

        revoked = trusted = False
        path = None
        try:
            path = validator.validate_usage(
                key_usage=cls.key_usage, extended_key_usage=cls.extd_key_usage)
            trusted = True
        except InvalidCertificateError as e:
            # TODO accumulate these somewhere
            logger.warning(e)
        except RevokedError:
            revoked = True
        except (PathValidationError, PathBuildingError) as e:
            logger.warning(e)
        if not trusted:
            subj = validator._certificate.subject.human_friendly
            logger.warning(f"Chain of trust validation for {subj} failed.")
        return trusted, revoked, path
Beispiel #31
0
    def _validate_chain_certvalidator(self, tlslite_connection):
        """Validate server certificate chain using 3rd party certvalidator library which uses oscrypt/libcrypto
        Note: oscrypt uses ctypes find_library() which does not work in certain distributions such as alpine.
        (e.g. see https://github.com/docker-library/python/issues/111)
        On such systems, users will have to rely on other server cert validation approaches such as using openssl
        or turning it off completely.
        """
        try:
            from certvalidator import CertificateValidator
            from certvalidator import ValidationContext
            from asn1crypto import x509, pem

            # validate server certificate chain
            session = tlslite_connection.sock.session
            assert type(session.serverCertChain.x509List) == list

            # get the end-entity cert
            file_bytes = session.serverCertChain.x509List[0].bytes
            end_entity_cert = x509.Certificate.load(str(file_bytes))

            def cert_files_exist(path, file_names):
                file_names = [os.path.join(path, f) for f in file_names]
                for f in file_names:
                    if not os.path.isfile(f):
                        return False
                return True

            def get_cert_bytes(cert_dir, file_names):
                file_names = [os.path.join(cert_dir, f) for f in file_names]
                result = []
                for fname in file_names:
                    arr = open(fname, "rb").read()
                    cert_bytes = pem.unarmor(arr)[2]
                    result.append(cert_bytes)
                return result

            intermediate_cert_names = [
                "comodo_ca_intermediate.pem",
                "sectigo_ca_intermediate.pem",
            ]

            extra_trust_names = [
                "scalyr_agent_ca_root.pem",
                "addtrust_external_ca_root.pem",
            ]

            # Determine the directory containing the certs.
            # First check the directory containing the _ca_file
            # but if we don't find the intermediate/extra certs there
            # then look in the relative `certs` directory.  The latter
            # will typically be required if running directly from source
            all_cert_names = intermediate_cert_names + extra_trust_names
            cert_dir = os.path.dirname(self._ca_file)
            if not cert_files_exist(cert_dir, all_cert_names):
                path = os.path.dirname(os.path.abspath(__file__))
                path = os.path.abspath(path + "../../certs")
                if cert_files_exist(path, all_cert_names):
                    cert_dir = path

            trust_roots = None
            intermediate_certs = get_cert_bytes(cert_dir, intermediate_cert_names)
            extra_trust_roots = get_cert_bytes(cert_dir, extra_trust_names)

            if trust_roots:
                context = ValidationContext(
                    trust_roots=trust_roots,
                    extra_trust_roots=extra_trust_roots,
                    other_certs=intermediate_certs,
                    # whitelisted_certs=[end_entity_cert.sha1_fingerprint],
                )
            else:
                context = ValidationContext(
                    extra_trust_roots=extra_trust_roots,
                    other_certs=intermediate_certs,
                    # whitelisted_certs=[end_entity_cert.sha1_fingerprint],
                )
            validator = CertificateValidator(
                end_entity_cert, validation_context=context
            )
            validator.validate_tls(six.text_type(self._host))
            log.info(
                "Scalyr server cert chain successfully validated via certvalidator library"
            )
        except Exception as ce:
            log.exception("Error validating server certificate chain: %s" % ce)
            raise
Beispiel #32
0
def run():
    """
    Runs through TLS hosts in the Alexa top 1000 to test TLS functionality

    :return:
        A bool - if the test succeeded without any socket errors
    """

    task_start = time.time()
    success = 0
    tls_errors = 0
    socket_errors = 0
    mismatch_info = []

    context = ValidationContext(allow_fetching=True)

    with open(os.path.join(fixtures_dir, 'alexa_top_1000.csv'), 'rb') as f:
        for line in f:
            domain = line.decode('utf-8').rstrip()
            os_result = None
            cv_result = None
            os_message = None
            cv_message = None

            try:
                os_start = time.time()
                con = tls.TLSSocket(domain, 443, timeout=3)
                con.close()
                success += 1
                os_result = 'OK'
                os_message = 'Success'
                _color('green', 'OK', domain, os_start)
            except (TLSVerificationError) as e:
                tls_errors += 1
                os_result = 'TLS'
                os_message = str_cls(e)
                _color('yellow', 'TLS', domain, os_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                os_result = 'SOCK'
                os_message = str_cls(e)
                _color('red', 'SOCK', domain, os_start, str_cls(e))

            try:
                cv_start = time.time()
                session = tls.TLSSession(manual_validation=True)
                con = tls.TLSSocket(domain, 443, timeout=3, session=session)
                validator = CertificateValidator(con.certificate,
                                                 con.intermediates, context)
                validator.validate_tls(domain)
                con.close()
                success += 1
                cv_result = 'OK'
                cv_message = 'Success'
                _color('green', 'OK', domain, cv_start)
            except (PathValidationError, PathBuildingError) as e:
                tls_errors += 1
                cv_result = 'TLS'
                cv_message = str_cls(e)
                _color('yellow', 'TLS', domain, cv_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                cv_result = 'SOCK'
                cv_message = str_cls(e)
                _color('red', 'SOCK', domain, cv_start, str_cls(e))

            if os_result != cv_result:
                mismatch_info.append(
                    [domain, os_result, os_message, cv_result, cv_message])

    total_time = time.time() - task_start
    total_domains = success + tls_errors + socket_errors

    stats = []
    if success > 0:
        stats.append('%d [%sOK%s]' % (success, Fore.GREEN, Fore.RESET))
    if tls_errors > 0:
        stats.append('%d [%sTLS%s]' % (tls_errors, Fore.YELLOW, Fore.RESET))
    if socket_errors > 0:
        stats.append('%d [%sSOCK%s]' % (socket_errors, Fore.RED, Fore.RESET))
    print('')
    print('Checked %d domains in %.3f seconds - %s' %
          (total_domains, total_time, ' '.join(stats)))

    if mismatch_info:
        print('')
        for info in mismatch_info:
            os_result = '[%s] %s' % (info[1], info[2])
            cv_result = '[%s] %s' % (info[3], info[4])
            _color(
                'red', 'DIFF',
                'oscrypto and certvalidator results for %s are different' %
                info[0], None, os_result, cv_result)

    return socket_errors == 0
Beispiel #33
0
def run():
    """
    Runs through TLS hosts in the Alexa top 1000 to test TLS functionality

    :return:
        A bool - if the test succeeded without any socket errors
    """

    task_start = time.time()
    success = 0
    tls_errors = 0
    socket_errors = 0
    mismatch_info = []

    context = ValidationContext(allow_fetching=True)

    with open(os.path.join(fixtures_dir, 'alexa_top_1000.csv'), 'rb') as f:
        for line in f:
            domain = line.decode('utf-8').rstrip()
            os_result = None
            cv_result = None
            os_message = None
            cv_message = None

            try:
                os_start = time.time()
                con = tls.TLSSocket(domain, 443, timeout=3)
                con.close()
                success += 1
                os_result = 'OK'
                os_message = 'Success'
                _color('green', 'OK', domain, os_start)
            except (TLSVerificationError) as e:
                tls_errors += 1
                os_result = 'TLS'
                os_message = str_cls(e)
                _color('yellow', 'TLS', domain, os_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                os_result = 'SOCK'
                os_message = str_cls(e)
                _color('red', 'SOCK', domain, os_start, str_cls(e))

            try:
                cv_start = time.time()
                session = tls.TLSSession(manual_validation=True)
                con = tls.TLSSocket(domain, 443, timeout=3, session=session)
                validator = CertificateValidator(con.certificate, con.intermediates, context)
                validator.validate_tls(domain)
                con.close()
                success += 1
                cv_result = 'OK'
                cv_message = 'Success'
                _color('green', 'OK', domain, cv_start)
            except (PathValidationError, PathBuildingError) as e:
                tls_errors += 1
                cv_result = 'TLS'
                cv_message = str_cls(e)
                _color('yellow', 'TLS', domain, cv_start, str_cls(e))
            except (socket.error) as e:
                socket_errors += 1
                cv_result = 'SOCK'
                cv_message = str_cls(e)
                _color('red', 'SOCK', domain, cv_start, str_cls(e))

            if os_result != cv_result:
                mismatch_info.append([
                    domain,
                    os_result,
                    os_message,
                    cv_result,
                    cv_message
                ])

    total_time = time.time() - task_start
    total_domains = success + tls_errors + socket_errors

    stats = []
    if success > 0:
        stats.append('%d [%sOK%s]' % (success, Fore.GREEN, Fore.RESET))
    if tls_errors > 0:
        stats.append('%d [%sTLS%s]' % (tls_errors, Fore.YELLOW, Fore.RESET))
    if socket_errors > 0:
        stats.append('%d [%sSOCK%s]' % (socket_errors, Fore.RED, Fore.RESET))
    print('')
    print('Checked %d domains in %.3f seconds - %s' % (total_domains, total_time, ' '.join(stats)))

    if mismatch_info:
        print('')
        for info in mismatch_info:
            os_result = '[%s] %s' % (info[1], info[2])
            cv_result = '[%s] %s' % (info[3], info[4])
            _color(
                'red',
                'DIFF',
                'oscrypto and certvalidator results for %s are different' % info[0],
                None,
                os_result,
                cv_result
            )

    return socket_errors == 0
Beispiel #34
0
    def verify_bib(self, ctr, bib):
        addl_protected = b''
        addl_unprotected = {}
        aad_scope = 0x7
        for param in bib.payload.parameters:
            if param.type_code == 3:
                addl_protected = bytes(param.value)
            elif param.type_code == 4:
                addl_unprotected = dict(param.value)
            elif param.type_code == 5:
                aad_scope = int(param.value)

        addl_protected_map = cbor2.loads(
            addl_protected) if addl_protected else {}
        dupe_keys = set(addl_protected_map.keys()).intersection(
            set(addl_unprotected.keys()))
        if dupe_keys:
            LOGGER.warning('Duplicate keys in additional headers: %s',
                           dupe_keys)
            return StatusReport.ReasonCode.FAILED_SEC
        addl_headers = dict(addl_protected_map)
        addl_headers.update(addl_unprotected)

        bundle_at = DtnTimeField.dtntime_to_datetime(
            ctr.bundle.primary.create_ts.getfieldval('dtntime'))
        val_ctx = ValidationContext(
            trust_roots=[
                cert.public_bytes(serialization.Encoding.DER)
                for cert in self._ca_certs
            ],
            other_certs=[
                cert.public_bytes(serialization.Encoding.DER)
                for cert in self._cert_chain
            ],
            moment=bundle_at,
        )
        LOGGER.debug('Validating certificates at time %s', bundle_at)

        failure = None
        for (ix, blk_num) in enumerate(bib.payload.targets):
            target_blk = ctr.block_num(blk_num)
            for result in bib.payload.results[ix].results:
                msg_cls = CoseMessage._COSE_MSG_ID[result.type_code]

                # replace detached payload
                msg_enc = bytes(result.getfieldval('value'))
                msg_dec = cbor2.loads(msg_enc)
                LOGGER.debug('Received COSE message\n%s',
                             encode_diagnostic(msg_dec))
                msg_dec[2] = target_blk.getfieldval('btsd')

                msg_obj = msg_cls.from_cose_obj(msg_dec)
                msg_obj.external_aad = CoseContext.get_bpsec_cose_aad(
                    ctr, target_blk, bib, aad_scope, addl_protected)
                # use additional headers as defaults
                for (key, val) in msg_cls._parse_header(addl_headers).items():
                    msg_obj.uhdr.setdefault(key, val)
                LOGGER.info('full uhdr %s', msg_obj.uhdr)

                x5t_item = msg_obj.get_attr(headers.X5t)
                x5t = X5T.decode(x5t_item) if x5t_item else None

                x5chain_item = msg_obj.get_attr(headers.X5chain)
                if isinstance(x5chain_item, bytes):
                    x5chain = [x5chain_item]
                else:
                    x5chain = x5chain_item
                LOGGER.info('Validating X5t %s and X5chain length %d',
                            x5t.encode() if x5t else None,
                            len(x5chain) if x5chain else 0)

                if x5t is None and x5chain:
                    # Only one possible end-entity cert
                    LOGGER.warning('No X5T in header, assuming single chain')
                    found_chain = x5chain
                else:
                    try:
                        found_chain = x5chain if x5t.matches(
                            x5chain[0]) else None
                        if not found_chain:
                            raise RuntimeError(
                                'No chain matcing end-entity cert for {}'.
                                format(x5t.encode()))
                        LOGGER.debug(
                            'Found chain matcing end-entity cert for %s',
                            x5t.encode())
                    except Exception as err:
                        LOGGER.error(
                            'Failed to find cert chain for block num %d: %s',
                            blk_num, err)
                        failure = StatusReport.ReasonCode.FAILED_SEC
                        continue

                LOGGER.debug('Validating chain with %d certs against %d CAs',
                             len(found_chain), len(self._ca_certs))
                try:
                    val = CertificateValidator(
                        end_entity_cert=found_chain[0],
                        intermediate_certs=found_chain[1:],
                        validation_context=val_ctx)
                    val.validate_usage(
                        key_usage={'digital_signature'},
                        extended_key_usage={'1.3.6.1.5.5.7.3.35'},
                        extended_optional=True)
                except Exception as err:
                    LOGGER.error('Failed to verify chain on block num %d: %s',
                                 blk_num, err)
                    failure = StatusReport.ReasonCode.FAILED_SEC
                    continue

                peer_nodeid = bib.payload.source
                end_cert = x509.load_der_x509_certificate(
                    found_chain[0], default_backend())
                authn_nodeid = tcpcl.session.match_id(
                    peer_nodeid, end_cert, x509.UniformResourceIdentifier,
                    LOGGER, 'NODE-ID')
                if not authn_nodeid:
                    LOGGER.error(
                        'Failed to authenticate peer "%s" on block num %d',
                        peer_nodeid, blk_num)
                    failure = StatusReport.ReasonCode.FAILED_SEC
                    # Continue on to verification

                try:
                    msg_obj.key = self.extract_cose_key(end_cert.public_key())
                    msg_obj.verify_signature()
                    LOGGER.info('Verified signature on block num %d', blk_num)
                except Exception as err:
                    LOGGER.error(
                        'Failed to verify signature on block num %d: %s',
                        blk_num, err)
                    failure = StatusReport.ReasonCode.FAILED_SEC

        return failure
Beispiel #35
0
    def valider_x509_enveloppe(self,
                               enveloppe: EnveloppeCertificat,
                               date_reference: datetime.datetime = None):
        """
        Valide une enveloppe
        :param enveloppe:
        :param date_reference:
        :param ignorer_date: Charger le certificat en utilisation date courante ou fin de periode de validite
        :return: Resultat de validation (toujours valide)
        :raises certvalidator.errors.PathBuildingError: Si le path est invalide
        """
        cert_pem = enveloppe.certificat_pem.encode('utf-8')
        inter_list = list()

        # self._logger.debug("CERT PEM :\n%s" % enveloppe.certificat_pem)
        for pem in enveloppe.reste_chaine_pem:
            # self._logger.debug("Chaine PEM :\n%s" % pem.strip())
            inter_list.append(pem.strip().encode('utf-8'))

        if date_reference is not None:
            # batir un contexte avec la date
            validation_context = ValidationContext(
                moment=date_reference, trust_roots=[self.__cert_millegrille])
        else:
            validation_context = self.__validation_context

        # Verifier le certificat - noter qu'une exception est lancee en cas de probleme
        try:
            validator = CertificateValidator(
                cert_pem,
                intermediate_certs=inter_list,
                validation_context=validation_context)
            resultat = validator.validate_usage({'digital_signature'})
            enveloppe.set_est_verifie(True)
        except PathValidationError as pve:
            msg = pve.args[0]
            if 'expired' in msg:
                self._logger.info(
                    "Un des certificats est expire, verifier en fonction de la date de reference"
                )
                # Le certificat est expire, on fait la validation pour la fin de la periode de validite
                date_reference = pytz.UTC.localize(enveloppe.not_valid_after)
                validation_context = ValidationContext(
                    moment=date_reference,
                    trust_roots=[self.__cert_millegrille])
                validator = CertificateValidator(
                    cert_pem,
                    intermediate_certs=inter_list,
                    validation_context=validation_context)
                try:
                    resultat = validator.validate_usage({'digital_signature'})
                    enveloppe.set_est_verifie(True)
                    raise CertificatExpire(
                    )  # La chaine est valide pour une date anterieure
                except PathValidationError as pve:
                    if self._logger.isEnabledFor(logging.DEBUG):
                        self._logger.exception(
                            "Erreur validation path certificat")
                    else:
                        self._logger.info(
                            "Erreur validation path certificat : %s", str(pve))
            else:
                if self._logger.isEnabledFor(logging.DEBUG):
                    self._logger.exception("Erreur validation path certificat")
                else:
                    self._logger.info("Erreur validation path certificat : %s",
                                      str(pve))
                raise pve

        except PathBuildingError as pbe:
            # Verifier si on a une millegrille tierce
            dernier_cert_pem = inter_list[-1]
            dernier_cert = EnveloppeCertificat(certificat_pem=dernier_cert_pem)
            if dernier_cert.is_rootCA:
                idmg = dernier_cert.idmg
                # Verifier si le idmg est dans la liste des idmg autorises
                autorisation = self.__autorisations_idmg.get(idmg)
                if autorisation is None:
                    # Pas autorise, lancer l'exception
                    raise pbe
                elif autorisation.get('domaines_permis'):
                    # Valider la chaine en fonction de la racine fournie
                    if date_reference is not None:
                        # batir un contexte avec la date
                        validation_context = ValidationContext(
                            moment=date_reference,
                            trust_roots=[
                                self.__cert_millegrille, dernier_cert_pem
                            ])
                    else:
                        validation_context = ValidationContext(trust_roots=[
                            self.__cert_millegrille, dernier_cert_pem
                        ])

                    validator = CertificateValidator(
                        cert_pem,
                        intermediate_certs=inter_list,
                        validation_context=validation_context)

                    validator.validate_usage({'digital_signature'})

                    # Valide, on lance une exception pour indiquer la condition de validite (business rule)
                    raise AutorisationConditionnelleDomaine(
                        autorisation['domaines_permis'], idmg, enveloppe)

        return resultat
Beispiel #36
0
 def verify_chain(self, key_usage: Set[str]):
     val = CertificateValidator(
         end_entity_cert=self.cert_chain[0],
         intermediate_certs=self.cert_chain[1:],
     )
     return val.validate_usage(key_usage)