synner
synner

Reputation: 31

How to identify end-entity certificate in chain/bundle with Python Cryptography?

I am using Python module pefile to extract the Authenticode from a Windows PE file.

pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']])

vAddr = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']].VirtualAddress

signature = pe.write()[vAddr + 8:]

try:
    certChain = cryptography.hazmat.primitives.serialization.pkcs7.load_der_pkcs7_certificates(bytes(signature))
except ValueError as e:
    print(type(e).__name__ + ": " + str(e) + "\n", file=sys.stderr)
    return

Now, certChain contains a List of cryptography.x509.Certificate objects. But how do I identify which Certificate belongs to the end-entity??? Sometimes it's the first or last one, sometimes it's in the middle. I can print them and manually examine their Subject / Issuer attributes / etc. but how do I programmatically identify it?

==>> "/cygdrive/d/Install/GPU-Z.2.45.0.exe" <<==

Subject         : <Name(C=US,O=DigiCert\, Inc.,CN=DigiCert Timestamp 2021)>
Issuer          : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert SHA2 Assured ID Timestamping CA)>
S/N             : 0xd424ae0be3a88ff604021ce1400f0dd
Public Key      : RSA (2048 bytes)
SHA1 Fingerprint: e1:d7:82:a8:e1:91:be:ef:6b:ca:16:91:b5:aa:b4:94:a6:24:9b:f3
Not valid before: 2021-01-01 00:00:00
Not valid after : 2031-01-06 00:00:00
SignHashAlgo    : SHA256

Subject         : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert SHA2 Assured ID Timestamping CA)>
Issuer          : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert Assured ID Root CA)>
S/N             : 0xaa125d6d6321b7e41e405da3697c215
Public Key      : RSA (2048 bytes)
SHA1 Fingerprint: 3b:a6:3a:6e:48:41:35:57:72:de:be:f9:cd:cf:4d:5a:f3:53:a2:97
Not valid before: 2016-01-07 12:00:00
Not valid after : 2031-01-07 12:00:00
SignHashAlgo    : SHA256

Subject         : <Name(1.3.6.1.4.1.311.60.2.1.3=US,1.3.6.1.4.1.311.60.2.1.2=Washington,2.5.4.15=Private Organization,2.5.4.5=604 057 982,C=US,ST=Washington,L=Spokane,O=TechPowerUp LLC,CN=TechPowerUp LLC)>
Issuer          : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert EV Code Signing CA (SHA2))>
S/N             : 0xd7e33145979e788f23fbaff5a3fa9f9
Public Key      : RSA (2048 bytes)
SHA1 Fingerprint: bf:cf:d0:f8:f0:92:12:8f:ac:8b:c9:92:26:55:3d:08:77:2b:fb:07
Not valid before: 2019-07-22 00:00:00
Not valid after : 2022-06-15 12:00:00
SignHashAlgo    : SHA256
subjectAltName  : []

Subject         : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert EV Code Signing CA (SHA2))>
Issuer          : <Name(C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert High Assurance EV Root CA)>
S/N             : 0x3f1b4e15f3a82f1149678b3d7d8475c
Public Key      : RSA (2048 bytes)
SHA1 Fingerprint: 60:ee:3f:c5:3d:4b:df:d1:69:7a:e5:be:ae:1c:ab:1c:0f:3a:d4:e3
Not valid before: 2012-04-18 12:00:00
Not valid after : 2027-04-18 12:00:00
SignHashAlgo    : SHA256

Upvotes: 0

Views: 288

Answers (1)

synner
synner

Reputation: 31

This is the best version I came up so far. Certificate object is of type cryptography.x509.Certificate.

def findEndEntitiyCert(certList: typing.List[Certificate]) -> Certificate:
    endEntityCert = []
    for cert in certList:
        try:
            cert_BasicConstr = cert.extensions.get_extension_for_oid(oid.\
                ExtensionOID.BASIC_CONSTRAINTS)
            if cert_BasicConstr.value.ca:
                # End-Entity certificate cannot be Certificate Authority!
                continue

            cert_KeyUsage = cert.extensions.get_extension_for_oid(oid.\
                ExtensionOID.KEY_USAGE)
            if not hasattr(cert_KeyUsage.value, "digital_signature"):
                # End-Entity certificate must be able to sign code.
                continue

            cert_ExtKeyUsage = cert.extensions.get_extension_for_oid(oid.\
                ExtensionOID.EXTENDED_KEY_USAGE)
            extUsages = {}
            for usage in cert_ExtKeyUsage.value:
                #print(type(usage._name), usage._name)
                #print(type(usage.dotted_string), usage.dotted_string)
                extUsages.update({usage._name: usage.dotted_string})
            if "timeStamping" in extUsages:
                # End-Entity certificate cannot be used for RFC 3161.
                continue

            endEntityCert.append(cert)
        except ExtensionNotFound:
            continue
    assert len(endEntityCert) == 1, "Found more than one End-Entity Certificate!"
    return endEntityCert[0]

Upvotes: 1

Related Questions