I have an executable, that has 2 signatures:
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?
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>.*)"