解密 AES 更改最后一个字母
Decrypt AES changing the last letter
我需要在客户端应用程序中加密一些数据,稍后在服务器应用程序中对其进行验证。
我假设如果消息被解密,那么它来自有效的客户端,因为密钥是创建有效的加密字符串所必需的。
我正在使用来自 MSDN 的 AES 实现 https://docs.microsoft.com/pt-br/dotnet/api/system.security.cryptography.aes?view=netframework-4.8
我选择 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;
}
但我注意到,如果最后一个字符(等号之前)发生变化,则字符串会被解密,因为没有任何变化。
例如:
HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR14= 已生成,我将其更改为 HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR15= 并得到了相同的结果。
有人可以指导我如何保证生成的字符串被更改后无法解密吗?
Solomon 的所有评论或多或少都一针见血。
I'm assuming that if the message was decrypted then it was from a valid client, since the key is necessary to create a valid crypted string.
这个基本假设实际上是错误的。在许多情况下(在未经身份验证的操作模式下),即使密文已被修改,解密也可以成功 - 导致明文与最初加密的明文不同。
回想一下,AES 是一种分组密码。它将一个 128 位块转换为另一个 128 位块。唯一的其他变量是使用的密钥和操作(例如加密或解密)。没有机制来检测传入的 128 位块是否自某些先前操作以来已被修改——AES 不知道这一点。它只是一个键控转换功能。
为避免此问题,请使用经过身份验证的操作模式(如 GCM)或使用 HMAC。有关在 C# 中使用 GCM 的示例,请参阅 this repository 中的示例。
关于第二期:
But I noticed that if there is a change in the last caracter (before equals sign) the string is decrypted as nothing was changed.
从技术上讲,没有任何变化 - 这是 "feature"。每个 base64 字符代表原始数据的 6 位。这意味着,除非您的密文长度可以被 8 和 6 整除,否则会有位 "left over"。请参阅下面我们编码 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
本质上,没什么好担心的。
我需要在客户端应用程序中加密一些数据,稍后在服务器应用程序中对其进行验证。 我假设如果消息被解密,那么它来自有效的客户端,因为密钥是创建有效的加密字符串所必需的。
我正在使用来自 MSDN 的 AES 实现 https://docs.microsoft.com/pt-br/dotnet/api/system.security.cryptography.aes?view=netframework-4.8
我选择 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;
}
但我注意到,如果最后一个字符(等号之前)发生变化,则字符串会被解密,因为没有任何变化。
例如:
HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR14= 已生成,我将其更改为 HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR15= 并得到了相同的结果。
有人可以指导我如何保证生成的字符串被更改后无法解密吗?
Solomon 的所有评论或多或少都一针见血。
I'm assuming that if the message was decrypted then it was from a valid client, since the key is necessary to create a valid crypted string.
这个基本假设实际上是错误的。在许多情况下(在未经身份验证的操作模式下),即使密文已被修改,解密也可以成功 - 导致明文与最初加密的明文不同。
回想一下,AES 是一种分组密码。它将一个 128 位块转换为另一个 128 位块。唯一的其他变量是使用的密钥和操作(例如加密或解密)。没有机制来检测传入的 128 位块是否自某些先前操作以来已被修改——AES 不知道这一点。它只是一个键控转换功能。
为避免此问题,请使用经过身份验证的操作模式(如 GCM)或使用 HMAC。有关在 C# 中使用 GCM 的示例,请参阅 this repository 中的示例。
关于第二期:
But I noticed that if there is a change in the last caracter (before equals sign) the string is decrypted as nothing was changed.
从技术上讲,没有任何变化 - 这是 "feature"。每个 base64 字符代表原始数据的 6 位。这意味着,除非您的密文长度可以被 8 和 6 整除,否则会有位 "left over"。请参阅下面我们编码 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
本质上,没什么好担心的。