2017-08-27 125 views
2

我试图在C#中签名一些字节,这要归功于类RSACryptoServiceProvider,并在C++中使用Crypto++库进行验证。尽管我尝试了所有的尝试,但验证失败,尽管我确信我的密钥和签名。c#中的RSA签名和Crypto ++的C++验证

在C#中我签如下:

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++中,我创建了公共密钥,并尝试验证如下:

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

我的密钥如下生成

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<PSS, SHA256>::Verifier

你能帮我解决吗?

+1

RSACryptoServiceProvider只做PKCS1_v1.5签名,它不能做PSS。所以你需要使用PKCS验证器。 – bartonjs

+0

谢谢,我已将我的验证器修改为'RSASS ::验证器',但问题仍然存在。 – Malick

回答

2

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

大量的试验/错误,我终于成功后,问题就来了,从我建立了加密+ 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; 
} 

Additionaly,我已复制与Windows API的验证,对于这种情况,我不使用xml关键,但直接从我获得rsa.ExportCspBlob(false)

(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) 

Th e代码产生:

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

是的,我发现你更容易创建'Integer',我会使用它。谢谢 – Malick

+1

@Malick - 这很有效,因为'StringSource'和过滤器之类的来源,像'Base64Decoder'都是从'BufferedTransformation'继承的。一旦你习惯了它,它易于使用的来源,过滤器和接收器。问题是,它是一个新用户,学习曲线可能很陡峭。 [管道](https://www.cryptopp.com/wiki/Pipelining)的机制并不明显。 – jww