簡體   English   中英

C++/CLI AES 256 位加密

[英]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();

我嘗試了所有可用的填充,例如ANSIX923ISO10126PKCS7 ,但沒有任何幫助。 如果您能提供任何幫助,我將不勝感激。

AES 參數在 C++/CLI 代碼中設置得太晚,即encryptorDecryptor的創建之后 因此, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM