Ejemplo n.º 1
0
def verify(issuer: x509.Certificate, cert: x509.Certificate):
    """
    Verify certificate with its issuer
    """
    h = cert.signature_hash_algorithm
    issuer.public_key().verify(cert.signature, cert.tbs_certificate_bytes,
                               padding.PKCS1v15(), h)
Ejemplo n.º 2
0
    def from_certificate(cls, cert: Certificate) -> "SrkItemRSA":
        """Create SRKItemRSA from certificate."""
        assert isinstance(cert, Certificate)

        flag = 0
        try:
            key_usage = cert.extensions.get_extension_for_class(
                KeyUsage)  # type:ignore
            assert isinstance(key_usage.value, KeyUsage)
            if key_usage.value.key_cert_sign:
                flag = 0x80
        except ExtensionNotFound:
            pass

        if isinstance(cert.public_key(), rsa.RSAPublicKey):
            public_key = cert.public_key()
            assert isinstance(public_key, rsa.RSAPublicKey)
            pub_key_numbers = public_key.public_numbers()
            assert isinstance(pub_key_numbers, rsa.RSAPublicNumbers)
            # get modulus and exponent of public key since we are RSA
            modulus_len = math.ceil(pub_key_numbers.n.bit_length() / 8)
            exponent_len = math.ceil(pub_key_numbers.e.bit_length() / 8)
            modulus = pub_key_numbers.n.to_bytes(modulus_len, "big")
            exponent = pub_key_numbers.e.to_bytes(exponent_len, "big")

            return cls(modulus, exponent, flag)
        raise NotImplementedSRKCertificate()
Ejemplo n.º 3
0
    def _save(
            cls, key: PrivateKey, cert: x509.Certificate, directory: str,
            name: str, key_format: Encoding
    ) -> ((str, bytes), (str, bytes), (str, bytes)):

        private_file = f'{name}.{key_format.name.lower()}'
        private_path = join(directory, private_file)
        private_bytes = key.private_bytes(key_format, PrivateFormat.PKCS8,
                                          NoEncryption())

        public_file = f'{name}.pub.{key_format.name.lower()}'
        public_path = join(directory, public_file)
        public_bytes = cert.public_key().public_bytes(key_format,
                                                      PublicFormat.PKCS1)

        cert_file = f'{name}.cert.{key_format.name.lower()}'
        cert_path = join(directory, cert_file)
        cert_bytes = cert.public_bytes(key_format)

        serialized = ((private_path, private_bytes),
                      (public_path, public_bytes), (cert_path, cert_bytes))

        for file_path, file_bytes in serialized:
            with open(file_path, 'wb') as out:
                out.write(file_bytes)

        return serialized
Ejemplo n.º 4
0
def x509_certificate_to_json(certificate: x509.Certificate) -> Dict[str, Any]:
    public_key = certificate.public_key()

    try:
        public_key_size = public_key.key_size  # type: ignore
    except AttributeError:
        public_key_size = None

    public_key_json = _PublicKeyAsJson(
        algorithm=public_key.__class__.__name__,
        key_size=public_key_size,
        # EC-only fields
        ec_curve_name=public_key.curve.name if isinstance(public_key, EllipticCurvePublicKey) else None,
        ec_x=public_key.public_numbers().x if isinstance(public_key, EllipticCurvePublicKey) else None,
        ec_y=public_key.public_numbers().y if isinstance(public_key, EllipticCurvePublicKey) else None,
        # RSA-only fields
        rsa_e=public_key.public_numbers().e if isinstance(public_key, RSAPublicKey) else None,
        rsa_n=public_key.public_numbers().n if isinstance(public_key, RSAPublicKey) else None,
    )

    signature_hash_algorithm: Optional[_HashAlgorithmAsJson]
    if certificate.signature_hash_algorithm:
        signature_hash_algorithm = _HashAlgorithmAsJson(
            name=certificate.signature_hash_algorithm.name,
            digest_size=certificate.signature_hash_algorithm.digest_size,
        )
    else:
        signature_hash_algorithm = None

    # We may get garbage/invalid certificates so we need to handle ValueErrors.
    # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information
    subject_field: Optional[x509.name.Name]
    try:
        subject_field = certificate.subject
    except ValueError:
        subject_field = None

    issuer_field: Optional[x509.name.Name]
    try:
        issuer_field = certificate.issuer
    except ValueError:
        issuer_field = None

    cert_as_json = _X509CertificateAsJson(
        as_pem=certificate.public_bytes(Encoding.PEM).decode("ascii"),
        hpkp_pin=b64encode(get_public_key_sha256(certificate)).decode("ascii"),
        fingerprint_sha1=b64encode(certificate.fingerprint(hashes.SHA1())).decode("ascii"),
        fingerprint_sha256=b64encode(certificate.fingerprint(hashes.SHA256())).decode("ascii"),
        serial_number=certificate.serial_number,
        not_valid_before=certificate.not_valid_before,
        not_valid_after=certificate.not_valid_after,
        subject_alternative_name=_SubjAltNameAsJson(dns=extract_dns_subject_alternative_names(certificate)),
        signature_hash_algorithm=signature_hash_algorithm,
        signature_algorithm_oid=certificate.signature_algorithm_oid,
        subject=subject_field,
        issuer=issuer_field,
        public_key=public_key_json,
    )
    return asdict(cert_as_json)
Ejemplo n.º 5
0
    def _get_basic_certificate_text(cls,
                                    certificate: Certificate) -> List[str]:
        text_output = [
            cls._format_field(
                "SHA1 Fingerprint:",
                binascii.hexlify(certificate.fingerprint(
                    hashes.SHA1())).decode("ascii")),
            cls._format_field("Common Name:",
                              _get_name_as_short_text(certificate.subject)),
            cls._format_field("Issuer:",
                              _get_name_as_short_text(certificate.issuer)),
            cls._format_field("Serial Number:",
                              str(certificate.serial_number)),
            cls._format_field("Not Before:",
                              certificate.not_valid_before.date().isoformat()),
            cls._format_field("Not After:",
                              certificate.not_valid_after.date().isoformat()),
            cls._format_field("Public Key Algorithm:",
                              certificate.public_key().__class__.__name__),
        ]

        if certificate.signature_hash_algorithm:
            # The signature_hash_algorithm can be None if signature did not use separate hash (ED25519, ED448)
            # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certificate.signature_hash_algorithm
            text_output.append(
                cls._format_field("Signature Algorithm:",
                                  certificate.signature_hash_algorithm.name))

        public_key = certificate.public_key()
        if isinstance(public_key, EllipticCurvePublicKey):
            text_output.append(
                cls._format_field("Key Size:", str(public_key.curve.key_size)))
            text_output.append(
                cls._format_field("Curve:", str(public_key.curve.name)))
        elif isinstance(public_key, RSAPublicKey):
            text_output.append(
                cls._format_field("Key Size:", str(public_key.key_size)))
            text_output.append(
                cls._format_field(
                    "Exponent:",
                    str(public_key.public_numbers().e)))  # type: ignore
        else:
            # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/314
            pass

        try:
            # Print the SAN extension if there's one
            text_output.append(
                cls._format_field(
                    "DNS Subject Alternative Names:",
                    str(extract_dns_subject_alternative_names(certificate))))
        except KeyError:
            pass

        return text_output
Ejemplo n.º 6
0
    def store_certificate(self, certificate: Certificate) -> Path:
        """Store the supplied certificate as a PEM file.
        """
        # A given certificate's path is always <SHA-256>.pem.
        cert_file_name = hexlify(certificate.fingerprint(SHA256())).decode('ascii')
        cert_path = self._path / f'{cert_file_name}.pem'

        # If the cert is NOT already there, add it
        if not cert_path.exists():
            with open(cert_path, 'w') as cert_file:
                cert_file.write(certificate.public_bytes(Encoding.PEM).decode('ascii'))

        return cert_path
Ejemplo n.º 7
0
def x509_certificate_to_json(certificate: x509.Certificate) -> Dict[str, Any]:
    result: Dict[str, Union[None, str, Dict[str, Any]]] = {
        # Add general info
        "as_pem": certificate.public_bytes(Encoding.PEM).decode("ascii"),
        "hpkp_pin": b64encode(
            get_public_key_sha256(certificate)).decode("utf-8"),  # RFC 7469
        # Add some of the fields of the cert
        "serialNumber": str(certificate.serial_number),
        "notBefore": certificate.not_valid_before.isoformat(),
        "notAfter": certificate.not_valid_after.isoformat(),
        "subjectAlternativeName": {
            "DNS": extract_dns_subject_alternative_names(certificate)
        },
    }

    if certificate.signature_hash_algorithm:
        # The signature_hash_algorithm can be None if signature did not use separate hash (ED25519, ED448)
        # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certificate.signature_hash_algorithm
        result[
            "signatureAlgorithm"] = certificate.signature_hash_algorithm.name
    else:
        result["signatureAlgorithm"] = None

    # We may get garbage/invalid certificates so we need to handle ValueErrors.
    # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information
    for name_field in ["subject", "issuer"]:
        try:
            result[name_field] = getattr(certificate, name_field)
        except ValueError as e:
            x509name_as_json = _X509NameAsJson(rfc4514_string=None,
                                               attributes=None,
                                               parsing_error=e.args[0])
            result[name_field] = asdict(x509name_as_json)

    # Add some info about the public key
    public_key = certificate.public_key()
    public_key_dict: Dict[str, Union[str, int]] = {
        "algorithm": public_key.__class__.__name__
    }
    if isinstance(public_key, EllipticCurvePublicKey):
        public_key_dict["size"] = public_key.curve.key_size
        public_key_dict["curve"] = public_key.curve.name
    elif isinstance(public_key, RSAPublicKey):
        public_key_dict["size"] = public_key.key_size
        public_key_dict["exponent"] = public_key.public_numbers().e
    else:
        # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/402
        pass

    result["publicKey"] = public_key_dict
    return result
Ejemplo n.º 8
0
    def from_crypto(cls, certificate: x509.Certificate):
        # TODO: sometimes serial numbers are too large even for SQLite BIGINT
        m = cls()
        m.pem_data = certificate.public_bytes(
            encoding=serialization.Encoding.PEM)
        m.not_after = certificate.not_valid_after
        m.not_before = certificate.not_valid_before
        m.fingerprint = certificate.fingerprint(hashes.SHA256())

        subject: x509.Name = certificate.subject

        cns = subject.get_attributes_for_oid(NameOID.COMMON_NAME)
        if cns is not None:
            m.x509_cn = cns[0].value
        return m
Ejemplo n.º 9
0
    def from_crypto_type(cls, certificate: x509.Certificate, certtype: CertificateType):
        # type: (certtype, x509.Certificate, CertificateType) -> Certificate
        m = cls()
        m.pem_data = certificate.public_bytes(serialization.Encoding.PEM)
        m.not_after = certificate.not_valid_after
        m.not_before = certificate.not_valid_before
        m.fingerprint = certificate.fingerprint(hashes.SHA256())
        m.discriminator = certtype.value

        subject: x509.Name = certificate.subject
        cns = subject.get_attributes_for_oid(NameOID.COMMON_NAME)
        if cns is not None:
            m.x509_cn = cns[0].value

        return m
Ejemplo n.º 10
0
 def builder(
     *, config: SigningConfig, certificate: Certificate, element_id: str
 ) -> Element:
     return ds.Signature(
         ds.SignedInfo(
             ds.CanonicalizationMethod(Algorithm=XML_EXC_C14N),
             ds.SignatureMethod(
                 Algorithm=utils.signature_method_algorithm(config.signature_method)
             ),
             ds.Reference(
                 ds.Transforms(
                     ds.Transform(Algorithm=XMLDSIG_ENVELOPED_SIGNATURE),
                     ds.Transform(Algorithm=XML_EXC_C14N),
                 ),
                 ds.DigestMethod(
                     Algorithm=utils.digest_method_algorithm(config.digest_method)
                 ),
                 ds.DigestValue(),
                 URI="#" + element_id,
             ),
         ),
         ds.SignatureValue(),
         ds.KeyInfo(
             ds.X509Data(utils.ascii_b64(certificate.public_bytes(Encoding.DER)))
         ),
     )
Ejemplo n.º 11
0
 def from_certificate(cls, cert: Certificate) -> "SrkItem":
     """Pick up the right implementation of an SRK item."""
     assert isinstance(cert, Certificate)
     public_key = cert.public_key()
     if isinstance(public_key, rsa.RSAPublicKey):
         return SrkItemRSA.from_certificate(cert)
     raise NotImplementedSRKCertificate()
Ejemplo n.º 12
0
    def put_certificate(
        cls,
        session: "core.AuthSession",
        object_id: int,
        label: str,
        domains: int,
        capabilities: CAPABILITY,
        certificate: x509.Certificate,
    ) -> "Opaque":
        """Import an X509 certificate into the YubiHSM as an Opaque.

        :param session: The session to import via.
        :param object_id: The ID to set for the object. Set to 0 to let the
            YubiHSM designate an ID.
        :param label: A text label to give the object.
        :param domains: The set of domains to assign the object to.
        :param capabilities: The set of capabilities to give the object.
        :param certificate: A certificate to import.
        :return: A reference to the newly created object.
        """
        encoded_cert = certificate.public_bytes(Encoding.DER)
        return cls.put(
            session,
            object_id,
            label,
            domains,
            capabilities,
            ALGORITHM.OPAQUE_X509_CERTIFICATE,
            encoded_cert,
        )
Ejemplo n.º 13
0
def test_decode_base64_cert_ok(
    trusted_cert_b64: str,
    trusted_cert: Certificate,
) -> None:
    assert _decode_base64_cert(trusted_cert_b64).public_bytes(
        Encoding.DER
    ) == trusted_cert.public_bytes(Encoding.DER)
Ejemplo n.º 14
0
def create_degenerate_certificate(certificate: x509.Certificate) -> ContentInfo:
    """Produce a PKCS#7 Degenerate case with a single certificate.

    Args:
          certificate (x509.Certificate): The certificate to attach to the degenerate pkcs#7 payload.
    Returns:
          ContentInfo: The ContentInfo containing a SignedData structure.
    """
    der_bytes = certificate.public_bytes(
        serialization.Encoding.DER
    )
    asn1cert = parse_certificate(der_bytes)

    empty = ContentInfo({
        'content_type': ContentType('data')
    })
    
    sd = SignedData({
        'version': CMSVersion(1),
        'encap_content_info': empty,
        'digest_algorithms': DigestAlgorithms([]),
        'certificates': CertificateSet([CertificateChoices('certificate', asn1cert)]),
        'signer_infos': SignerInfos([]),
        'crls': RevocationInfoChoices([]),
    })

    return ContentInfo({
        'content_type': ContentType('signed_data'),
        'content': sd,
    })
Ejemplo n.º 15
0
    def _write_tls_certificate(
            self,
            port: int,  # used to avoid duplicate certs with the same IP
            certificate: Certificate,
            force: bool = True) -> Path:

        # Read
        x509 = OpenSSL.crypto.X509.from_cryptography(certificate)
        subject_components = x509.get_subject().get_components()
        common_name_as_bytes = subject_components[0][1]
        common_name_on_certificate = common_name_as_bytes.decode()
        host = common_name_on_certificate

        certificate_filepath = self.generate_certificate_filepath(host=host,
                                                                  port=port)
        certificate_already_exists = certificate_filepath.is_file()
        if force is False and certificate_already_exists:
            raise FileExistsError(
                'A TLS certificate already exists at {}.'.format(
                    certificate_filepath))

        # Write
        certificate_filepath.parent.mkdir(parents=True, exist_ok=True)
        with open(certificate_filepath, 'wb') as certificate_file:
            public_pem_bytes = certificate.public_bytes(
                self.TLS_CERTIFICATE_ENCODING)
            certificate_file.write(public_pem_bytes)

        self.log.debug(
            f"Saved TLS certificate for {host} to {certificate_filepath}")
        return certificate_filepath
Ejemplo n.º 16
0
    def _validate(crl: x509.CertificateRevocationList,
                  issuer: x509.Certificate):
        """Validates a crl against a issuer certificate"""

        if crl.next_update is None:
            # rfc5280: Conforming CRL issuers MUST
            # include the nextUpdate field in all CRLs.
            raise CouldNotGetValidCRLError("CRL is missing next update field")

        if not (crl.next_update > datetime.utcnow()
                and crl.last_update < datetime.utcnow()):
            raise CouldNotGetValidCRLError(
                f"CRL failed date validation. "
                f"Last update: '{crl.last_update}' Next Update: '{crl.next_update}'"
            )

        if not crl.issuer == issuer.subject:
            raise CouldNotGetValidCRLError(
                f"CRL failed issuer validation. "
                f"Expected: {issuer.subject.rfc4514_string()} "
                f"Actual: {crl.issuer.rfc4514_string()}.")

        # cast because mypy. The type of key is checked
        # when it is loaded in CertRetriever.
        if not crl.is_signature_valid(cast(RSAPublicKey, issuer.public_key())):
            raise CouldNotGetValidCRLError("CRL failed signature validation")
Ejemplo n.º 17
0
 def assertBasic(  # pylint: disable=invalid-name
     self, cert: x509.Certificate, algo: typing.Type[hashes.HashAlgorithm] = hashes.SHA256
 ) -> None:
     """Assert some basic key properties."""
     self.assertEqual(cert.version, x509.Version.v3)
     self.assertIsInstance(cert.public_key(), rsa.RSAPublicKey)
     self.assertIsInstance(cert.signature_hash_algorithm, algo)
def key_type_and_size(cert: x509.Certificate) -> List[Tuple[bool, str]]:
    checks = []

    # get the public key
    pk = cert.public_key()

    # check the keypair type
    exp_pk_type = 'RSA'
    pk_type = 'RSA' if isinstance(pk, rsa.RSAPublicKey) else 'NOT ALLOWED'
    msg = 'The keypair must be %s' % exp_pk_type
    res = FAILURE if pk_type != exp_pk_type else SUCCESS
    checks.append((res, msg))

    # check the key size
    min_size = 2048
    size = pk.key_size

    msg = ('The key size must be greater than or equal to %d (now: %d)'
           % (min_size, size))
    res = FAILURE if size < min_size else SUCCESS
    checks.append((res, msg))

    msg = ('The key size must be one of %s (now: %d)'
           % (ALLOWED_SIZES, size))
    res = FAILURE if size not in ALLOWED_SIZES else SUCCESS
    checks.append((res, msg))

    return checks
Ejemplo n.º 19
0
def inspect_certificate(cert: x509.Certificate) -> typing.Dict[str, object]:
    """
    Extract useful fields from a x509 client certificate object.
    """
    name_attrs = cert.subject.get_attributes_for_oid(COMMON_NAME)
    common_name = name_attrs[0].value if name_attrs else ""

    fingerprint_bytes = cert.fingerprint(hashes.SHA256())
    fingerprint = f"SHA256:{fingerprint_bytes.hex().zfill(64).upper()}"
    fingerprint_b64 = base64.urlsafe_b64encode(fingerprint_bytes).decode()

    not_before = cert.not_valid_before.strftime("%Y-%m-%dT%H:%M:%SZ")
    not_after = cert.not_valid_after.strftime("%Y-%m-%dT%H:%M:%SZ")

    serial_number = cert.serial_number

    data = {
        "common_name": common_name,
        "fingerprint": fingerprint,
        "fingerprint_b64": fingerprint_b64,
        "not_before": not_before,
        "not_after": not_after,
        "serial_number": serial_number,
    }
    return data
Ejemplo n.º 20
0
def verify(signature: bytes, data: bytes, certificate: Certificate,
           hasher: HashAlgorithm) -> None:
    key = certificate.public_key()
    if not isinstance(key, RSAPublicKey):
        raise TypeError(
            f"Only certificates with RSA Keys are supported. Got {key!r} instead."
        )
    key.verify(signature, data, padding.PKCS1v15(), hasher)
def make_thumbprint(certificate: x509.Certificate,
                    thumbprint_type: hashes.HashAlgorithm) -> str:
    """Create singular thumbprint, with provided certificate and algorithm type"""
    return (base64.urlsafe_b64encode(certificate.fingerprint(
        thumbprint_type))  # The thumbprint is a URL-encoded hash...
            .decode("utf-8")  # ... as a Python string ...
            .strip("=")  # ... with the padding removed.
            )
Ejemplo n.º 22
0
    def from_crypto(cls, certificate: x509.Certificate):
        m = cls()
        m.pem_data = certificate.public_bytes(
            encoding=serialization.Encoding.PEM)
        m.not_after = certificate.not_valid_after
        m.not_before = certificate.not_valid_before
        m.fingerprint = certificate.fingerprint(hashes.SHA1())

        subject: x509.Name = certificate.subject

        m.x509_cn = subject.get_attributes_for_oid(
            NameOID.COMMON_NAME)[0].value
        # m.x509_c = subject.get_attributes_for_oid(NameOID.COUNTRY_NAME)
        # m.x509_o = subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)
        # m.x509_ou = subject.get_attributes_for_oid(NameOID.ORGANIZATIONAL_UNIT_NAME)
        # m.x509_st = subject.get_attributes_for_oid(NameOID.STATE_OR_PROVINCE_NAME)

        return m
Ejemplo n.º 23
0
    def _write_tls_certificate(self,
                               certificate: Certificate,
                               host: str = None,
                               force: bool = True) -> str:

        # Read
        x509 = OpenSSL.crypto.X509.from_cryptography(certificate)
        subject_components = x509.get_subject().get_components()
        common_name_as_bytes = subject_components[0][1]
        common_name_on_certificate = common_name_as_bytes.decode()
        if not host:
            host = common_name_on_certificate

        try:
            pseudonym = certificate.subject.get_attributes_for_oid(
                NameOID.PSEUDONYM)[0]
        except IndexError:
            raise self.InvalidNodeCertificate(
                f"Missing checksum address on certificate for host '{host}'. "
                f"Does this certificate belong to an Ursula?")
        else:
            checksum_address = pseudonym.value

        if not is_checksum_address(checksum_address):
            raise self.InvalidNodeCertificate(
                "Invalid certificate wallet address encountered: {}".format(
                    checksum_address))

        # Validate
        # TODO: It's better for us to have checked this a while ago so that this situation is impossible.  #443
        if host and (host != common_name_on_certificate):
            raise ValueError(
                f"You passed a hostname ('{host}') that does not match the certificate's common name."
            )

        certificate_filepath = self.generate_certificate_filepath(
            checksum_address=checksum_address)
        certificate_already_exists = os.path.isfile(certificate_filepath)
        if force is False and certificate_already_exists:
            raise FileExistsError(
                'A TLS certificate already exists at {}.'.format(
                    certificate_filepath))

        # Write
        os.makedirs(os.path.dirname(certificate_filepath), exist_ok=True)
        with open(certificate_filepath, 'wb') as certificate_file:
            public_pem_bytes = certificate.public_bytes(
                self.TLS_CERTIFICATE_ENCODING)
            certificate_file.write(public_pem_bytes)

        nickname, pairs = nickname_from_seed(checksum_address)
        self.log.debug(
            f"Saved TLS certificate for {nickname} {checksum_address}: {certificate_filepath}"
        )

        return certificate_filepath
Ejemplo n.º 24
0
def dummy_cert(
    privkey: rsa.RSAPrivateKey,
    cacert: x509.Certificate,
    commonname: Optional[str],
    sans: List[str],
    organization: Optional[str] = None,
) -> Cert:
    """
        Generates a dummy certificate.

        privkey: CA private key
        cacert: CA certificate
        commonname: Common name for the generated certificate.
        sans: A list of Subject Alternate Names.
        organization: Organization name for the generated certificate.

        Returns cert if operation succeeded, None if not.
    """
    builder = x509.CertificateBuilder()
    builder = builder.issuer_name(cacert.subject)
    builder = builder.add_extension(x509.ExtendedKeyUsage(
        [ExtendedKeyUsageOID.SERVER_AUTH]),
                                    critical=False)
    builder = builder.public_key(cacert.public_key())

    now = datetime.datetime.now()
    builder = builder.not_valid_before(now - datetime.timedelta(days=2))
    builder = builder.not_valid_after(now + CERT_EXPIRY)

    subject = []
    is_valid_commonname = (commonname is not None and len(commonname) < 64)
    if is_valid_commonname:
        assert commonname is not None
        subject.append(x509.NameAttribute(NameOID.COMMON_NAME, commonname))
    if organization is not None:
        assert organization is not None
        subject.append(
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization))
    builder = builder.subject_name(x509.Name(subject))
    builder = builder.serial_number(x509.random_serial_number())

    ss: List[x509.GeneralName] = []
    for x in sans:
        try:
            ip = ipaddress.ip_address(x)
        except ValueError:
            ss.append(x509.DNSName(x))
        else:
            ss.append(x509.IPAddress(ip))
    # RFC 5280 §4.2.1.6: subjectAltName is critical if subject is empty.
    builder = builder.add_extension(x509.SubjectAlternativeName(ss),
                                    critical=not is_valid_commonname)
    cert = builder.sign(private_key=privkey,
                        algorithm=hashes.SHA256())  # type: ignore
    return Cert(cert)
Ejemplo n.º 25
0
def _check_ocsp_response_signature(response_ocsp: 'ocsp.OCSPResponse',
                                   issuer_cert: x509.Certificate, cert_path: str) -> None:
    """Verify an OCSP response signature against certificate issuer or responder"""
    def _key_hash(cert: x509.Certificate) -> bytes:
        return x509.SubjectKeyIdentifier.from_public_key(cert.public_key()).digest

    if (response_ocsp.responder_name == issuer_cert.subject
            or response_ocsp.responder_key_hash == _key_hash(issuer_cert)):
        # Case where the OCSP responder is also the certificate issuer
        logger.debug('OCSP response for certificate %s is signed by the certificate\'s issuer.',
                     cert_path)
        responder_cert = issuer_cert
    else:
        # Case where the OCSP responder is not the certificate issuer
        logger.debug('OCSP response for certificate %s is delegated to an external responder.',
                     cert_path)

        responder_certs = [cert for cert in response_ocsp.certificates
                           if response_ocsp.responder_name == cert.subject or \
                              response_ocsp.responder_key_hash == _key_hash(cert)]
        if not responder_certs:
            raise AssertionError('no matching responder certificate could be found')

        # We suppose here that the ACME server support only one certificate in the OCSP status
        # request. This is currently the case for LetsEncrypt servers.
        # See https://github.com/letsencrypt/boulder/issues/2331
        responder_cert = responder_certs[0]

        if responder_cert.issuer != issuer_cert.subject:
            raise AssertionError('responder certificate is not signed '
                                 'by the certificate\'s issuer')

        try:
            extension = responder_cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage)
            delegate_authorized = x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING in extension.value
        except (x509.ExtensionNotFound, IndexError):
            delegate_authorized = False
        if not delegate_authorized:
            raise AssertionError('responder is not authorized by issuer to sign OCSP responses')

        # Following line may raise UnsupportedAlgorithm
        chosen_cert_hash = responder_cert.signature_hash_algorithm
        # For a delegate OCSP responder, we need first check that its certificate is effectively
        # signed by the certificate issuer.
        crypto_util.verify_signed_payload(issuer_cert.public_key(), responder_cert.signature,
                                          responder_cert.tbs_certificate_bytes, chosen_cert_hash)

    # Following line may raise UnsupportedAlgorithm
    chosen_response_hash = response_ocsp.signature_hash_algorithm
    # We check that the OSCP response is effectively signed by the responder
    # (an authorized delegate one or the certificate issuer itself).
    if not chosen_response_hash:
        raise AssertionError("no signature hash algorithm defined")
    crypto_util.verify_signed_payload(responder_cert.public_key(), response_ocsp.signature,
                                      response_ocsp.tbs_response_bytes, chosen_response_hash)
Ejemplo n.º 26
0
def get_spki_hash(certificate: Certificate) -> bytes:
    """
    Get the public key hash, as per RFC 7469: base64 of sha256 of the public
    key encoded in DER + Subject Public Key Info format.

    We use the URL-safe base64 variant, since this is typically found in NURLs.
    """
    public_key_bytes = certificate.public_key().public_bytes(
        Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
    return urlsafe_b64encode(
        sha256(public_key_bytes).digest()).strip().rstrip(b"=")
Ejemplo n.º 27
0
def to_pem(certificate: x509.Certificate) -> str:
    """Convert an instance of x509.Certificate to a PEM string
    
    Args:
          certificate (x509.Certificate): Cert to convert
    Returns:
          PEM string
    """
    serialized = certificate.public_bytes(encoding=serialization.Encoding.PEM)

    return serialized
Ejemplo n.º 28
0
    def from_orm(cls, certificate: x509.Certificate) -> "_CertificateAsJson":
        signature_hash_algorithm: Optional[_HashAlgorithmAsJson]
        if certificate.signature_hash_algorithm:
            signature_hash_algorithm = _HashAlgorithmAsJson.from_orm(
                certificate.signature_hash_algorithm)
        else:
            signature_hash_algorithm = None

        # We may get garbage/invalid certificates so we need to handle ValueErrors.
        # See https://github.com/nabla-c0d3/sslyze/issues/403 for more information
        subject_field: Optional[_X509NameAsJson]
        try:
            subject_field = _X509NameAsJson.from_orm(certificate.subject)
        except ValueError:
            subject_field = None

        issuer_field: Optional[_X509NameAsJson]
        try:
            issuer_field = _X509NameAsJson.from_orm(certificate.issuer)
        except ValueError:
            issuer_field = None

        return cls(
            as_pem=certificate.public_bytes(Encoding.PEM).decode("ascii"),
            hpkp_pin=b64encode(
                get_public_key_sha256(certificate)).decode("ascii"),
            fingerprint_sha1=b64encode(certificate.fingerprint(
                hashes.SHA1())).decode("ascii"),
            fingerprint_sha256=b64encode(
                certificate.fingerprint(hashes.SHA256())).decode("ascii"),
            serial_number=certificate.serial_number,
            not_valid_before=certificate.not_valid_before,
            not_valid_after=certificate.not_valid_after,
            subject_alternative_name=_SubjAltNameAsJson(
                dns=extract_dns_subject_alternative_names(certificate)),
            signature_hash_algorithm=signature_hash_algorithm,
            signature_algorithm_oid=certificate.signature_algorithm_oid,
            subject=subject_field,
            issuer=issuer_field,
            public_key=_PublicKeyAsJson.from_orm(certificate.public_key()),
        )
Ejemplo n.º 29
0
def verify(root: str, signature: str, cert: Certificate):
    """
    Verify signature over root of Merkle Tree
    """
    sig = base64.b64decode(signature)
    pk = cert.public_key()
    assert isinstance(pk, ec.EllipticCurvePublicKey)
    pk.verify(
        sig,
        bytes.fromhex(root),
        ec.ECDSA(utils.Prehashed(hashes.SHA256())),
    )
Ejemplo n.º 30
0
def _write_tls_certificate(certificate: Certificate,
                           full_filepath: str,
                           force: bool = False,
                           ) -> str:
    cert_already_exists = os.path.isfile(full_filepath)
    if force is False and cert_already_exists:
        raise FileExistsError('A TLS certificate already exists at {}.'.format(full_filepath))

    with open(full_filepath, 'wb') as certificate_file:
        public_pem_bytes = certificate.public_bytes(TLS_CERTIFICATE_ENCODING)
        certificate_file.write(public_pem_bytes)
    return full_filepath