C# 中的 RSA 签名和使用 Crypto++ 的 C++ 验证

2024-01-04

由于该类,我正在尝试在 C# 中签署一些字节RSAC加密服务提供者 https://msdn.microsoft.com/fr-fr/en-en/en-en/library/system.security.cryptography.rsacryptoserviceprovider(v=vs.110).aspx并用 C++ 验证它Crypto++ https://www.cryptopp.com/图书馆。尽管我进行了所有尝试,但验证还是失败,尽管我确信我的密钥和签名。

在 C# 中,我签名如下:

var message = "hello";
var bytes = System.Text.Encoding.UTF8.GetBytes(message);
byte[] signedHash;
using (RSACryptoServiceProvider rsa = new  RSACryptoServiceProvider())
{
    // Import the key information.
    rsa.ImportParameters(privateKey);
    // Sign the data, using SHA256 as the hashing algorithm 
    signedHash = rsa.SignData(bytes, CryptoConfig.MapNameToOID("SHA256"));
}

我的密钥生成如下:

CspParameters parameters = new CspParameters();
parameters.KeyNumber = (int)KeyNumber.Signature;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024, parameters))
{
    privateKeyInfo = rsa.ExportParameters(true);
    publicKeyInfo = rsa.ExportParameters(false);
}

在 C++ 中,我创建公钥并尝试按如下方式验证它:

RSA::PublicKey publicKey;
byte signature[128];
signature[0]= 150;
//....fill up to 127 , corresponds to "signedHash" variable from c# code
signature[127]= 89;

string simplemessage = "hello";
string modulus = "0Z8GUI/rxlXanCCjkiP+c9HyvdlOibst2YD5XmZk4F86aLr7LbLtI7FMnr6rcQZa6RXkAykb5MIbasmkOmkLzSjhdTThnaZyuKBOBoybYB5mDecF2VMXfUIryEBFn4i6y58qhy0BnDnIhucdNXX0px10HL3uYzR2KBTC0lSFFmE=";
string exponent = "AQAB";

char modulusCharred[1024];
strncpy_s(modulusCharred, base64ToHex(modulus).c_str(), sizeof(modulusCharred));
modulusCharred[sizeof(modulusCharred) - 1] = 0;

char exponentCharred[1024];
strncpy_s(exponentCharred, base64ToHex(exponent).c_str(), sizeof(exponentCharred));
exponentCharred[sizeof(exponentCharred) - 1] = 0;

Integer n(modulusCharred);
Integer e(exponentCharred);

publicKey.Initialize(n, e);

AutoSeededRandomPool rnd;
if(!publicKey.Validate(rnd, 3))
    throw runtime_error("Rsa public key validation failed"); // no error is thrown 

RSASS<PSS, SHA256>::Verifier verifier(publicKey);
bool result = verifier.VerifyMessage((const byte*)simplemessage.c_str(),simplemessage.length(), signature,128);

if(true == result) {
    cout << "Signature on message verified" << endl;
} else {
    cout << "Message verification failed" << endl; // always fail...
}

模数和指数是从 C# 中使用获得的 xml 复制/过去的rsa.ToXmlString(假) https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa.toxmlstring(v=vs.110).aspx。功能base64toHex给出(在另一个 SO 帖子上找到):

std::string base64ToHex(std::string base64String)
{
    std::string decodedString, finalString;
    CryptoPP::StringSource river(base64String, true,
        new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decodedString)));

    CryptoPP::StringSource stream(decodedString, true,
        new CryptoPP::HexEncoder(new CryptoPP::StringSink(finalString)));

    finalString.erase(std::remove(finalString.begin(), finalString.end(), '\n'), finalString.end());

    return finalString;
} 

我不想使用任何外部文件,只想使用字节(或字符串)变量。另外我不确定我定义验证程序的方式:RSASS<PSS, SHA256>::Verifier.

你能帮我解决这个问题吗?


这个答案适用于 crypto++ 和 windows API。

经过多次试验/错误我终于成功了,问题来自于我构建 crypto++ rsa 密钥的方式(Integer类型:模数和指数)。

我创建了一个新函数GetIntegerFromBase64String直接转换我们从 xml 输出中获得的模数和指数rsa.ToXmlString到 Integer 类型来初始化 cryptopp 中的密钥。

完整的 crypto++ 示例:

        string signature_64str = "G+PQaArLByTNYF5c5BZo2X3Guf1AplyJyik6NXCJmXnZ7CD5AC/OKq+Iswcv8GboUVsMTvl8G+lCa9Od0DfytnDui7kA/c1qtH7BZzF55yA5Yf9DGOfD1RHOl3OkRvpK/mF+Sf8nJwgxsg51C3pk/oBFjA450q2zq8HfFG2KJcs=";  
        string modulus_64str = "0Z8GUI/rxlXanCCjkiP+c9HyvdlOibst2YD5XmZk4F86aLr7LbLtI7FMnr6rcQZa6RXkAykb5MIbasmkOmkLzSjhdTThnaZyuKBOBoybYB5mDecF2VMXfUIryEBFn4i6y58qhy0BnDnIhucdNXX0px10HL3uYzR2KBTC0lSFFmE=";
        string exponent_64str  = "AQAB";

        Integer mod_integer = GetIntegerFromBase64String(modulus_64str);
        Integer pub_integer = GetIntegerFromBase64String(exponent_64str);
        InvertibleRSAFunction param;
        param.SetModulus(mod_integer);
        param.SetPublicExponent(pub_integer);
        RSA::PublicKey pubkey(param);

        string decoded_sig = DecodeBase64String(signature_64str);


        if(!pubkey.Validate(rnd, 3))
                cout << "Rsa public key validation failed" << endl;
        else
               cout << " key validation success"<<  endl;


        RSASS<PKCS1v15, SHA512>::Verifier verif(pubkey);
        bool res = verif.VerifyMessage( reinterpret_cast<const byte*>(message.c_str()), message.length(), reinterpret_cast<const byte*>(decoded_sig.c_str()), decoded_sig.length() );

         if( res ) {
                    cout << "Signature on message verified " << endl;
         } else {
                    cout << "Message verification failed " << endl;
         }

with :

string DecodeBase64String(string encoded )
{
        string decoded;    
        Base64Decoder decoder;
        decoder.Attach( new StringSink( decoded ) );
        decoder.Put( (byte*)encoded.data(), encoded.size() );
        decoder.MessageEnd();
        return decoded;

}
 Integer GetIntegerFromBase64String(string encoded)
{
        string decoded = DecodeBase64String(encoded);              
        Integer integer( (byte*)decoded.c_str(),decoded.length());
        return integer;
}

此外,我已经使用 Windows API 重现了验证,对于这种情况,我不使用 xml 密钥,而是直接使用我从以下位置获得的 blob 密钥(以 64 位编码)rsa.ExportCspBlob(假) https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.exportcspblob(v=vs.110).aspx

完整的 Windows API 示例:

在 c# 中,我得到的 CspBlob 如下:

  using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.ImportParameters(privateKey);
                var cspBlob = rsa.ExportCspBlob(false);
                var cspBlobBase_64str = Convert.ToBase64String(cspBlob);// <---- HERE

然后在 C++ 中,我加载 blob 并验证签名,如下所示:

#include <windows.h>
...
            string ErrorMessage;
            string signature_64str = "G+PQaArLByTNYF5c5BZo2X3Guf1AplyJyik6NXCJmXnZ7CD5AC/OKq+Iswcv8GboUVsMTvl8G+lCa9Od0DfytnDui7kA/c1qtH7BZzF55yA5Yf9DGOfD1RHOl3OkRvpK/mF+Sf8nJwgxsg51C3pk/oBFjA450q2zq8HfFG2KJcs=";
            string public_key_blob_64_bit_encoded = "BgIAAACkAABSU0ExAAQAAAEAAQBhFoVU0sIUKHY0Y+69HHQdp/R1NR3nhsg5nAEthyqfy7qIn0VAyCtCfRdT2QXnDWYeYJuMBk6guHKmneE0deEozQtpOqTJahvC5BspA+QV6VoGcau+nkyxI+2yLfu6aDpf4GRmXvmA2S27iU7ZvfLRc/4jkqMgnNpVxuuPUAaf0Q==";
            string message = "hello";


            if( RSA_VerifySignature(message,   signature_64str, public_key_blob_64_bit_encoded,  ErrorMessage))
            {
                 cout << "OK : Signature on message verified " << endl;
            }
            else
            {
                 cout << "Message verification failed, Error : " << ErrorMessage << endl;
            }

with :

 bool RSA_VerifySignature(string message, string signature_64BitEncoded, string publickeyBlob_64BitEncoded, string &ErrorMessage)
{
     const size_t LENGHT_SIGNATURE = 128; // 128 bytes == 1024 RSA Key bits 
     const size_t LENGHT_BLOB_PUBLIC_KEY = 148; // 148 bytes 

     bool isSigOk = false;
     HCRYPTHASH hash;

     byte  decoded_Blob[LENGHT_BLOB_PUBLIC_KEY] ;
     size_t size_pubkey = Base64Decode(publickeyBlob_64BitEncoded, decoded_Blob, LENGHT_BLOB_PUBLIC_KEY);

     byte  decoded_signature[LENGHT_SIGNATURE] ;
     size_t size_signature =Base64Decode(signature_64BitEncoded, decoded_signature, LENGHT_SIGNATURE);

     //reverse bytes
     byte reverse_decoded_signature[LENGHT_SIGNATURE];
     for(int i=0;i<sizeof(reverse_decoded_signature);i++)
         reverse_decoded_signature[i] = decoded_signature[LENGHT_SIGNATURE-i-1];


     HCRYPTPROV cryptProvider;
     // Get a handle to the PROV_RSA_AES (for CALG_SHA_512).
     if (!CryptAcquireContext(&cryptProvider, 0, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){  
        ErrorMessage = "Failure to acquire context";
        goto Exit;
     }

     HCRYPTKEY publicKeyc;
     // convert the blob to the public key
     if(!CryptImportKey(cryptProvider, decoded_Blob, LENGHT_BLOB_PUBLIC_KEY, 0, 0, &publicKeyc)){ 

        ErrorMessage = "Failure to import key";
        goto Exit;
     }

     // create the hash object
     if(!CryptCreateHash(cryptProvider, CALG_SHA_512 , 0, 0, &hash)){
        ErrorMessage = "Failure to creat Hash"  ;
        goto Exit;
     }

     //hash the message
     if(!CryptHashData(hash, (byte*) message.c_str(), message.length(), 0)){  
        ErrorMessage = "Failure to Hash Data"  ;
        goto Exit;
     }

       isSigOk = CryptVerifySignature(hash, reverse_decoded_signature, sizeof(reverse_decoded_signature), publicKeyc, nullptr, 0);

       if(!isSigOk) ErrorMessage = "Invalid Signature"  ;


     Exit:  
     // After processing, hHash and cryptProvider must be released. 
     if(hash) 
        CryptDestroyHash(hash);
     if(cryptProvider) 
        CryptReleaseContext(cryptProvider,0);
       return isSigOk;
}

where Base64Decode来自这个所以答案 https://stackoverflow.com/a/21867132/3205529.

ps:请注意,我在这个答案中已切换到SHA512。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 中的 RSA 签名和使用 Crypto++ 的 C++ 验证 的相关文章

  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • SetCurrentDirectoryW 中的错误 206

    在我之后之前不清楚的问题 https stackoverflow com questions 44389617 long path name in setcurrentdirectoryw 我以某种方式能够创建一个具有长路径名的目录 但是
  • 使用实体框架模型输入安全密钥

    这是我今天的完美想法 Entity Framework 中的强类型 ID 动机 比较 ModelTypeA ID 和 ModelTypeB ID 总是 至少几乎 错误 为什么编译时不处理它 如果您使用每个请求示例 DbContext 那么很
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • 使用 WebClient 时出现 System.Net.WebException:无法创建 SSL/TLS 安全通道

    当我执行以下代码时 System Net ServicePointManager ServerCertificateValidationCallback sender certificate chain errors gt return t
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • C# 成员变量继承

    我对 C 有点陌生 但我在编程方面有相当广泛的背景 我想做的事情 为游戏定义不同的 MapTiles 我已经像这样定义了 MapTile 基类 public class MapTile public Texture2D texture pu
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • 对来自流读取器的过滤数据执行小计

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐