CryptoAPI:如何使用CryptoVerifySignature从OpenSSL或Java验证DSA签名

CryptoAPI:如何使用CryptoVerifySignature从OpenSSL或Java验证DSA签名,openssl,digital-signature,cryptoapi,Openssl,Digital Signature,Cryptoapi,我希望能够使用Microsoft CryptoAPI验证OpenSSL生成的DSA签名 考虑到您有以下输入: 现有DSA公钥: 待核实的数据 二进制签名 签名已从Base64转换为一系列48字节。如果不熟悉CryptoAPI,这将比它本应具有的难度大得多 主要障碍是: 使用CryptStringToBinaryA和CryptDecodeObjectEx解码X509 DSA公钥 转换DSA签名格式 OpenSSL的DSA_签名生成ASN.1 DER格式的DSA签名 CryptoAPI的Cr

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

考虑到您有以下输入:

  • 现有DSA公钥:
  • 待核实的数据
  • 二进制签名

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

如果不熟悉CryptoAPI,这将比它本应具有的难度大得多

主要障碍是:

  • 使用CryptStringToBinaryA和CryptDecodeObjectEx解码X509 DSA公钥
  • 转换DSA签名格式
    • OpenSSL的DSA_签名生成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;
}

如果对CryptoAPI没有很好的了解,这将比它应该具备的困难得多

主要障碍是:

  • 使用CryptStringToBinaryA和CryptDecodeObjectEx解码X509 DSA公钥
  • 转换DSA签名格式
    • OpenSSL的DSA_签名生成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;
}

请注意,Microsoft的加密API不支持大于1024的DSA密钥长度。CryptImportPubliceInfo()将使用NTE\u BAD\u数据失败。

请注意,Microsoft的加密API不支持大于1024的DSA密钥长度。CryptImportPubliceInfo()将因NTE\u坏数据而失败。

谢谢!顺便说一句:当使用大于1024(Win10)的DSA密钥时,我打赌E_INVALIDARG(0x80070057)。谢谢!顺便说一句:当使用大于1024(Win10)的DSA密钥时,我打赌E_INVALIDARG(0x80070057)。