理解为什么pkcs7 block在使用openssl验证时失败

Understand why pkcs7 block failed during verification using openssl

我有一个名为 p7 的 PKCS#7 Der 格式文件 和一个名为 mroot.der.cer 的 x509 证书文件,它与 p7 链的根证书相匹配。

我想使用以下命令使用 openssl 验证我的 p7 证书链:

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

但是,我收到以下错误:

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

我还尝试添加最后一个命令,即 -noverify 标志,但出现了不同的错误。

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:

pkcs7 结构应该没问题,因为我已经从 PE 文件 iexlorer.exe 中提取了它,并从它的链中提取了根证书,并将其称为受信任的证书。

我做错了什么?

P.s。 为了观察我所做的同样的失败,我将文件上传到以下链接:

https://ufile.io/vrqpt

https://ufile.io/ajgex

您上传的示例文件有一些属性导致无法通过验证。

首先,p7 文件中签名者的证书已于 Apr 24 22:33:39 2014 GMT 过期。如果要验证链,则必须禁用过期日期检查。这是通过验证标志 X509_V_FLAG_NO_CHECK_TIME 或 OpenSSL smime -verify 工具的选项 -no_check_time 以编程方式完成的。

那么,您在 mroot.pem.cer 文件中找到的 "root of trust" 不是正确的。您提取了 Microsoft Time-Stamp PCA 证书,而 p7 文件的签名者链接到 Microsoft Code Signing PCA 证书。

假设您将该正确的证书提取到名为 trust.pem.cer 的文件中。该证书不是自签名的:其颁发者是 Microsoft Root Certificate Authority。如果您希望这样的证书位于链的末端,则必须表明您正在使用所谓的部分链。这是通过验证标志 X509_V_FLAG_PARTIAL_CHAIN 或 OpenSSL smime -verify 工具的选项 -partial_chain 以编程方式完成的。

此外,PKCS7 验证的 OpenSSL 实现似乎要求您的证书包含 S/MIME 签名的扩展密钥用法,而您的证书不包含。看起来这可以通过为 OpenSSL X509_STORE 设置代码签名目的来解决。 OpenSSL smime -verify 工具不会公开此类设置,因此您必须通过设置 XKU_CODE_SIGN 目的以编程方式执行此操作。 XKU 代表 eXtended Key Usage,需要注意的是,关于它的 OpenSSL 文档几乎不存在。如果你决定使用它,你必须彻底测试它。

下面的代码(return 省略了代码检查)成功验证了 p7 文件中的证书链,但未验证签名:

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);

为了验证签名本身:函数 verify_pe_pkcs7() in osslsigncode.c 给出了执行此操作的示例代码。它的 PKCS7_verify() 调用不会验证证书链,但会检查签名。这需要提取存储在 p7 中名为 SpcIndirectDataContent 类型的 Microsoft 特定元素中的哈希,如下面的 @dave_thompson_085 所指出的。可以验证接管该散列的签名。为了进行完整验证,您还需要重新计算 PE 文件本身的哈希值,并将其与 p7.hash 文件中的哈希值进行比较。

此答案基于 OpenSSL 1.1.1。刚才,我意识到您正在使用 libressl,它基于(很多)旧版本的 OpenSSL。它可能不适用于您的情况。例如,对于我的 libressl 版本,smime -verify 工具不支持 partial_chainno_time_check 选项,因为它们是在 OpenSSL 1.1.0 分支中引入的。