解密AES更改最后一个字母

时间:2019-12-09 19:51:51

标签: encryption aes

我需要加密客户端应用程序中的某些数据,并稍后在服务器应用程序中进行验证。 我假设消息是解密的,那么它是来自有效的客户端,因为密钥是创建有效的加密字符串所必需的。

我正在使用MSDN https://docs.microsoft.com/pt-br/dotnet/api/system.security.cryptography.aes?view=netframework-4.8中的AES实现

我选择AES是因为在我的测试中它生成了一个短字符串。对我来说这是一个重要的问题。

public static void Main()
    {
        string original = "message to secure";

        using (Aes myAes = Aes.Create())
        {
            myAes.Key = Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODw=="); 

            byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

            var encryptedString = Convert.ToBase64String(encrypted);

            string roundtrip = DecryptStringFromBytes_Aes(Convert.FromBase64String(encryptedString), myAes.Key, myAes.IV);

            Console.WriteLine("Encrypted: " + encryptedString); 
            Console.WriteLine("Decrypted: " + roundtrip); 
        }

        Console.ReadKey();
    }

    static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
    { 
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");
        byte[] encrypted;

        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;

            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }

        return encrypted;
    }

    static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
    { 
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");

        string plaintext = null;

        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
        }

        return plaintext;
    }

但是我注意到,如果最后一个字符发生变化(等号之前),则该字符串将被解密,因为没有任何变化。

例如:

HdPAmfHTxkMmj8D3Ve​​lWjH2A8iGm6gnzzPYGNT5NR14 =已生成,我将其更改为HdPAmfHTxkMmj8D3Ve​​lWjH2A8iGm6gnzzPYGNT5NR1 5 =并得到了相同的结果。

有人可以指导我如何保证如果生成的字符串被更改了吗?

1 个答案:

答案 0 :(得分:1)

所罗门发表的所有评论或多或少都触及了头。

  

我假设消息是解密的,那么它是来自有效的客户端的,因为密钥是创建有效的加密字符串所必需的。

这个基本假设实际上是错误的。在许多情况下(未经身份验证的操作模式),即使密文已被修改,解密也可以成功进行-导致明文与原始加密的明文不同。

回想一下,AES是一种分组密码。它将一个128位的块转换为另一个128位的块。唯一的其他变量是所使用的密钥以及操作(例如加密或解密)。自从某些先前的操作以来,没有机制可以检测传入的128位块是否已被修改-AES对此不了解。它只是一个键转换函数。

为避免此问题,请使用GCM等经过身份验证的操作模式,或使用HMAC。有关在C#中使用GCM的示例,请参见this repository中的示例。


关于第二个问题:

  

但是我注意到,如果最后一个字符发生变化(等号之前),则该字符串将被解密,因为没有任何变化。

从技术上说,什么都没改变-这是一个“功能”。每个base64字符代表6位原始数据。这意味着,除非您的密文长度可被8和6整除,否则会有“剩余”位。请参阅下面的示例,其中我们对16位进行编码:

Raw                      : { 0x00, 0x01 }
Binary                   : 00000000 00000001

Base64                   : AAE=
Binary (6 Digit Grouping): 000000 000000 000100
Binary (8 Digit Grouping): 00000000 00000001 00
                                             ^^ these bits are irrelevant

本质上,没有什么可担心的。