Question

I am trying to verify the certificates/signatures of a a PE file with OpenSSL (or actually with Python, but it seems like Python sucks regarding certificate handling).

I have extracted the DER PKCS7 certificate from the PE file as described here: http://blog.didierstevens.com/2008/01/11/the-case-of-the-missing-digital-signatures-tab/

And I have created a modified version of the PE file without the checksum and signature data, like described here: http://www.mail-archive.com/cryptography@c2.net/msg04202.html

The sha1sum of the modified file is the same as the sha1sum in the certificate.

I have tried to verify the unsigned, modified PE file with openssl as such: openssl smime -verify -in signature.der -content modified_executable.exe -inform DER -binary But I only get

Verification failure 140415508248232:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:342:Verify error:unsupported certificate purpose

If i add -noverify to the command I just get

Verification failure 140595583981224:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:1097: 140595583981224:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:410:

What am I missing?

Was it helpful?

Solution

Assumption: the following is done on Cygwin with OpenSSL 0.9.8e

For the "unsupported certificate purpose", the immediate signer may have no S/MIME purpose.

From OpenSSL documentation:
-purpose purpose
    the intended use for the certificate. Without this option no chain verification will be done.
$ openssl x509 -purpose -in goodcert.pem -noout
Certificate purposes:
SSL client : No
SSL client CA : No
SSL server : No
SSL server CA : No
Netscape SSL server : No
Netscape SSL server CA : No
S/MIME signing : No
S/MIME signing CA : No
S/MIME encryption : No
S/MIME encryption CA : No
CRL signing : No
CRL signing CA : No
Any Purpose : Yes
Any Purpose CA : Yes

With reference to this, I added the switch "-purpose any". Then I no longer see "unsupported certificate purpose" but still encounter same digest & signature failures as you.

1900:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:948:
1900:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:312:

With hints from this and lots of research(#1,#2), it turns out the input "modified_exe" to -content was wrong. It should have been the content field in the Sequence ContentInfo of the PKCS #7 SignedData, excluding its DER tag and length bytes.
Refer to Authenticode_PE.docx for the declaration of SignedData.
(simply too much details I don't see fit including!)

Check below for clarity:

openssl asn1parse -inform der -in signature.der > signature.txt
head signature.txt -n30
    0:d=0  hl=4 l=5464 cons: SEQUENCE          
    4:d=1  hl=2 l=   9 prim: OBJECT            :pkcs7-signedData
   15:d=1  hl=4 l=5449 cons: cont [ 0 ]        
   19:d=2  hl=4 l=5445 cons: SEQUENCE          //SignedData
   23:d=3  hl=2 l=   1 prim: INTEGER           :01 //Version
   26:d=3  hl=2 l=  11 cons: SET               //DigestAlgorithmIdentifiers
   28:d=4  hl=2 l=   9 cons: SEQUENCE          
   30:d=5  hl=2 l=   5 prim: OBJECT            :sha1
   37:d=5  hl=2 l=   0 prim: NULL              
   39:d=3  hl=2 l= 104 cons: SEQUENCE          //ContentInfo
   41:d=4  hl=2 l=  10 prim: OBJECT            :1.3.6.1.4.1.311.2.1.4 //ContentType
   53:d=4  hl=2 l=  90 cons: cont [ 0 ]        
   55:d=5  hl=2 l=  88 cons: SEQUENCE          //SpcIndirectDataContent (exclude this tag and length bytes)
   57:d=6  hl=2 l=  51 cons: SEQUENCE          //SpcAttributeTypeAndOptionalValue
   59:d=7  hl=2 l=  10 prim: OBJECT            :1.3.6.1.4.1.311.2.1.15 //ObjectID
   71:d=7  hl=2 l=  37 cons: SEQUENCE          
   73:d=8  hl=2 l=   1 prim: BIT STRING        
   76:d=8  hl=2 l=  32 cons: cont [ 0 ]        
   78:d=9  hl=2 l=  30 cons: cont [ 2 ]        
   80:d=10 hl=2 l=  28 prim: cont [ 0 ]        
  110:d=6  hl=2 l=  33 cons: SEQUENCE          //DigestInfo
  112:d=7  hl=2 l=   9 cons: SEQUENCE          //AlgorithmIdentifier
  114:d=8  hl=2 l=   5 prim: OBJECT            :sha1 //ObjectID
  121:d=8  hl=2 l=   0 prim: NULL              
  123:d=7  hl=2 l=  20 prim: OCTET STRING      [HEX DUMP]:<hash of modified_exe> //digest OCTETSTRING
  145:d=3  hl=4 l=4774 cons: cont [ 0 ]        
  149:d=4  hl=4 l=1332 cons: SEQUENCE          
  153:d=5  hl=4 l= 796 cons: SEQUENCE          
  157:d=6  hl=2 l=   3 cons: cont [ 0 ]        
  159:d=7  hl=2 l=   1 prim: INTEGER           :02

The byte stream from offset 57 to 144 is the correct input to -content!
Exact offset depends on your file.
As a rough guidance, 2 lines before "1.3.6.1.4.1.311.2.1.15" is the "SpcIndirectDataContent", on this line notice that 55+2+88-1=144. the next line start from 57.

final cmd:

openssl smime -verify -inform DER -in signature.der -binary -content signedData -CAfile myCA.crt -purpose any -out tmp

OTHER TIPS

Verification failure 140415508248232:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:342:Verify error:unsupported certificate purpose

As already pointed by @guest, OpenSSL has a "feature" when verifying smime (or cms): it behaves as though -purpose smimesign has been passed. Details on this here.

If your signing certificate is not compatible with OpenSSL's smimesign "purpose" (see man x509, section CERTIFICATE EXTENSIONS for list of "purposes"), then you'd have to disable extensions checking with -purpose any (and check them with other functions if your policy requires that).

If i add -noverify to the command

You probably do not want to. This option disables any checks on the signer's certificate.

And I have created a modified version of the PE file without the checksum and signature data, like described here: http://www.mail-archive.com/cryptography@c2.net/msg04202.html

Why not just use Microsoft's published format? There's no need to resort to reverse engineering.


What am I missing?

A PE/PE+ executable has parts of its file signed, and not the whole file. When digesting data, you have to omit the checksum from the OptionalHeader, omit the certificates table from the Data Directory, and omit the Attribute Certificate Table section.

Here are two references you might want to get familiar with:

The sections to omit are shown in Windows Authenticode Portable Executable Signature Format, page 6. Its reproduced below.

enter image description here


If you need help walking the PE file format programmatically, see Matt Pietrek's An In-Depth Look into the Win32 Portable Executable File Format from MSDN magazine.

the hash of the modified file is the same as the hash in the extracted certificate

My bad. I missed that part.

Here's where you are failing in pk7_smime.c. Its the line i = X509_verify_cert:

i = X509_verify_cert(&cert_ctx);
if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx);
X509_STORE_CTX_cleanup(&cert_ctx);
if (i <= 0) {
    PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR);
    ERR_add_error_data(2, "Verify error:",
        X509_verify_cert_error_string(j));
    sk_X509_free(signers);
    return 0;
}

X509_verify_cert is in <openssl dir>/crypto/x509/x509_vfy.c around line 150. Its a rather large function (about 250 lines), so you're probably going to need to walk through it.

Before you walk the function, you might try adding -signer option to openssl smime function. It could be a simple fix. See the OpenSSL docs on simime(3).

I don't know where to get the signing certificate since its not in front of me and searching for "Microsoft Code Signing CA" is producing too much noise. Dump the end entity cert, find the issuer, and then do a search for the issuer's name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top