Zohar81
Zohar81

Reputation: 5074

Understand why pkcs7 block failed during verification using openssl

I've got PKCS#7 Der formatted file called p7 and an x509 certificate file called mroot.der.cer which matches the root certificate of p7 chain.

I'd like to verify my p7 certificate chain using openssl using the following commands :

First - convert my mroot trusted cert file to pem format. 
openssl x509 -in mroot.der.cer -inform der -outform PEM -out mroot.pem.cer

Second - verify the root chain using mroot.pem.cer
openssl  smime -verify -CAfile mroot.pem.cer -in p7 -inform DER -out blabla

However, I got the following error :

Verification failure 140735569544136:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_smime.c:343:Verify error:unable to get local issuer certificate

I also tried to add the last command, the -noverify flag but got a different error.

Verification failure 140735569544136:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_doit.c:1084: 140735569544136:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_smime.c:412:

The pkcs7 structure should be fine as I've extracted it from the PE file iexlorer.exe and from its chain I've extracted the root certificate, and refer to it as a trusted one.

What am I doing wrong here ?

P.s. To observe the same failures I did, I've uploaded the files to the following links :

https://ufile.io/vrqpt

https://ufile.io/ajgex

Upvotes: 1

Views: 5797

Answers (1)

Reinier Torenbeek
Reinier Torenbeek

Reputation: 17383

Your uploaded example files have a few properties which prevent it from being verified.

First, the certificate of the signer in the p7 file has expired on Apr 24 22:33:39 2014 GMT. You will have to disable checking of the expiration date if you want to verify the chain. This is programmatically done with the verify flag X509_V_FLAG_NO_CHECK_TIME, or the option -no_check_time for the OpenSSL smime -verify tool.

Then, your "root of trust", found in the mroot.pem.cer file is not the right one. You extracted the Microsoft Time-Stamp PCA certificate whereas the signer of the p7 file chains up to the Microsoft Code Signing PCA certificate.

Let's say that you extract that correct certificate to a file called trust.pem.cer. That certificate is not self-signed: its issuer is the Microsoft Root Certificate Authority. You will have to indicate that you are using a so-called partial chain if you want such a certificate to be at the end of the chain. This is programmatically done with the verify flag X509_V_FLAG_PARTIAL_CHAIN, or the option -partial_chain for the OpenSSL smime -verify tool.

Also, it looks like the OpenSSL implementation of the PKCS7 verification requires your certificate to include the extended key usage of S/MIME signing, which your certificate does not include. It looks like this can be worked around by setting a code-signing purpose for the OpenSSL X509_STORE. The OpenSSL smime -verify tool does not expose this kind of setting so you will have to do this programmatically by setting the XKU_CODE_SIGN purpose. XKU stands for eXtended Key Usage and, as a caveat, the OpenSSL documentation around that is virtually non-existent. You have to thoroughly test this if you decide to use it.

The piece of code below (return code checking omitted) successfully verifies the certificate chain in your p7 file, but not the signature:

BIO *bio_p7 = BIO_new_file("p7", "r");
PKCS7 *p7 = d2i_PKCS7_bio(bio_p7, NULL);
X509_STORE *store = X509_STORE_new();
X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
X509_LOOKUP_load_file(lookup, "trust.pem.cer", X509_FILETYPE_PEM);
X509_STORE_set_purpose(store, XKU_CODE_SIGN); /* see caveat above */
X509_VERIFY_PARAM_set_flags(
    X509_STORE_get0_param(store),
    X509_V_FLAG_NO_CHECK_TIME | X509_V_FLAG_PARTIAL_CHAIN);
int retcode = PKCS7_verify(p7, NULL, store, NULL, NULL, PKCS7_NOSIGS);

For a verification of the signature itself: the function verify_pe_pkcs7() in osslsigncode.c gives example code to do that. Its PKCS7_verify() invocation does not verify the certificate chain, but it does check the signature. This requires extraction of a hash which is stored in a Microsoft-specific element in the p7 of a type called SpcIndirectDataContent, as pointed out by @dave_thompson_085 below. It is possible to verify the signature that was taken over that hash. For a complete verification, you will also need to re-calculate the hash over the PE file itself and compare it to the hash value found in the p7.

This answer is based on OpenSSL 1.1.1. Just now, I realize that you are using libressl, which is based on a (much) older version of OpenSSL. It might not work in your case. For example, for my version of libressl, the smime -verify tool does not support the partial_chain and no_time_check options because those were introduced in the OpenSSL 1.1.0 branch.

Upvotes: 4

Related Questions