[英]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.