C ++和C#与Crypto之间的加密互操作性

时间:2014-06-13 22:41:55

标签: c# c++ qt encryption aes

我正在尝试用Qt项目中的Crypto ++ lib加密C ++中的字符串,并在Web应用程序中用C#解密它。这是我的代码。

C ++ Code,使用Crypto ++ lib

std::string Crypter::encrypt(const std::string& str_in, const std::string& key, const std::string& iv)
{
    std::string str_out;
    CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption encryption((byte*)key.c_str(), key.length(), (byte*)iv.c_str());
    qDebug() << encryption.DefaultKeyLength();
    qDebug() << encryption.DefaultIVLength();


    CryptoPP::StringSource encryptor(str_in, true,
        new CryptoPP::StreamTransformationFilter(encryption,
            new CryptoPP::Base64Encoder(
                new CryptoPP::StringSink(str_out),
                false // do not append a newline
            )
        )
    );
    return str_out;
}

在此处调用此功能

std::string str = "123456789012345";
std::string key = "01234567891234560123456789123456"; // 32 bytes
std::string iv  = "0123456789123456"; // 16 bytes
std::string str_encrypted = c->encrypt(str, key, iv);
std::string str_decrypted = c->decrypt(str_encrypted, key, iv);
std::cout << "str_encrypted: " << str_encrypted << std::endl;
std::cout << "str_decrypted: " << str_decrypted << std::endl;

此代码生成以下结果

Plain text: "123456789012345"
Encrypted value (base64): 3Qo/6hWctRiID3txA9nC

我用C#写的相同代码

    private void button1_Click(object sender, EventArgs e)
    {
        string strOutput = Encrypt("123456789012345");
        Debug.WriteLine("Encrypted value is: " + strOutput);
    }

   private string Encrypt(string clearText)
    {
        byte[] clearBytes = Encoding.ASCII.GetBytes(clearText + "\0");

        using (Aes encryptor =  Aes.Create("AES"))
        {
            encryptor.BlockSize = 128;
            encryptor.KeySize = 128;
            encryptor.Mode = CipherMode.CFB;
            encryptor.Key = Encoding.ASCII.GetBytes("01234567891234560123456789123456");
            encryptor.IV = Encoding.ASCII.GetBytes("0123456789123456");
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                byte[] bt = ms.ToArray();
                clearText = Convert.ToBase64String(bt);
            }
        }
        return clearText;
    }

产生以下结果

Encrypted value is: 3YklwM2vG20ZmkOT029jTTL7FlSZHrh0RfvaT1FFa2k=

有人可以告诉我我错过了什么吗?从两种语言获得类似输出的正确方法是什么。

我的目标是加密C ++中的值并在C#中对其进行解密。

修改

我做了一些改变。

用123456789012345替换Hello world 将编码从utf更改为Ascii 在C#字符串的末尾添加了一个空字节 将模式更改为CFB

我还使用新结果编辑了原始结果

不幸的是,在这样做之后,两个字符串都不匹配。

我确保两个输入都相同。

2 个答案:

答案 0 :(得分:1)

您的C ++代码是std::string。这很可能是在ANSI代码页下编码的文本。当你将它传递给CryptoPP::StringSource时,我希望它能直接处理该文本的字节,而不会将其转换为任何其他编码。

您的C#正在传递Encoding.Unicode.GetBytes的结果。这意味着加密正在处理UTF-16编码数据的字节。

由于编码不同,字节表示不同。然后由于字节不同,加密结果也不同。

您需要让两段代码在同一方案下运行。

如果您想要处理ANSI(甚至只是ASCII)字符(可能是您的C ++代码的情况),那么您可以修改C#代码以使用Encoding.Default.GetBytes(或者可能{ {1}})获取clearText的字节。


修改

进一步看,当您的C#代码使用Encoding.ASCII.GetBytes时,您的C ++代码正在使用CryptoPP::CFB_Mode。这些模式需要匹配,否则算法将以不同的方式应用。

您可能需要检查其他属性,例如填充,以确保两者都在相同的方案下工作。

答案 1 :(得分:0)

似乎存在两个潜在问题。以下代码将生成与CryptoCC库(3Qo/6hWctRiID3txA9nC)相同的输出:

byte[] clearBytes = Encoding.ASCII.GetBytes(clearText);
using (var encryptor = RijndaelManaged.Create())
{
    encryptor.KeySize = 128;
    encryptor.Padding = PaddingMode.Zeros;
    encryptor.Mode = CipherMode.CFB;
    encryptor.Key = Encoding.ASCII.GetBytes("01234567891234560123456789123456");
    encryptor.IV = Encoding.ASCII.GetBytes("0123456789123456");
    using (MemoryStream ms = new MemoryStream())
    {
       using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(clearBytes, 0, clearBytes.Length);
            cs.Close();
        }
        Array.Copy(ms.ToArray(), clearBytes, clearBytes.Length);
        clearText = Convert.ToBase64String(clearBytes);
     }
 }
 return clearText;

同样,以下Crypto ++实现将提供示例中返回的值(3YklwM2vG20ZmkOT029j)。

std::string encrypt(const std::string& str_in, const std::string& key, const std::string& iv)
{
std::string str_out;
CryptoPP::AES::Encryption e1((byte*)key.c_str(), key.length());
    // use feedback size of 1 byte.
CryptoPP::CFB_Mode_ExternalCipher::Encryption encryption(e1, (byte*)iv.c_str(), 1);


    CryptoPP::StringSource encryptor(str_in, true,
     new CryptoPP::StreamTransformationFilter(encryption,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(str_out),
            false // do not append a newline
        )
     )
    );
    return str_out;
}

一些注意事项:

  1. 没有必要在字符串后附加一个零尾。

  2. Crypto ++实现不允许在密码反馈(CFB)模式下填充。 .NET实现需要填充;但是,可以手动截断多余的数据(如上面的.NET示例中所做的那样)。 (见http://social.msdn.microsoft.com/Forums/vstudio/en-US/a1be5f49-5f0f-4f5f-b01c-af46fdc71915/des-encryption-cfb-mode)。

  3. 有关使用AES代替Rijndael作为CSP的含义,请参阅this post。特别是,以下警告适用于CFB模式:

  4.   

    基本上,如果您想将RijndaelManaged用作AES,您需要确保:

         
        
    1. 块大小设置为128位
    2.   
    3. 您没有使用CFB模式,或者如果您的反馈大小也是128位
    4.   

    在这种情况下,使用CFB模式会带来额外的复杂性。请注意,这是使用CFB的结果;如果使用密码块链接(CBC)模式,AesRijndael将返回与给定密钥和值(IwffxivpwdSuS9BV0KeyCg==)相同的Crypto ++结果。