예제 #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)
예제 #2
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']))
예제 #3
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)
예제 #4
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']))
    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']))
예제 #6
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
예제 #7
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']))
예제 #8
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)
    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']))
예제 #10
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)
예제 #11
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
예제 #12
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)
예제 #13
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
예제 #14
0
파일: context.py 프로젝트: okrt/signify
    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
예제 #15
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']))
예제 #16
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"})
예제 #17
0
파일: context.py 프로젝트: wtfuzz/signify
    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]
예제 #18
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)
예제 #19
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
예제 #20
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"})
예제 #21
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
예제 #22
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
예제 #23
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)
예제 #24
0
    def validate(self, root_ca: CaSecret, with_openssl: bool = False):
        '''
        Validate that the cert and its certchain are anchored to the root cert.
        This function does not check certificate recovation or OCSP

        :param Secret root_ca: the self-signed root CA to validate against
        :param with_openssl: also use the openssl binary to validate the cert
        :returns: (none)
        :raises: ValueError if the certchain is invalid
        '''

        pem_signed_cert = self.cert_as_pem()
        pem_cert_chain = [
            x.public_bytes(serialization.Encoding.PEM) for x in self.cert_chain
        ]
        context = ValidationContext(trust_roots=[root_ca.cert_as_pem()])
        validator = CertificateValidator(pem_signed_cert,
                                         pem_cert_chain,
                                         validation_context=context)
        try:
            validator.validate_usage(set())
        except (ValidationError, PathBuildingError) as exc:
            raise ValueError(f'Certchain failed validation: {exc}') from exc

        if not with_openssl:
            return

        tmpdir = tempfile.TemporaryDirectory()
        rootfile = tmpdir.name + '/rootca.pem'
        with open(rootfile, 'w') as file_desc:
            file_desc.write(root_ca.cert_as_pem().decode('utf-8'))

        certfile = tmpdir.name + '/cert.pem'
        with open(certfile, 'w') as file_desc:
            file_desc.write(self.cert_as_pem().decode('utf-8'))

        cmd = [
            'openssl',
            'verify',
            '-CAfile',
            rootfile,
        ]

        if self.cert_chain:
            chainfile = tmpdir.name + '/chain.pem'
            with open(chainfile, 'w') as file_desc:
                for cert in self.cert_chain:
                    file_desc.write(
                        cert.public_bytes(
                            serialization.Encoding.PEM).decode('utf-8'))
            cmd.extend(['-untrusted', chainfile])

        cmd.append(certfile)
        result = subprocess.run(cmd)

        if result.returncode != 0:
            raise ValueError(
                f'Certificate validation with command {" ".join(cmd)} '
                f'failed: {result.returncode} for cert {certfile} and '
                f'root CA {rootfile}')

        tmpdir.cleanup()

        _LOGGER.debug('Successfully validated certchain using OpenSSL for '
                      f'cert {certfile} and root CA {rootfile}')
예제 #25
0
    def verify(self, certificate, message, signature):
        """
        :param expected_measurement:
            Hex-String of the expected measurement, 
            e.g. "4ff505f350698c78e8b3b49b8e479146ce3896a06cd9e5109dfec8f393f14025"

        :param certificate:
            A byte string of the certificate
 
        :param message:
            A byte string of the data the signature is for
 
        :param signature:
            A byte string of the data the signature is for
 
        :raises:
            CertificateValidationError
            SignatureValidationError
            MessageParsingError
            ISVEnclaveQuoteStatusError
            EnclaveQuoteFlagsError
            MeasurementMismatchError
 
        :return:
            the QuoteBody inside the message
        """

        # Step 1: validate certificate with Intel SGX attestation root CA
        try:
            validator = CertificateValidator(certificate,
                                             validation_context=self.context)
            validator.validate_usage(set(["digital_signature"]))
        except:
            raise CertificateValidationError

        # Step 2: verify message signature
        try:
            pubk = asymmetric.load_public_key(certificate)
            asymmetric.rsa_pkcs1v15_verify(pubk, signature, message, "sha256")
        except:
            raise SignatureValidationError

        # Step 3: parse message and get quote body
        try:
            message_dic = json.loads(message)
            quote_body_encoded = message_dic["isvEnclaveQuoteBody"]
            isv_enclave_quote_status = message_dic["isvEnclaveQuoteStatus"]
            quote_body = _QuoteBody.from_base64_string(quote_body_encoded)
        except:
            raise MessageParsingError

        # Step 4: check isv enclave quote status
        if isv_enclave_quote_status == "OK":
            pass
        elif isv_enclave_quote_status == "CONFIGURATION_NEEDED":
            if not self.accept_configuration_needed:
                raise ISVEnclaveQuoteStatusError
        elif isv_enclave_quote_status == "GROUP_OUT_OF_DATE":
            if not self.accept_group_out_of_date:
                raise ISVEnclaveQuoteStatusError
        else:
            raise ISVEnclaveQuoteStatusError

        # Step 5: check enclave quote flags
        flags = quote_body.flags
        if not (flags & Verification.IASAttributeFlags.INIT):
            raise EnclaveQuoteFlagsError
        if not (flags & Verification.IASAttributeFlags.MODE64BIT):
            raise EnclaveQuoteFlagsError
        if flags & Verification.IASAttributeFlags.DEBUG:
            if not self.accept_debug:
                raise EnclaveQuoteFlagsError

        # Step 6: assert expected measurement
        if self.expected_measurement:
            measurement = bytes(quote_body.mrenclave).hex()
            if not (measurement == self.expected_measurement):
                raise MeasurementMismatchError(
                    f"Expected: {self.expected_measurement}. Actual: {measurement}."
                )

        return QuoteBody(quote_body)