了解在使用openssl验证期间pkcs7块为何失败

时间:2018-11-10 22:12:33

标签: openssl x509certificate pkcs#7

我有一个名为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

但是,出现以下错误:

  

验证失败   140735569544136:错误:21075075:PKCS7例程:PKCS7_verify:证书验证错误:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_smime.c:343:验证错误:无法获得本地发行者证书

我还尝试添加最后一个命令-noverify标志,但是遇到了另一个错误。

  

验证失败   140735569544136:错误:21071065:PKCS7例程:PKCS7_signatureVerify:摘要失败:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_doit.c:1084:   140735569544136:错误:21075069:PKCS7例程:PKCS7_verify:签名失败:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pkcs7/pk7_smime.c:412:< / p>

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

我在这里做错了什么?

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

https://ufile.io/vrqpt

https://ufile.io/ajgex

1 个答案:

答案 0 :(得分:2)

您上传的示例文件具有一些属性,因此无法通过验证。

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

然后,在mroot.pem.cer文件中找到的“信任根”不是正确的文件。您提取了Microsoft Time-Stamp PCA证书,而p7文件的签名者最多链接到Microsoft Code Signing PCA证书。

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

此外,看起来PKCS7验证的OpenSSL实现要求您的证书包括S / MIME签名的扩展密钥用法,而您的证书不包括此扩展密钥。通过设置OpenSSL X509_STORE的代码签名目的,似乎可以解决此问题。 OpenSSL smime -verify工具不会公开这种设置,因此您必须通过设置XKU_CODE_SIGN来以编程方式进行此设置。 XKU代表扩展密钥用法,并且要注意,实际上不存在围绕它的OpenSSL文档。如果决定使用它,则必须彻底测试。

下面的代码段(省略了返回代码检查)成功地验证了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()调用不会验证证书链,但会检查签名。这需要提取散列,该散列存储在名为SpcIndirectDataContent的p7的Microsoft特定元素中,如下面的@ dave_thompson_085所指出的。可以验证在该哈希上获得的签名。为了进行完整的验证,您还需要重新计算PE文件本身的哈希值,并将其与p7中的哈希值进行比较。

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