pythondigital-signatureportable-executablepyopenssl

can't extract sha256 signature from PE file with pyOpenSSL


I have an executable, that has 2 signatures:

enter image description here

Took the get_certificates function from here:

def get_certificates(self):
from OpenSSL.crypto import _lib, _ffi, X509
"""
https://github.com/pyca/pyopenssl/pull/367/files#r67300900

Returns all certificates for the PKCS7 structure, if present. Only
objects of type ``signedData`` or ``signedAndEnvelopedData`` can embed
certificates.

:return: The certificates in the PKCS7, or :const:`None` if
    there are none.
:rtype: :class:`tuple` of :class:`X509` or :const:`None`
"""
certs = _ffi.NULL
if self.type_is_signed():
    certs = self._pkcs7.d.sign.cert
elif self.type_is_signedAndEnveloped():
    certs = self._pkcs7.d.signed_and_enveloped.cert

pycerts = []
for i in range(_lib.sk_X509_num(certs)):
    pycert = X509.__new__(X509)
    # pycert._x509 = _lib.sk_X509_value(certs, i)
    # According to comment from @ Jari Turkia
    # to prevent segfaults use '_lib.X509_dup('
    pycert._x509 = _lib.X509_dup(_lib.sk_X509_value(certs, i))
    pycerts.append(pycert)

if not pycerts:
    return None
return tuple(pycerts)

I am using the code as follows:

security_directory = pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"]
ds_address = pe_data.OPTIONAL_HEADER.DATA_DIRECTORY[security_directory].VirtualAddress
ds_size = pe_data.OPTIONAL_HEADER.DATA_DIRECTORY[security_directory].Size

if 0 == ds_address:
    return False

digital_signature = file_data[ds_address + 8:]
pkcs = OpenSSL.crypto.load_pkcs7_data(OpenSSL.crypto.FILETYPE_ASN1, bytes(digital_signature))
cert_list = get_certificates(pkcs)

for cert in cert_list:
    cert_dump = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
    cert_data = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_dump)

    algorithm = cert_data.get_signature_algorithm().decode("utf-8")
    serial = cert_data.get_serial_number()
    serial_str = "%x" % serial

    issuer_str = cert_data.get_subject().CN

    print("[INFO]\t\tSerial: [%s] - Algorithm: [%s] Issuer: [%s]" % (serial_str, algorithm, issuer_str))

But only the sha1 chain is extracted (covered sensitive data with *s):

[INFO]  Checking: some_file
[INFO]          Serial: [*******************************] - Algorithm: [sha1WithRSAEncryption] Issuer: [**********************] <- correct
[INFO]          Serial: [*******************************] - Algorithm: [sha1WithRSAEncryption] Issuer: [DigiCert Assured ID Code Signing CA-1]
[INFO]          Serial: [*******************************] - Algorithm: [sha256WithRSAEncryption] Issuer: [DigiCert Trusted G4 RSA4096 SHA256 TimeStamping CA]
[INFO]          Serial: [*******************************] - Algorithm: [sha256WithRSAEncryption] Issuer: [DigiCert Timestamp 2022 - 2]
[INFO]  Checking: another_file
[INFO]          Serial: [*******************************] - Algorithm: [sha1WithRSAEncryption] Issuer: [**********************] <- correct
[INFO]          Serial: [*******************************] - Algorithm: [sha1WithRSAEncryption] Issuer: [DigiCert Assured ID Code Signing CA-1]
[INFO]          Serial: [*******************************] - Algorithm: [sha256WithRSAEncryption] Issuer: [DigiCert Trusted G4 RSA4096 SHA256 TimeStamping CA]
[INFO]          Serial: [*******************************] - Algorithm: [sha256WithRSAEncryption] Issuer: [DigiCert Timestamp 2022 - 2]

My question is: how do I get the other chain? or that is also not implemented in pyOpenSSL?


Solution

  • In the end had to resort to sigcheck with subprocess.check_output(), and use the following python regex with finditer():

    regex_str = r".*\t {3}(?P<signer>.*)\n" \
                r"\t\tCert Status:\s*(?P<status>.*)\n" \
                r"\t\tValid Usage:\s*(?P<valid_usage>.*)\n" \
                r"\t\tCert Issuer:\s*(?P<issuer>.*)\n" \
                r"\t\tSerial Number:\s*(?P<serial>.*)\n" \
                r"\t\tThumbprint:\s*(?P<thumbprint>.*)\n" \
                r"\t\tAlgorithm:\s*(?P<algorithm>.*)\n" \
                r"\t\tValid from:\s*(?P<valid_from>.*)\n" \
                r"\t\tValid to:\s*(?P<valid_to>.*)"