Exemple #1
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)
Exemple #2
0
def certificate_to_context(
        certificate: x509.Certificate) -> Common.Certificate:
    """
    certificate_to_context function
    Translates an X509 certificate into a Common.Certificate object

    :type certificate: ``x509.Certificate``
    :param oid: Certificate Extension OID

    :return: Certificate represented as a Common.Certificate object
    :rtype: ``Common.Certificate``
    """
    spkisha256 = hashes.Hash(hashes.SHA256(), backends.default_backend())
    spkisha256.update(certificate.public_key().public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo))

    extensions_contexts: List[Common.CertificateExtension] = []
    for extension in certificate.extensions:
        extension_oid = cast(oid.ObjectIdentifier, extension.oid)
        extensions_contexts.append(
            extension_context(oid=extension_oid.dotted_string,
                              extension_name=extension_oid._name,
                              critical=extension.critical,
                              extension_value=extension.value))

    indicator = certificate.fingerprint(hashes.SHA256()).hex()
    cert = Common.Certificate(
        subject_dn=certificate.subject.rfc4514_string(),
        issuer_dn=certificate.issuer.rfc4514_string(),
        serial_number=str(certificate.serial_number),
        validity_not_before=certificate.not_valid_before.strftime(
            "%Y-%m-%dT%H:%M:%S.000Z"),
        validity_not_after=certificate.not_valid_after.strftime(
            "%Y-%m-%dT%H:%M:%S.000Z"),
        sha256=certificate.fingerprint(hashes.SHA256()).hex(),
        sha1=certificate.fingerprint(hashes.SHA1()).hex(),
        md5=certificate.fingerprint(hashes.MD5()).hex(),
        spki_sha256=spkisha256.finalize().hex(),
        extensions=extensions_contexts,
        signature_algorithm=certificate.signature_hash_algorithm.
        name,  # type: ignore[union-attr]
        signature=certificate.signature.hex(),
        publickey=public_key_context(certificate.public_key()),  # type: ignore
        dbot_score=Common.DBotScore(indicator=indicator,
                                    indicator_type=DBotScoreType.CERTIFICATE,
                                    integration_name="X509Certificate",
                                    score=Common.DBotScore.NONE),
        pem=certificate.public_bytes(
            serialization.Encoding.PEM).decode('ascii'))

    return cert
Exemple #3
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
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.
            )
    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
Exemple #6
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()),
        )
Exemple #7
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
Exemple #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
Exemple #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
Exemple #10
0
def _get_leaf_cert_info(cert: x509.Certificate):
    output.norm("Certificate Information:")

    output.norm(f"\tSubject: {cert.subject.rfc4514_string()}")
    output.norm(
        f'\tCommon Names: {" ".join(cert_info.get_common_names(cert))}')

    output.norm("\tAlternative names:")
    alt_names = cert_info.get_alt_names(cert)
    for name in alt_names:
        output.norm(f"\t\t{name}")

    output.norm(f'\tNot Before: {cert.not_valid_before.isoformat(" ")}')
    output.norm(f'\tNot After: {cert.not_valid_after.isoformat(" ")}')

    output.norm(f"\tKey: {cert.signature_algorithm_oid._name}")

    # TODO: Public Key Hash

    serial = format(cert.serial_number, "02x")
    output.norm(f"\tSerial: {serial}")

    output.norm(f"\tIssuer: {cert.issuer.rfc4514_string()}")

    output.norm(f"\tOCSP Must Staple: {cert_info.get_must_staple(cert)}")

    output.empty()

    exts = cert_info.format_extensions(cert)
    for ext in exts:
        output.norm(f"\tExtensions: {ext}")

    output.empty()

    scts = cert_info.get_scts(cert)
    for sct in scts:
        output.norm(
            f'\tSCT: {cert_info.get_ct_log_name(sct[1])} - {sct[2].isoformat(" ")}'
        )

    output.empty()

    cert_hash = bytes.hex(cert.fingerprint(hashes.SHA1()))
    output.norm(f"\tFingerprint: {cert_hash}")
    output.norm(f"\t\thttps://censys.io/certificates?q={cert_hash}")
    output.norm(f"\t\thttps://crt.sh/?q={cert_hash}")

    output.empty()
Exemple #11
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
 def from_certificate(cls,
                      certificate: Certificate) -> 'RootCertificateRecord':
     subject_name = CertificateUtils.get_canonical_subject_name(certificate)
     fingerprint = certificate.fingerprint(SHA256())
     return cls(subject_name, fingerprint)