理解为什么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。
为了观察我所做的同样的失败,我将文件上传到以下链接:
您上传的示例文件有一些属性导致无法通过验证。
首先,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_chain
和 no_time_check
选项,因为它们是在 OpenSSL 1.1.0 分支中引入的。
我有一个名为 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。 为了观察我所做的同样的失败,我将文件上传到以下链接:
您上传的示例文件有一些属性导致无法通过验证。
首先,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_chain
和 no_time_check
选项,因为它们是在 OpenSSL 1.1.0 分支中引入的。