CryptoAPI:如何使用 CryptVerifySignature 验证来自 OpenSSL 或 Java 的 DSA 签名

发布于 2024-10-18 02:23:13 字数 173 浏览 1 评论 0原文

我希望能够使用 Microsoft CryptoAPI 验证 OpenSSL 生成的 DSA 签名。

假设您有以下输入:

  • 现有的 DSA 公钥:
  • 要验证的数据
  • 二进制签名

签名已从 Base64 转换为一系列 48 字节。

I would like to be able to verify an OpenSSL-generated DSA signature using the Microsoft CryptoAPI.

Consider that you have the following inputs:

  • an existing DSA public key:
  • the data to be verified
  • a binary signature

The signature has already been converted from Base64 into a series of 48 bytes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清眉祭 2024-10-25 02:23:13

如果没有很好地了解 CryptoAPI,这将会困难得多。

主要的障碍是:

  • 使用 CryptStringToBinaryA 和 CryptDecodeObjectEx 解码 X509 DSA 公钥
  • 转换 DSA 签名格式
    • OpenSSL 的 DSA_sign 生成 ASN.1 DER 格式的 DSA 签名
    • CryptoAPI 的 CryptVerifySignature 需要 P1363 格式的 DSA 签名

以下是我最终如何解决该问题的粗略示例:

const char* pubKey =  "MIIBtjCCASsGByqGSM44BAEwggEeAoGBANW/k8nYREKtRMvIShnJTSAwxF33haU4"
                      .....
                      "/FEGAibbOp31rjq9UfaJ2t06eN0t0B+DP1hjz/MfpGtPOxHqF3dQnDRa3ot1FSTP";

bool verify(const unsigned char* msgData, unsigned int msgLength, const unsigned char* signature, unsigned int signatureLength)
{
    HCRYPTPROV hCryptProv;
    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_DSS, CRYPT_VERIFYCONTEXT))
    {
        return false;
    }

    bool result = false;

    unsigned char derPubKey[2048];
    DWORD  derPubKeyLen = 2048;
    CERT_PUBLIC_KEY_INFO *publicKeyInfo = NULL;
    DWORD  publicKeyInfoLen = 0;

    if ( CryptStringToBinaryA( pubKey, strlen(pubKey), CRYPT_STRING_BASE64, derPubKey, &derPubKeyLen, NULL, NULL ) &&
         CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen, 
                              CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
    {
        HCRYPTKEY hPubKey;
        if (CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING, publicKeyInfo, &hPubKey))
        {
            HCRYPTHASH hHash;
            if (CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash))
            {
                CryptHashData(hHash, msgData, msgLength, 0);

                BYTE* dsaSignature = NULL;
                DWORD dsaSignatureLen = 0;
                if (CryptDecodeObjectEx( X509_ASN_ENCODING, X509_DSS_SIGNATURE, signature, signatureLength,
                                         CRYPT_ENCODE_ALLOC_FLAG, NULL, &dsaSignature, &dsaSignatureLen ) )
                {
                    if (CryptVerifySignature(hHash, dsaSignature, dsaSignatureLen, hPubKey, NULL, 0))
                    {
                        result = true;
                    }
                    LocalFree(dsaSignature);
                }

                CryptDestroyHash(hHash);
            }
            CryptDestroyKey(hPubKey);
        }
        LocalFree(publicKeyInfo);
    }
    CryptReleaseContext(hCryptProv, 0);
    return result;
}

Without good knowledge of the CryptoAPI, this is much more difficult that it should be.

The major stumbling blocks were:

  • Decode the X509 DSA public key using CryptStringToBinaryA and CryptDecodeObjectEx
  • Convert the DSA signature format
    • OpenSSL's DSA_sign produces a DSA signature in the ASN.1 DER format
    • CryptoAPI's CryptVerifySignature expects the DSA signature in the P1363 format

Here's a rough sample of how I finally solved the problem:

const char* pubKey =  "MIIBtjCCASsGByqGSM44BAEwggEeAoGBANW/k8nYREKtRMvIShnJTSAwxF33haU4"
                      .....
                      "/FEGAibbOp31rjq9UfaJ2t06eN0t0B+DP1hjz/MfpGtPOxHqF3dQnDRa3ot1FSTP";

bool verify(const unsigned char* msgData, unsigned int msgLength, const unsigned char* signature, unsigned int signatureLength)
{
    HCRYPTPROV hCryptProv;
    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_DSS, CRYPT_VERIFYCONTEXT))
    {
        return false;
    }

    bool result = false;

    unsigned char derPubKey[2048];
    DWORD  derPubKeyLen = 2048;
    CERT_PUBLIC_KEY_INFO *publicKeyInfo = NULL;
    DWORD  publicKeyInfoLen = 0;

    if ( CryptStringToBinaryA( pubKey, strlen(pubKey), CRYPT_STRING_BASE64, derPubKey, &derPubKeyLen, NULL, NULL ) &&
         CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen, 
                              CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
    {
        HCRYPTKEY hPubKey;
        if (CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING, publicKeyInfo, &hPubKey))
        {
            HCRYPTHASH hHash;
            if (CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash))
            {
                CryptHashData(hHash, msgData, msgLength, 0);

                BYTE* dsaSignature = NULL;
                DWORD dsaSignatureLen = 0;
                if (CryptDecodeObjectEx( X509_ASN_ENCODING, X509_DSS_SIGNATURE, signature, signatureLength,
                                         CRYPT_ENCODE_ALLOC_FLAG, NULL, &dsaSignature, &dsaSignatureLen ) )
                {
                    if (CryptVerifySignature(hHash, dsaSignature, dsaSignatureLen, hPubKey, NULL, 0))
                    {
                        result = true;
                    }
                    LocalFree(dsaSignature);
                }

                CryptDestroyHash(hHash);
            }
            CryptDestroyKey(hPubKey);
        }
        LocalFree(publicKeyInfo);
    }
    CryptReleaseContext(hCryptProv, 0);
    return result;
}
倾城花音 2024-10-25 02:23:13

请注意,Microsoft 的加密 API 不支持大于 1024 的 DSA 密钥长度。CryptImportPublicKeyInfo() 将失败并显示 NTE_BAD_DATA。

Just a note, Microsoft's crypto API doesn't support DSA key lengths greater than 1024. CryptImportPublicKeyInfo() will fail with NTE_BAD_DATA.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文