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

标签 sign asn.1 cng wincrypt

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

多亏了 OpenSSL、PKCS7 RFC ( https://www.rfc-editor.org/rfc/rfc2315 ) 和各种其他来源,我才能够弄清楚 JAR 中包含的 META-INF/LOCALSIG.DSA 的实际文件内容。但经过两周的挖掘、试验和错误后,我仍然卡住了,不知道还能尝试什么。

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

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

那么,如何将 PKCS7 签名文件与 Windows 加密 API 一起使用?

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


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

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

其实有两种不同证书的场景(内部和外部发行),一种使用DSA(sig文件包含一个证书),另一种使用RSA(sig文件包含三个证书)。这意味着我无法硬编码要使用的证书或方法。我需要从提供的文件中提取该信息。我该怎么做?

最佳答案

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

示例代码:

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

更新
要验证证书链,您需要使用 CertGetCertificateChain

示例代码:

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

关于sign - 使用 WinCrypt 或 CNG 验证签名文件 (PKCS7),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54222565/

相关文章:

java - 需要帮助理解 org.bouncycaSTLe.asn1.ASN1InputStream "java.io.IOException: DER length more than 4 bytes: 63"

c++ - 如何简单地创建一个 ASN.1 DER 编码的 blob

c# - 创建 CNG key 的权限

certificate - 从 .NET 4.6 访问 CNG 证书的私钥 - 未找到 GetCngPrivateKey

C 只打印整数的符号

networking - 什么是 ASN 文件以及如何使用它?

hash - 在 Erlang 中将数据 block 签名为哈希不起作用。基于ec的 key 推导

rsa - 在 Windows 中使用 CryptoAPI Next Generation (CNG) 进行 RSA 加密/解密的错误?

encryption - go crypto/ecdsa 即使数据不同也要验证是否为真

java - 签名 PADES 顺序或并行