简体   繁体   English

C++/CLI AES 256 位加密

[英]C++/CLI AES 256-bit Encryption

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:到目前为止,我发现的大多数示例和问题仅适用于 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;
}

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");
    }

However I'm getting some issues when Decrypting it, I managed to catch the CryptographicException :但是我在解密时遇到了一些问题,我设法捕获了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

Line 198 refers to the last try block: cs->Close();第 198 行引用了最后一个 try 块: cs->Close();

I tried all kind of available paddings though, such as ANSIX923 , ISO10126 , PKCS7 , but none helped.我尝试了所有可用的填充,例如ANSIX923ISO10126PKCS7 ,但没有任何帮助。 I'd appreciate any help you're able to provide.如果您能提供任何帮助,我将不胜感激。

The AES parameters are set too late in the C++/CLI code, that is, after the creation of encryptor and decryptor . AES 参数在 C++/CLI 代码中设置得太晚,即encryptorDecryptor的创建之后 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).因此, RijndaelManaged实例自动生成的随机密钥和IV 以及其他默认值(PKCS7、CBC 等)用于创建加密器和解密器(而不是使用Rfc2898DeriveBytes实例生成的密钥和IV 以及指定的其余部分)值)。 Because of the randomness, key and IV are different for encryption and decryption and thus decryption fails.由于随机性,加密和解密的密钥和IV不同,因此解密失败。 Therefore, the AES parameters must be set before encryptor and decryptor are created, ie the correct order is (using the example of encryption):因此,必须在创建加密器和解密器之前设置 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);

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.填充的默认值是 PKCS7,与零填充相比,它是更可靠的填充,如注释中所述。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM