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

查看:69
本文介绍了C ++ / CLI AES 256位加密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我发现的大多数示例和问题仅适用于C#,但是我试图将以下C#代码重现为C ++ / CLI:

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:

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

但是解密时遇到一些问题,我设法 CryptographicException

However I'm getting some issues when Decrypting it, I managed to catch the 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行是到最后一个尝试块: cs-> Close();

Line 198 refers to the last try block: cs->Close();

我尝试了所有可用的填充,例如 ANSIX923 ISO10126 PKCS7 ,但没有帮助了。

I tried all kind of available paddings though, such as ANSIX923, ISO10126, PKCS7, but none helped. I'd appreciate any help you're able to provide.

推荐答案

在C ++ / CLI中设置AES参数太晚了,我将不胜感激。代码,即之后创建加密器解密器。因此,自动生成的随机密钥和 RijndaelManaged 实例的IV和其他默认值(PKCS7,CBC等)用于创建加密器和解密器(而不是密钥)。和IV使用 Rfc2898DeriveBytes 实例以及其余指定值生成)。由于随机性,密钥和IV对于加密和解密是不同的,因此解密失败。因此,必须在创建加密器和解密器之前设置AES参数,即正确的顺序是(使用加密示例):

The AES parameters are set too late in the C++/CLI code, that is, after the creation of encryptor and decryptor. 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). Because of the randomness, key and IV are different for encryption and decryption and thus decryption fails. Therefore, the AES parameters must be set before encryptor and decryptor are created, i.e. the correct order is (using the example of encryption):

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,与注释中提到的零填充相比,填充是更可靠的填充。

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.

这篇关于C ++ / CLI AES 256位加密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆