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)
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
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
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()), )
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
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
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
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()
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)