CertificateExtract

Extract fields from a certificate file and return the standard context.

python · Common Scripts

Source

from typing import Any, cast

import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from cryptography import x509

# mypy: ignore-errors
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import asymmetric, hashes, serialization
from cryptography.x509 import certificate_transparency, extensions, general_name, oid

from CommonServerUserPython import *  # noqa # pylint: disable=unused-wildcard-import

_INSTANCE_TO_TYPE = {
    general_name.OtherName: "otherName",
    general_name.RFC822Name: "rfc822Name",
    general_name.DNSName: "dNSName",
    general_name.DirectoryName: "directoryName",
    general_name.UniformResourceIdentifier: "uniformResourceIdentifier",
    general_name.IPAddress: "iPAddress",
    general_name.RegisteredID: "registeredID",
}

_SCT_LOG_ENTRY_TYPE_NAME = {
    certificate_transparency.LogEntryType.PRE_CERTIFICATE: "PreCertificate",
    certificate_transparency.LogEntryType.X509_CERTIFICATE: "X509Certificate",
}


""" STANDALONE FUNCTION """


def get_indicator_from_value(indicator_value: str) -> Any:
    """
    get_indicator_from_value function
    Finds an indicator in XSOAR store given a value

    :type value: ``str``
    :param value: Indicator value

    :return: Indicator
    :rtype: ``Any``
    """
    try:
        res = demisto.executeCommand("findIndicators", {"query": f'value:"{indicator_value}" and type:Certificate'})
        indicator = res[0]["Contents"][0]

        return indicator
    except BaseException:
        return None


def oid_name(oid: oid.ObjectIdentifier) -> str:
    """
    oid_name function
    Translates an oid.ObjectIdentifier into a string representation

    :type oid: ``oid.ObjectIdentifier``
    :param oid: OID as oid.ObjectIdentifier

    :return: OID in dotted string format
    :rtype: ``str``
    """
    n = oid._name
    if n.startswith("Unknown"):
        return oid.dotted_string

    return n


def repr_or_str(o: Any) -> str:
    """
    repr_or_str function
    Returns a string representation of the input:
    - If input is bytes returns the hex representation
    - If input is str returns the string
    - If input is None returns empty string

    :type o: ``Any``
    :param o: Input data (str or bytes)

    :return: String representation of the input
    :rtype: ``str``
    """
    if isinstance(o, str):
        return o
    elif isinstance(o, bytes):
        return o.hex()
    elif o is None:
        return ""

    return repr(o)


def load_certificate(path: str) -> x509.Certificate:
    """
    load_certificate function
    Loads a certificate from a file

    :type path: ``str``
    :param path: File path

    :return: X509 Certificate parsed by cryptography.x509
    :rtype: ``x509.Certificate``
    """
    with open(path, "rb") as f:
        contents = f.read()

    try:
        certificate = x509.load_pem_x509_certificate(contents, backends.default_backend())

    except Exception as e:
        demisto.debug(f"Error loading certificate as PEM, trying with DER. Error was: {e!r}")
        certificate = x509.load_der_x509_certificate(contents, backends.default_backend())

    return certificate


def int_to_comma_hex(n: int, blength: int | None = None) -> str:
    """
    int_to_comma_hex
    Translates an integer in its corresponding hex string

    :type n: ``int``
    :param n: Input integer

    :type blength: ``Optional[int]``
    :param blength: Add padding to reach length

    :return: Translated hex string
    :rtype: ``str``
    """
    bhex = f"{n:x}"
    if len(bhex) % 2 == 1:
        bhex = "0" + bhex

    if blength is not None:
        bhex = "00" * max(blength - len(bhex), 0) + bhex

    return ":".join([bhex[i : i + 2] for i in range(0, len(bhex), 2)])


def public_key_context(
    pkey: asymmetric.dsa.DSAPublicKey
    | asymmetric.rsa.RSAPublicKey
    | asymmetric.ec.EllipticCurvePublicKey
    | asymmetric.ed25519.Ed25519PublicKey
    | asymmetric.ed448.Ed448PublicKey,
) -> Common.CertificatePublicKey:
    """
    public_key_context function
    Translates an X509 certificate Public Key into a Common.CertificatePublicKey object

    :type pkey: ``Union[asymmetric.dsa.DSAPublicKey, asymmetric.rsa.RSAPublicKey, asymmetric.ec.EllipticCurvePublicKey, \
         asymmetric.ed25519.Ed25519PublicKey, asymmetric.ed448.Ed448PublicKey]``
    :param pkey: Certificate Public Key

    :return: Certificate Public Key represented as a Common.CertificatePublicKey object
    :rtype: ``Common.CertificatePublicKey``
    """
    if isinstance(pkey, asymmetric.dsa.DSAPublicKey):
        return Common.CertificatePublicKey(
            algorithm=Common.CertificatePublicKey.Algorithm.DSA,
            length=pkey.key_size,
            publickey=int_to_comma_hex(pkey.public_numbers().y),
            p=int_to_comma_hex(pkey.public_numbers().parameter_numbers.p),
            q=int_to_comma_hex(pkey.public_numbers().parameter_numbers.q),
            g=int_to_comma_hex(pkey.public_numbers().parameter_numbers.g),
        )

    elif isinstance(pkey, asymmetric.rsa.RSAPublicKey):
        return Common.CertificatePublicKey(
            algorithm=Common.CertificatePublicKey.Algorithm.RSA,
            length=pkey.key_size,
            modulus=int_to_comma_hex(pkey.public_numbers().n),
            exponent=pkey.public_numbers().e,
        )

    elif isinstance(pkey, asymmetric.ec.EllipticCurvePublicKey):
        return Common.CertificatePublicKey(
            algorithm=Common.CertificatePublicKey.Algorithm.EC,
            length=pkey.key_size,
            x=int_to_comma_hex(pkey.public_numbers().x),
            y=int_to_comma_hex(pkey.public_numbers().y),
            curve=pkey.curve.name,
        )

    return Common.CertificatePublicKey(
        algorithm=Common.CertificatePublicKey.Algorithm.UNKNOWN,
        length=0,
        publickey=pkey.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw).hex(),
    )


def map_gn(gn: Any) -> Common.GeneralName:
    """
    map_gn function
    Check whether the provided General Name is a compatible type and maps it to a Common.GeneralName class

    :type gn: ``Any``
    :param gn: General name to be checked

    :return: General Name mapped to a Common.GeneralName class
    :rtype: ``Common.GeneralName``
    """
    if gn is None:
        raise ValueError("gn cannot be None")

    itype = next((t for t in _INSTANCE_TO_TYPE if isinstance(gn, t)), None)
    if itype is not None:
        return Common.GeneralName(gn_type=_INSTANCE_TO_TYPE[itype], gn_value=str(gn.value))
    raise ValueError("general name must be a valid type")


def extension_context(oid: str, extension_name: str, critical: bool, extension_value: Any) -> Common.CertificateExtension:
    """
    extension_context function
    Translates an X509 certificate extension into a Common.CertificateExtension object

    :type oid: ``str``
    :param oid: Certificate Extension OID

    :type extension_name: ``str``
    :param extension_name: Name of the Extension

    :type critical: ``bool``
    :param critical: Whether the Extension is marked as critical

    :type extension_value: ``Any``
    :param extension_value: Value of the extension (parsed from cryptograph module)

    :return: Extension represented as a Common.CertificateExtension object
    :rtype: ``Common.CertificateExtension``
    """
    if isinstance(extension_value, extensions.SubjectAlternativeName):
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.SUBJECTALTERNATIVENAME,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            subject_alternative_names=[
                Common.CertificateExtension.SubjectAlternativeName(gn=map_gn(gn))
                for gn in extension_value._general_names
                if gn is not None
            ],
        )
    elif isinstance(extension_value, extensions.AuthorityKeyIdentifier):
        authority_key_identifier = Common.CertificateExtension.AuthorityKeyIdentifier(
            issuer=[map_gn(n) for n in list(extension_value.authority_cert_issuer)]
            if (extension_value.authority_cert_issuer)
            else None,
            serial_number=extension_value.authority_cert_serial_number,  # type: ignore
            key_identifier=extension_value.key_identifier.hex(),  # type: ignore
        )
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.AUTHORITYKEYIDENTIFIER,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            authority_key_identifier=authority_key_identifier,
        )
    elif isinstance(extension_value, extensions.SubjectKeyIdentifier):
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.SUBJECTKEYIDENTIFIER,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            digest=extension_value.digest.hex(),
        )
    elif isinstance(extension_value, extensions.KeyUsage):
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.KEYUSAGE,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            digital_signature=extension_value.digital_signature,
            content_commitment=extension_value.content_commitment,
            key_encipherment=extension_value.key_encipherment,
            data_encipherment=extension_value.data_encipherment,
            key_agreement=extension_value.key_agreement,
            key_cert_sign=extension_value.key_cert_sign,
            crl_sign=extension_value.crl_sign,
        )
    elif isinstance(extension_value, extensions.ExtendedKeyUsage):
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.EXTENDEDKEYUSAGE,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            usages=[oid_name(o) for o in extension_value],
        )
    elif isinstance(extension_value, extensions.CRLDistributionPoints):
        distribution_points: list[Common.CertificateExtension.DistributionPoint] = []
        dp: extensions.DistributionPoint
        for dp in extension_value:
            distribution_points.append(
                Common.CertificateExtension.DistributionPoint(
                    full_name=None if dp.full_name is None else [map_gn(fn) for fn in list(dp.full_name)],
                    relative_name=None if dp.relative_name is None else dp.relative_name.rfc4514_string(),
                    crl_issuer=None if dp.crl_issuer is None else [map_gn(ci) for ci in list(dp.crl_issuer)],
                    reasons=None if dp.reasons is None else [repr_or_str(r) for r in dp.reasons],
                )
            )
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.CRLDISTRIBUTIONPOINTS,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            distribution_points=distribution_points,
        )
    elif isinstance(extension_value, extensions.CertificatePolicies):
        policies: list[Common.CertificateExtension.CertificatePolicy] = []
        p: extensions.PolicyInformation
        for p in extension_value:
            policies.append(
                Common.CertificateExtension.CertificatePolicy(
                    policy_identifier=oid_name(p.policy_identifier),
                    policy_qualifiers=None if not p.policy_qualifiers else [repr_or_str(pq) for pq in p.policy_qualifiers],
                )
            )
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.CERTIFICATEPOLICIES,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            certificate_policies=policies,
        )
    elif isinstance(extension_value, extensions.AuthorityInformationAccess):
        descriptions: list[Common.CertificateExtension.AuthorityInformationAccess] = []
        d: extensions.AccessDescription
        for d in extension_value:
            descriptions.append(
                Common.CertificateExtension.AuthorityInformationAccess(
                    access_method=oid_name(d.access_method), access_location=map_gn(d.access_location)
                )
            )

        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.AUTHORITYINFORMATIONACCESS,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            authority_information_access=descriptions,
        )
    elif isinstance(extension_value, extensions.BasicConstraints):
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.BASICCONSTRAINTS,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            basic_constraints=Common.CertificateExtension.BasicConstraints(
                ca=extension_value.ca, path_length=None if extension_value.path_length is None else extension_value.path_length
            ),
        )
    elif isinstance(extension_value, extensions.PrecertificateSignedCertificateTimestamps):
        presigcerttimestamps: list[Common.CertificateExtension.SignedCertificateTimestamp] = []
        presct: extensions.SignedCertificateTimestamp
        for presct in extension_value:
            presigcerttimestamps.append(
                Common.CertificateExtension.SignedCertificateTimestamp(
                    entry_type=_SCT_LOG_ENTRY_TYPE_NAME.get(presct.entry_type, presct.entry_type.value),  # type: ignore
                    version=presct.version.value,
                    log_id=presct.log_id.hex(),
                    timestamp=presct.timestamp.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                )
            )
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.PRESIGNEDCERTIFICATETIMESTAMPS,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            signed_certificate_timestamps=presigcerttimestamps,
        )
    elif isinstance(extension_value, extensions.SignedCertificateTimestamps):
        sigcerttimestamps: list[Common.CertificateExtension.SignedCertificateTimestamp] = []
        sct: extensions.SignedCertificateTimestamp
        for sct in extension_value:
            sigcerttimestamps.append(
                Common.CertificateExtension.SignedCertificateTimestamp(
                    entry_type=_SCT_LOG_ENTRY_TYPE_NAME.get(sct.entry_type, sct.entry_type.value),  # type: ignore
                    version=sct.version.value,
                    log_id=sct.log_id.hex(),
                    timestamp=sct.timestamp.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                )
            )
        return Common.CertificateExtension(
            extension_type=Common.CertificateExtension.ExtensionType.SIGNEDCERTIFICATETIMESTAMPS,
            oid=oid,
            extension_name=extension_name,
            critical=critical,
            signed_certificate_timestamps=sigcerttimestamps,
        )

    return Common.CertificateExtension(
        extension_type=Common.CertificateExtension.ExtensionType.OTHER,
        oid=oid,
        extension_name=extension_name,
        critical=critical,
        value=repr(extension_value),
    )


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


""" COMMAND FUNCTION """


def certificate_extract_command(args: dict[str, Any]) -> CommandResults:
    pem: str | None = args.get("pem")
    entry_id: str | None = args.get("entry_id")

    if pem is None and entry_id is None:
        raise ValueError("You should specify pem or entry_id")

    if pem is not None and entry_id is not None:
        raise ValueError("Only one of pem and entry_id should be specified")

    certificate: x509.Certificate
    if entry_id is not None:
        res_path = demisto.getFilePath(entry_id)
        if not res_path:
            raise ValueError("Invalid entry_id - not found")

        entry_id_path = res_path["path"]

        certificate = load_certificate(entry_id_path)

    if pem is not None:
        certificate = x509.load_pem_x509_certificate(pem.encode("ascii"), backends.default_backend())

    standard_context = certificate_to_context(certificate)
    readable_output = "Certificate decoded"

    return CommandResults(readable_output=readable_output, outputs=None, indicator=standard_context, ignore_auto_extract=True)


""" MAIN FUNCTION """


def main():
    try:
        return_results(certificate_extract_command(demisto.args()))
    except Exception as ex:
        return_error(f"Failed to execute CertificateExtract. Error: {ex!s}")


""" ENTRY POINT """


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Extract fields from a certificate file and return the standard context

Script Data


Name Description
Script Type python3
Tags  
Cortex XSOAR Version 6.0.0

Inputs


Argument Name Description
pem Certificate in PEM format
entry_id Certificate entry ID (in DER or PEM format)

Outputs


Path Description Type
Certificate.Name Name (CN or SAN) appearing in the certificate. String
Certificate.SubjectDN The Subject Distinguished Name of the certificate.
This field includes the Common Name of the certificate.
String
Certificate.PEM Certificate in PEM format. String
Certificate.IssuerDN The Issuer Distinguished Name of the certificate. String
Certificate.SerialNumber The Serial Number of the certificate. String
Certificate.ValidityNotAfter End of certificate validity period. Date
Certificate.ValidityNotBefore Start of certificate validity period. Date
Certificate.SubjectAlternativeName.Type Type of the SAN. String
Certificate.SubjectAlternativeName.Value Name of the SAN. String
Certificate.SHA512 SHA512 Fingerprint of the certificate in DER format. String
Certificate.SHA256 SHA256 Fingerprint of the certificate in DER format. String
Certificate.SHA1 SHA1 Fingerprint of the certificate in DER format. String
Certificate.MD5 MD5 Fingerprint of the certificate in DER format. String
Certificate.PublicKey.Algorithm Algorithm used for public key of the certificate. String
Certificate.PublicKey.Length Length in bits of the public key of the certificate. Number
Certificate.PublicKey.Modulus Modulus of the public key for RSA keys. String
Certificate.PublicKey.Exponent Exponent of the public key for RSA keys. Number
Certificate.PublicKey.PublicKey The public key for DSA/Unknown keys. String
Certificate.PublicKey.P The P parameter for DSA keys. String
Certificate.PublicKey.Q The Q parameter for DSA keys. String
Certificate.PublicKey.G The G parameter for DSA keys. String
Certificate.PublicKey.X The X parameter for EC keys. String
Certificate.PublicKey.Y The Y parameter for EC keys. String
Certificate.PublicKey.Curve Curve of the Public Key for EC keys. String
Certificate.SPKISHA256 SHA256 fingerprint of the certificate Subject Public Key Info. String
Certificate.Signature.Algorithm Algorithm used in the signature of the certificate. String
Certificate.Signature.Signature Signature of the certificate. String
Certificate.Extension.Critical Critical flag of the certificate extension. Bool
Certificate.Extension.OID OID of the certificate extension. String
Certificate.Extension.Name Name of the certificate extension. String
Certificate.Extension.Value Value of the certificate extension. Unknown
Certificate.Malicious.Vendor The vendor that reported the file as malicious. String
Certificate.Malicious.Description A description explaining why the file was determined to be malicious. String
DBotScore.Indicator The indicator that was tested. String
DBotScore.Type The indicator type. String
DBotScore.Vendor The vendor used to calculate the score. String
DBotScore.Score The actual score. Number

Script Example

!CertificateExtract entry_id="978@5b925e3c-6ab8-4209-86bf-10f4ed6a9dc0"

Context Example

{
    "Certificate": {
        "Extension": [
            {
                "Critical": true,
                "Name": "basicConstraints",
                "OID": "2.5.29.19",
                "Value": {
                    "CA": false
                }
            },
            {
                "Critical": false,
                "Name": "subjectKeyIdentifier",
                "OID": "2.5.29.14",
                "Value": {
                    "Digest": "3fe12ea63b9cf8414f0b001f2efa8ac5bd0fc73c"
                }
            },
            {
                "Critical": false,
                "Name": "keyUsage",
                "OID": "2.5.29.15",
                "Value": {
                    "DataEncipherment": true,
                    "DigitalSignature": true,
                    "KeyAgreement": true,
                    "KeyEncipherment": true
                }
            },
            {
                "Critical": false,
                "Name": "extendedKeyUsage",
                "OID": "2.5.29.37",
                "Value": {
                    "Usages": [
                        "clientAuth"
                    ]
                }
            },
            {
                "Critical": false,
                "Name": "subjectAltName",
                "OID": "2.5.29.17",
                "Value": [
                    {
                        "Type": "dNSName",
                        "Value": "*.example.com"
                    }
                ]
            }
        ],
        "IssuerDN": "CN=TestCA,OU=CA,O=Example.com,L=Casacca,ST=PR,C=IT",
        "MD5": "ac2fead0b8ac25a3633194b38ad91ca9",
        "Name": [
            "*.example.com",
            "IAmADVCertificate"
        ],
        "PEM": "-----BEGIN CERTIFICATE-----\nMIIDzjCCAbagAwIBAgIIXAVHw/CcpwowDQYJKoZIhvcNAQELBQAwYDELMAkGA1UE\nBhMCSVQxCzAJBgNVBAgTAlBSMRAwDgYDVQQHEwdDYXNhY2NhMRQwEgYDVQQKEwtF\neGFtcGxlLmNvbTELMAkGA1UECxMCQ0ExDzANBgNVBAMTBlRlc3RDQTAeFw0yNjEy\nMDIxNDQxMDBaFw0yMTEyMDIxNDQxMDBaMBwxGjAYBgNVBAMTEUlBbUFEVkNlcnRp\nZmljYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfWq17YxZZuf/jPU/to3HRumn\nv8/LgZQSwkKUNdXs6niQYplCHTGQW1g062EtrhDhrWXYDIR1wxxt139y+z3hs9KP\ngThaIkJNjFx8dVklvIWx16c6t4cujZSVr6/08TLOo34wfDAMBgNVHRMBAf8EAjAA\nMB0GA1UdDgQWBBQ/4S6mO5z4QU8LAB8u+orFvQ/HPDALBgNVHQ8EBAMCA7gwEwYD\nVR0lBAwwCgYIKwYBBQUHAwIwGAYDVR0RBBEwD4INKi5leGFtcGxlLmNvbTARBglg\nhkgBhvhCAQEEBAMCBaAwDQYJKoZIhvcNAQELBQADggIBAAEhwtW+srmNaTs1krrt\nxybcF23jDNzRv/2G91PHf0CxFq7BjHYYzF3CvvwaQfTXMKog6j5arenuKgLEPpO9\navXiAcPt7vy3bxqX8WP4xgjoaRcEglS0cwW7bc+wtQT2NzqCsiXqNeRSiHXanNLY\nBGOayOnulnWP9vuAHNhgTDgxvMphxNDYtY9MA1Qj+Bs0jIuRHdFWBTVXvPYaMr4/\nbtzhrJY7VeS47IXDyp9KZbczy+5LUSh7AdKjXOI/kG8MUlKvfw/Vy23rDIOwb/dM\n/PWnqkbTFZI6cBl5FxWh2i/MaYUvjaynS/6A1fZIsgnq9mcCO/Auzaw8pa4Zx55w\nnf5hHBtp/Ksc+0ayPgYmfYKNGYvu/DQW3gvMAigYYaLAIrykYbkDl7mmQgL9lBIT\nBpBN72+ho1oZWb7F8YrbPa4odu2PBxO8kuleQKMfW73rQVF9BWUoxk8oDdHFkeye\nA+saBni5EZyYyLDMJRB2R9h/6mC02JkCRceHf7STNxiTvHwew8eP7mTNfmyTJDND\nEsGgwQpK6w0ki3/kQ3pD8rFUtpnA3AqjB0BON6j+lSpyPx/2HvGSvSx3aT2B6apC\nnoZV/cVfp9txXt1CkRt3zLpdzbQWTrRUiL+L2EaLeHPd901NZG0voxVUP64TuouB\nSIzBSFl5/q4tqkY4D2Gv4gPX\n-----END CERTIFICATE-----\n",
        "PublicKey": {
            "Algorithm": "EC",
            "Curve": "secp384r1",
            "Length": 384,
            "X": "7d:6a:b5:ed:8c:59:66:e7:ff:8c:f5:3f:b6:8d:c7:46:e9:a7:bf:cf:cb:81:94:12:c2:42:94:35:d5:ec:ea:78:90:62:99:42:1d:31:90:5b:58:34:eb:61:2d:ae:10:e1",
            "Y": "ad:65:d8:0c:84:75:c3:1c:6d:d7:7f:72:fb:3d:e1:b3:d2:8f:81:38:5a:22:42:4d:8c:5c:7c:75:59:25:bc:85:b1:d7:a7:3a:b7:87:2e:8d:94:95:af:af:f4:f1:32:ce"
        },
        "SHA1": "18ac1daece43f5b769260adbabf763046ebda80e",
        "SHA256": "869c151a7174f95a35b58c7aa900da5daed3723b63a31f08b89edd788653e402",
        "SPKISHA256": "f2d670d5c0e7e3ba2fb928dc2533c0191a679170acd7dc9387130173492e4b18",
        "SerialNumber": "6630784933253916426",
        "Signature": {
            "Algorithm": "sha256",
            "Signature": "0121c2d5beb2b98d693b3592baedc726dc176de30cdcd1bffd86f753c77f40b116aec18c7618cc5dc2befc1a41f4d730aa20ea3e5aade9ee2a02c43e93bd6af5e201c3edeefcb76f1a97f163f8c608e86917048254b47305bb6dcfb0b504f6373a82b225ea35e4528875da9cd2d804639ac8e9ee96758ff6fb801cd8604c3831bcca61c4d0d8b58f4c035423f81b348c8b911dd156053557bcf61a32be3f6edce1ac963b55e4b8ec85c3ca9f4a65b733cbee4b51287b01d2a35ce23f906f0c5252af7f0fd5cb6deb0c83b06ff74cfcf5a7aa46d315923a7019791715a1da2fcc69852f8daca74bfe80d5f648b209eaf667023bf02ecdac3ca5ae19c79e709dfe611c1b69fcab1cfb46b23e06267d828d198beefc3416de0bcc02281861a2c022bca461b90397b9a64202fd94121306904def6fa1a35a1959bec5f18adb3dae2876ed8f0713bc92e95e40a31f5bbdeb41517d056528c64f280dd1c591ec9e03eb1a0678b9119c98c8b0cc25107647d87fea60b4d8990245c7877fb493371893bc7c1ec3c78fee64cd7e6c9324334312c1a0c10a4aeb0d248b7fe4437a43f2b154b699c0dc0aa307404e37a8fe952a723f1ff61ef192bd2c77693d81e9aa429e8655fdc55fa7db715edd42911b77ccba5dcdb4164eb45488bf8bd8468b7873ddf74d4d646d2fa315543fae13ba8b81488cc1485979feae2daa46380f61afe203d7"
        },
        "SubjectAlternativeName": [
            {
                "Type": "dNSName",
                "Value": "*.example.com"
            }
        ],
        "SubjectDN": "CN=IAmADVCertificate",
        "ValidityNotAfter": "2021-12-02T14:41:00.000Z",
        "ValidityNotBefore": "2026-12-02T14:41:00.000Z"
    },
    "DBotScore": {
        "Indicator": "869c151a7174f95a35b58c7aa900da5daed3723b63a31f08b89edd788653e402",
        "Score": 0,
        "Type": "certificate",
        "Vendor": "X509Certificate"
    }
}

Human Readable Output

Certificate decoded