Sign 使用WinCrypt或CNG验证签名文件(PKCS7)

Sign 使用WinCrypt或CNG验证签名文件(PKCS7),sign,asn.1,cng,wincrypt,Sign,Asn.1,Cng,Wincrypt,我需要使用Windows crypto API方法验证已签名的JAR文件。我对加密和签名事务只有基本的了解。我还不熟悉那些加密API(WinCrypt、Bcrypt、Ncrypt)。验证文件哈希不是问题,但签名部分阻止了我 多亏了OpenSSL、PKCS7 RFC()和其他各种源代码,我才能够计算出JAR中包含的META-INF/LOCALSIG.DSA的实际文件内容。但经过两个星期的挖掘、反复试验,我仍然被困在那里,不知道还能尝试什么 OpenSSL在LOCALSIG.DSA-content

我需要使用Windows crypto API方法验证已签名的JAR文件。我对加密和签名事务只有基本的了解。我还不熟悉那些加密API(WinCrypt、Bcrypt、Ncrypt)。验证文件哈希不是问题,但签名部分阻止了我

多亏了OpenSSL、PKCS7 RFC()和其他各种源代码,我才能够计算出JAR中包含的META-INF/LOCALSIG.DSA的实际文件内容。但经过两个星期的挖掘、反复试验,我仍然被困在那里,不知道还能尝试什么

OpenSSL在LOCALSIG.DSA-content LOCALSIG.SF-noverify中有一个漂亮的命令
OpenSSL smime-verify-inform-der-in LOCALSIG.DSA-content LOCALSIG.SF-noverify
,它正是我想要做的。不幸的是,我在Windows API中找不到这样的高级命令

我曾尝试使用所有三个API中的
VerifySignature
函数系列,但我需要为这些函数提供公钥,而且我没有使用任何
ImportKey
函数。因此,我尝试使用
cryptdecateobjectex
手动解析ASN1格式,以便将各个部分作为blob传递给API函数。虽然我在这方面取得了一些成功,但我再次陷入困境,因为我不知道如何解析集合。我不想从头开始编写自己的ASN1解析器

那么,如何在Windows crypto API中使用PKCS7签名文件呢

我想使用OpenSSL可能会更容易,但我必须说服我的雇主将OpenSSL添加到我们的代码库中,仅仅为了这个目的


更新:LOCALSIG.DSA文件包含签名者证书和LOCALSIG.SF文件的签名哈希。这可以使用LOCALSIG.DSA中的
openssl pkcs7-inform der-print\u certs-text-或LOCALSIG.DSA中的
openssl cms-cmsout-inform der-print-进行验证

该证书由我公司自行签署,并存放在证书库中。我可能需要提供整个信任链。这就是为什么我将
-noverify
选项添加到
openssl smime-verify


事实上,有两种不同证书的场景(内部和外部版本),一种使用DSA(sig文件包含一个证书),另一种使用RSA(sig文件包含三个证书)。这意味着我不能硬编码使用哪个证书或方法。我需要从提供的文件中提取这些信息。我如何做到这一点?

我不确定我们是否拥有所需的所有信息,因此我没有任何明确的答案。但是,让我们看看我们是否能够在解决方案方面取得进展

你问:

如何知道加载哪个证书以及从何处加载

作为验证者,您需要以某种形式拥有该可信证书。对于Windows,如果在您的证书存储中安装了该证书就好了。这就是我在回答中的假设。如果你不知道证书在哪里,那么你必须先弄清楚,否则你将无法正确验证签名。不知何故,证书需要由签名者通过可信渠道提供给验证者(你)

你还写道:

OpenSSL具有nice命令
opensslsime-verify-inform-der-in
LOCALSIG.DSA-content LOCALSIG.SF-noverify
,它与我的 想做什么

你确定这正是你想做的吗?由于您正在传递
-noverify
标志,因此此命令所做的一切就是验证
LOCALSIG.DSA
是否包含内容的正确签名
LOCALSIG.SF
。但是,它不会验证此签名是否由您信任的标识创建。任何拥有有效证书和密钥对的人都可以创建该签名

为了使用库存openssl版本验证签名(包括签名者的身份),您需要以openssl可以理解的格式(PEM或DER)提供整个证书链,直至自签名根证书。然后,您可以使用稍微不同的命令检查它

openssl smime -verify -inform der -in LOCALSIG.DSA -content LOCALSIG.SF -CAfile trusted-cert.pem

您的问题将更新,以指示签名证书是自签名的,并且在证书存储中。仍然存在多个可能的路由,但我假设此时您知道您希望生成此签名的签名者的主题名称。(如果没有,请说明您的期望。)有了这些信息,您可以使用以下步骤验证您的签名:

首先用于在证书存储中查找证书。例如用法,请参见

当您拥有该证书的句柄时,使用获取所需公钥的句柄


该句柄类型为
BCRYPT\u KEY\u handle
,您可以使用进行验证。

根据您的问题,您需要使用分离的签名验证PKC7。为此,您可以使用函数

示例代码:

CRYPT_VERIFY_MESSAGE_PARA vparam = { 0 };
    vparam.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
    vparam.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
    vparam.pfnGetSignerCertificate = nullptr;
    vparam.pvGetArg = nullptr;


    /* read your files somehow */
    std::vector<char> sig;
    if (!ReadFile("LOCALSIG.DSA", &sig))
    {
        std::cout << "Failed to read file" << std::endl;
        return;
    }

    std::vector<char> mes;
    if (!ReadFile("LOCALSIG.SF", &mes))
    {
        std::cout << "Failed to read file" << std::endl;
        return;
    }

    /* CryptVerifyDetachedMessageSignature requires array of messages to verify */
    const BYTE* marray[] = {
        reinterpret_cast<BYTE*>(mes.data())
    };
    DWORD marray_len[] = {
        static_cast<DWORD>(mes.size())
    };

    if (!CryptVerifyDetachedMessageSignature(vparam,
        0, 
        reinterpret_cast<BYTE*>(sig.data()),
        static_cast<DWORD>(sig.size()), 
        1, /* number of messages in marray */
        marray,
        marray_len,
        nullptr))
    {
        std::cout << "Failed to verify signature, error: " << GetLastError() << std::endl;
    }
    else
    {
        std::cout << "Verify success, signature valid" << std::endl;
    }
PCCERT_CHAIN_CONTEXT pChain = nullptr;
CERT_CHAIN_PARA chainPara = {0};
HCERTSTORE hStore = nullptr;
/* you sig file */
DATA_BLOB db = {
    static_cast<DWORD>(sig.size()), reinterpret_cast<BYTE *>(sig.data())};

/* you can open your sig file as certificate store */
hStore = CertOpenStore(CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, reinterpret_cast<BYTE *>(&db));
if (!hStore)
{
    goto Exit;
}

chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
chainPara.RequestedUsage.Usage.cUsageIdentifier = 0;

if (!CertGetCertificateChain(NULL,  /* use default chain engine */
                             pCert, /*  pCert - pointer to signer cert structure (the parameter that was obtained in the previous step) */
                             NULL,
                             hStore, /* point to additional store where need to search for certificates to build chain */
                             &chainPara,
                             CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
                             NULL,
                             &pChain))
{
    std::cout << "failed to build chain: " << GetLastError();
    goto Exit;
}

if (pChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
{
    std::cout << "certificate valid";
    goto Exit;
}
if (pChain->TrustStatus.dwErrorStatus & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
{
    std::cout << "certificate revocation status unknown";
}
/* you need to place root certificate to the Trusted Root Store */
if (pChain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT)
{
    std::cout << "untrusted CA";
}
/* and so on */

Exit : 
if (pCert)
{
    CertFreeCertificateContext(pCert);
}
if (pChain)
{
    CertFreeCertificateChain(pChain);
}
if (hStore)
{
    CertCloseStore(hStore, 0);
}
CRYPT\u VERIFY\u MESSAGE\u PARA vparam={0};
vparam.cbSize=sizeof(密码、验证、消息、段落);
vparam.dwMsgAndCertEncodingType=X509_ASN_编码| PKCS_7_ASN_编码;
vparam.pfnGetSignerCertificate=nullptr;
vparam.pvGetArg=nullptr;
/*以某种方式阅读你的文件*/
std::载体sig;
如果(!ReadFile(“LOCALSIG.DSA”、&sig))
{

std::我可以添加
-noverify
,因为证书是自签名的(公司范围,在证书存储中),这使得openssl退出时出错。显然,在这种情况下,我需要提供完整的连接证书链,我打算稍后解决,但可能是相关的。我在LOCALSIG.DSA
中使用了
openssl pkcs7-inform der-print\u certs-text-来检查文件。
cms
命令给出了几乎相同的结果lts,有更多的信息。@Simpleton我在答案中添加了更多的信息,