到目前为止,我发现的大多数示例和问题仅适用于C#,但是我试图将以下C#代码重现为C ++ / CLI:
Most examples and questions I've found so far is for C# only, however I'm trying to reproduce the following C# code into C++/CLI:
using System.Security.Cryptography; using System.IO; public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes) { byte[] encryptedBytes = null; // Set your salt here, change it to meet your flavor: // The salt bytes must be at least 8 bytes. byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; using (MemoryStream ms = new MemoryStream()) { using (RijndaelManaged AES = new RijndaelManaged()) { AES.KeySize = 256; AES.BlockSize = 128; var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); AES.Key = key.GetBytes(AES.KeySize / 8); AES.IV = key.GetBytes(AES.BlockSize / 8); AES.Mode = CipherMode.CBC; using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length); cs.Close(); } encryptedBytes = ms.ToArray(); } } return encryptedBytes; } public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes) { byte[] decryptedBytes = null; // Set your salt here, change it to meet your flavor: // The salt bytes must be at least 8 bytes. byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; using (MemoryStream ms = new MemoryStream()) { using (RijndaelManaged AES = new RijndaelManaged()) { AES.KeySize = 256; AES.BlockSize = 128; var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); AES.Key = key.GetBytes(AES.KeySize / 8); AES.IV = key.GetBytes(AES.BlockSize / 8); AES.Mode = CipherMode.CBC; using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length); cs.Close(); } decryptedBytes = ms.ToArray(); } } return decryptedBytes; } //Encrypt String public string EncryptText(string input, string password) { // Get the bytes of the string byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input); byte[] passwordBytes = Encoding.UTF8.GetBytes(password); // Hash the password with SHA256 passwordBytes = SHA256.Create().ComputeHash(passwordBytes); byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes); string result = Convert.ToBase64String(bytesEncrypted); return result; } //Decrypt String public string DecryptText(string input, string password) { // Get the bytes of the string byte[] bytesToBeDecrypted = Convert.FromBase64String(input); byte[] passwordBytes = Encoding.UTF8.GetBytes(password); passwordBytes = SHA256.Create().ComputeHash(passwordBytes); byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes); string result = Encoding.UTF8.GetString(bytesDecrypted); return result; }这是我到目前为止所得到的:
This is what I got so far:
using namespace System::Security::Cryptography; using namespace System::IO; private: array<unsigned char>^ AES_Encrypt(array<unsigned char>^ bytesToBeEncrypted, array<unsigned char>^ passwordBytes) { array<unsigned char>^ encryptedBytes = nullptr; // Set your salt here, change it to meet your flavor: // The salt bytes must be at least 8 bytes. array<unsigned char>^ saltBytes = gcnew array<unsigned char>(8) { 1, 2, 3, 4, 5, 6, 7, 8 }; MemoryStream^ ms = gcnew MemoryStream(); RijndaelManaged^ AES = gcnew RijndaelManaged(); auto cs = gcnew CryptoStream(ms, AES->CreateEncryptor(), CryptoStreamMode::Write); try { try { AES->KeySize = 256; AES->BlockSize = 128; AES->Padding = System::Security::Cryptography::PaddingMode::Zeros; auto key = gcnew Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); AES->Key = key->GetBytes(AES->KeySize / 8); AES->IV = key->GetBytes(AES->BlockSize / 8); AES->Mode = CipherMode::CBC; try { cs->Write(bytesToBeEncrypted, 0, bytesToBeEncrypted->Length); cs->Close(); } finally { if (cs != nullptr) delete cs; } encryptedBytes = ms->ToArray(); } finally { if (AES != nullptr) delete AES; } } finally { if (ms != nullptr) delete ms; } return encryptedBytes; } private: array<unsigned char>^ AES_Decrypt(array<unsigned char>^ bytesToBeDecrypted, array<unsigned char>^ passwordBytes) { array<unsigned char>^ decryptedBytes = nullptr; // Set your salt here, change it to meet your flavor: // The salt bytes must be at least 8 bytes. array<unsigned char>^ saltBytes = gcnew array<unsigned char>(8) { 1, 2, 3, 4, 5, 6, 7, 8 }; MemoryStream^ ms = gcnew MemoryStream(); RijndaelManaged^ AES = gcnew RijndaelManaged(); auto cs = gcnew CryptoStream(ms, AES->CreateDecryptor(), CryptoStreamMode::Write); try { try { AES->KeySize = 256; AES->BlockSize = 128; AES->Padding = System::Security::Cryptography::PaddingMode::Zeros; auto key = gcnew Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); AES->Key = key->GetBytes(AES->KeySize / 8); AES->IV = key->GetBytes(AES->BlockSize / 8); AES->Mode = CipherMode::CBC; try { cs->Write(bytesToBeDecrypted, 0, bytesToBeDecrypted->Length); cs->Close(); } finally { if (cs != nullptr) delete cs; } decryptedBytes = ms->ToArray(); } finally { if (AES != nullptr) delete AES; } } finally { if (ms != nullptr) delete ms; } return decryptedBytes; } //Encrypt String private: System::String^ EncryptText(System::String^ input, System::String^ password) { // Get the bytes of the string array<unsigned char>^ bytesToBeEncrypted = System::Text::Encoding::UTF8->GetBytes(input); array<unsigned char>^ passwordBytes = System::Text::Encoding::UTF8->GetBytes(password); // Hash the password with SHA256 passwordBytes = SHA256::Create()->ComputeHash(passwordBytes); array<unsigned char>^ bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes); System::String^ result = Convert::ToBase64String(bytesEncrypted); return result; } //Decrypt String private: System::String^ DecryptText(System::String^ input, System::String^ password) { // Get the bytes of the string array<unsigned char>^ bytesToBeDecrypted = Convert::FromBase64String(input); array<unsigned char>^ passwordBytes = System::Text::Encoding::Encoding::UTF8->GetBytes(password); passwordBytes = SHA256::Create()->ComputeHash(passwordBytes); array<unsigned char>^ bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes); System::String^ result = System::Text::Encoding::Encoding::UTF8->GetString(bytesDecrypted); return result; }加密工作正常,我做了一个简单的测试,将其更新为标签:
The Encryption is working fine, I did a simple test to update it into a label:
private: System::Void Button1_Click(System::Object^ sender, System::EventArgs^ e) { System::String^ temp = EncryptText(this->textBox1->Text, "batman"); this->label1->Text = temp; this->label2->Text = DecryptText(temp, "batman"); }但是解密时遇到一些问题,我设法 CryptographicException :
However I'm getting some issues when Decrypting it, I managed to catch the CryptographicException:
'EncryptionTest.exe' (Win32): Loaded 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\diasymreader.dll'. System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) at System.Security.Cryptography.CryptoStream.FlushFinalBlock() at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) at System.IO.Stream.Close() at EncryptionTest.MyForm.AES_Decrypt(Byte[] bytesToBeDecrypted, Byte[] passwordBytes) in C:\Users\[username]\source\repos\EncryptionTest\EncryptionTest\MyForm.h:line 198第198行是到最后一个尝试块: cs-> Close();
Line 198 refers to the last try block: cs->Close();
我尝试了所有可用的填充,例如 ANSIX923 , ISO10126 , PKCS7 ,但没有帮助了。
I tried all kind of available paddings though, such as ANSIX923, ISO10126, PKCS7, but none helped. I'd appreciate any help you're able to provide.
推荐答案在C ++ / CLI中设置AES参数太晚了,我将不胜感激。代码,即之后创建加密器和解密器。因此,自动生成的随机密钥和 RijndaelManaged 实例的IV和其他默认值(PKCS7,CBC等)用于创建加密器和解密器(而不是密钥)。和IV使用 Rfc2898DeriveBytes 实例以及其余指定值生成)。由于随机性,密钥和IV对于加密和解密是不同的,因此解密失败。因此,必须在创建加密器和解密器之前设置AES参数,即正确的顺序是(使用加密示例):
The AES parameters are set too late in the C++/CLI code, that is, after the creation of encryptor and decryptor. Therefore, an automatically generated random key and IV and other default values (PKCS7, CBC, etc.) of the RijndaelManaged instance are used to create encryptor and decryptor (instead of the key and IV generated with the Rfc2898DeriveBytes instance and the rest of the specified values). Because of the randomness, key and IV are different for encryption and decryption and thus decryption fails. Therefore, the AES parameters must be set before encryptor and decryptor are created, i.e. the correct order is (using the example of encryption):
AES->KeySize = 256; AES->BlockSize = 128; AES->Mode = CipherMode::CBC; AES->Padding = System::Security::Cryptography::PaddingMode::Zeros; auto key = gcnew Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); AES->Key = key->GetBytes(AES->KeySize / 8); AES->IV = key->GetBytes(AES->BlockSize / 8); auto cs = gcnew CryptoStream(ms, AES->CreateEncryptor(), CryptoStreamMode::Write);顺便说一句,密钥大小,块大小和模式的值对应于默认值。填充的默认值为PKCS7,与注释中提到的零填充相比,填充是更可靠的填充。
By the way, the values for key size, block size and mode correspond to the default values. The default value for padding is PKCS7, which is the more reliable padding compared to zero padding, as already noted in the comments.
更多推荐
C ++ / CLI AES 256位加密
发布评论