繁体   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