【问题标题】:RSA signature in c# and verification in C++ with Crypto++c# 中的 RSA 签名和 C++ 中的 Crypto++ 验证
【发布时间】:2017-08-27 23:16:38
【问题描述】:

感谢RSACryptoServiceProvider 类,我正在尝试在C# 中对一些字节进行签名,并使用Crypto++ 库在C++ 中对其进行验证。尽管我做了所有尝试,但验证失败了,尽管我确信我的密钥和签名。

在 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# 中使用 rsa.ToXmlString(false) 获得的 xml 复制/过去的。函数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&lt;PSS, SHA256&gt;::Verifier

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

【问题讨论】:

  • RSACryptoServiceProvider 只做 PKCS1_v1.5 签名,它不能做 PSS。所以你需要使用 PKCS 验证器。
  • 谢谢,我已将验证器修改为RSASS&lt;PKCS1v15, SHA256&gt;::Verifier,但问题仍然存在。

标签: c# c++ windows cryptography crypto++


【解决方案1】:

此答案适用于 crypto++ 和 windows API。

经过大量试验/错误后,我终于成功了,问题出在我构建 crypto++ rsa 密钥的方式上(Integer 类型:模数和指数)。

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

全加密++示例:

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

与:

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密钥,而是直接使用我从rsa.ExportCspBlob(false)获得的blob密钥(以64位编码)

完整的 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;
            }

与:

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

Base64Decode 来自这个SO answer

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

【讨论】:

    【解决方案2】:

    关于这个:

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

    您可能会发现这更容易(或者可能不会):

    $ cat test.cxx
    #include "cryptlib.h"
    #include "integer.h"
    #include "filters.h"
    #include "base64.h"
    #include "files.h"
    #include "hex.h"
    
    #include <iostream>
    #include <string>
    
    int main()
    {
        using CryptoPP::Integer;
        using CryptoPP::StringSource;
        using CryptoPP::Base64Decoder;
    
        std::string signature_64str = "G+PQaArLByTNYF5c5BZo2X3Guf1AplyJyik6NXCJmXnZ7CD5AC/OKq+Iswcv8GboUVsMTvl8G+lCa9Od0DfytnDui7kA/c1qtH7BZzF55yA5Yf9DGOfD1RHOl3OkRvpK/mF+Sf8nJwgxsg51C3pk/oBFjA450q2zq8HfFG2KJcs=";
        std::string modulus_64str = "0Z8GUI/rxlXanCCjkiP+c9HyvdlOibst2YD5XmZk4F86aLr7LbLtI7FMnr6rcQZa6RXkAykb5MIbasmkOmkLzSjhdTThnaZyuKBOBoybYB5mDecF2VMXfUIryEBFn4i6y58qhy0BnDnIhucdNXX0px10HL3uYzR2KBTC0lSFFmE=";
        std::string exponent_64str  = "AQAB";
    
        StringSource mod64(modulus_64str, true, new Base64Decoder());
        StringSource exp64(exponent_64str, true, new Base64Decoder());
    
        Integer mod_integer(mod64, mod64.MaxRetrievable());
        Integer pub_integer(exp64, exp64.MaxRetrievable());
    
        std::cout << mod_integer << std::endl;
        std::cout << pub_integer << std::endl;
    
        return 0;
    }
    

    代码使用这个Integer constructor:

    Integer (BufferedTransformation &bt, size_t byteCount, Signedness sign=UNSIGNED, ByteOrder order=BIG_ENDIAN_ORDER)
    

    代码产生:

    $ ./test.exe
    14720100561434965959279660783734794237717246172864229085441100391073349113964189
    60745884288272734508848163730385929321875027666547189267336228176446737079038557
    84643451276687447934542940291651491826423492370177748998568853020003645093067936
    387415713280978250634089486257964777026826174628998219103122976216673.
    65537.
    

    【讨论】:

    • 是的,我发现创建 Integer 的方法更容易,我会使用它。谢谢
    • @Malick - 因为源,如StringSource 和过滤器,如Base64Decoder,都继承自BufferedTransformation。一旦你习惯了它,它就很容易使用源、过滤器和接收器。问题是,作为一个新用户很糟糕,而且学习曲线可能很陡峭。 pipelines 的机制并不明显。
    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 2017-09-07
    • 1970-01-01
    相关资源
    最近更新 更多