[英]C++/CLI AES 256-bit Encryption
到目前为止,我发现的大多数示例和问题仅适用于 C#,但是我正在尝试将以下 C# 代码复制到 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;
}
这是我到目前为止得到的:
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;
}
加密工作正常,我做了一个简单的测试将其更新为标签:
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
:
'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 行引用了最后一个 try 块: cs->Close();
我尝试了所有可用的填充,例如ANSIX923
、 ISO10126
、 PKCS7
,但没有任何帮助。 如果您能提供任何帮助,我将不胜感激。
AES 参数在 C++/CLI 代码中设置得太晚,即在encryptor和Decryptor的创建之后。 因此, RijndaelManaged
实例自动生成的随机密钥和IV 以及其他默认值(PKCS7、CBC 等)用于创建加密器和解密器(而不是使用Rfc2898DeriveBytes
实例生成的密钥和IV 以及指定的其余部分)值)。 由于随机性,加密和解密的密钥和IV不同,因此解密失败。 因此,必须在创建加密器和解密器之前设置 AES 参数,即正确的顺序是(以加密为例):
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,与零填充相比,它是更可靠的填充,如注释中所述。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.