Я унаследовал некоторый устаревший код, изначально созданный в .NET 4.6, который выглядит следующим образом:
static void Main(string[] args)
{
Console.WriteLine(Encrypt("B9519163"));
}
public static string Encrypt(string text)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes("WiPB5/SrFvq6fu93"); // Dummy IV value
aes.Key = Encoding.UTF8.GetBytes("I9qKwOuZpWtt64Nl"); // Dummy key
aes.Mode = CipherMode.CFB;
aes.Padding = PaddingMode.PKCS7;
byte[] src = Encoding.Unicode.GetBytes(text);
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
return Convert.ToBase64String(dest);
}
}
Я создаю новое настольное приложение в .NET 8, которому необходимо расшифровать данные, созданные устаревшим кодом, и оно не работает из-за, по-видимому, несовместимости между тем, как работает шифрование/дешифрование aes между различными версиями .NET.
В .NET 4.6 массив байтов dest, возвращаемый функцией encrypt.TransformFinalBlock(), имеет длину 32 байта. Согласно моему пониманию документации PKCS7 оставшаяся часть 32-байтового массива со всеми дополненными байтами будет иметь то же значение. Однако дополненные байты заполняются, казалось бы, случайными значениями. Вот они, как видно в отладчике: {245, 51, 180, 226, 20, 222, 24, 183, 72, 152, 102, 17, 219, 215, 37, 233, 123, 119, 154, 204, 218, 52, 139, 81, 119. , 59, 42, 206, 13, 185, 247, 122}
Я не уверен, правильно ли я понимаю, как это должно работать.
Когда я запускаю тот же код в .NET 8, он возвращает 17-байтовый массив, который не кратен размеру блока. Первые 16 байтов такие же, как и ожидалось. {245, 51, 180, 226, 20, 222, 24, 183, 72, 152, 102, 17, 219, 215, 37, 233, 106}
Как я могу сделать свой код .NET 8 обратно совместимым для расшифровки значений, сгенерированных устаревшим кодом? Любая помощь в этом будет принята с благодарностью! Заранее спасибо.
У меня нет возможности протестировать .net 4.6, поэтому я могу только предполагать: попробуйте установить aes.FeedbackSize = 128 в .net 8.0 и посмотрите, будут ли результаты такими же.
@PresidentJamesK.Polk — я попробовал установить aes.FeedbackSize = 128 в .NET 8. Он вернул 32-байтовый массив в dest, но в нем были совершенно другие значения по сравнению с версией .NET 4.6. В этом смысле это несовместимо и не помогает в достижении моей цели.
Я нашел ответ здесь на первый вопрос о, казалось бы, случайных числах в дополненных 32-байтах, возвращаемых в .NET 4.6. Остается вопрос, почему .NET 8 не дает такого же результата.
Оба кода используют CFB8. В .NET Framework обычно требуется заполнение (но не для примера данных B9519163, поскольку его размер составляет всего 16 байт из-за кодировки Unicode (=UTF16LE). На самом деле это ошибка, поскольку CFB — это режим потокового шифрования, не требующий заполнения. В .NET 8 заполнение (правильно) не требуется и поэтому должно быть явно установлено значение None. Если этого не происходит, открытый текст дополняется значением 0x01 (по какой-либо причине).
Чтобы воспроизвести зашифрованный текст (из-за неправильного заполнения .NET Framework) с помощью .NET 8, заполнение в .NET 8 должно быть явно отключено (None), а открытый текст должен быть явно дополнен PKCS#7 (для последнего вам нужна собственная реализация заполнения PKCS#7, вы можете найти несколько в Интернете).
@Topaco - большое спасибо за это понимание. Что-то в этом роде я вижу здесь. Я пытаюсь реализовать ваше предложение создать собственную реализацию заполнения PKCS#7 с использованием BouncyCastle, но не понимаю, как сделать ее полностью совместимой с тем, что ошибочно генерируется .NET Framework.





Спасибо @Topaco за то, что помогли мне найти решение. Ваша помощь очень ценится. Вот что мне удалось решить эту проблему и обеспечить обратную совместимость с .NET Framework.
static void Main(string[] args)
{
Console.WriteLine(Encrypt("B9519163"));
}
public static string Encrypt(string text)
{
var aes = Aes.Create();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes("WiPB5/SrFvq6fu93");
aes.Key = Encoding.UTF8.GetBytes("I9qKwOuZpWtt64Nl");
aes.Mode = CipherMode.CFB;
aes.Padding = PaddingMode.None;
byte[] src = Encoding.Unicode.GetBytes(text);
byte[] paddedSrc = PerformPKCS7Padding(src, 16);
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(paddedSrc, 0, paddedSrc.Length);
return Convert.ToBase64String(dest);
}
}
static byte[] PerformPKCS7Padding(byte[] input, int blockSize)
{
int paddingLength = blockSize - (input.Length % blockSize);
byte[] paddedData = new byte[input.Length + paddingLength];
// Copy original data
Array.Copy(input, paddedData, input.Length);
// Add padding bytes
for (int i = input.Length; i < paddedData.Length; i++)
{
paddedData[i] = (byte)paddingLength;
}
return paddedData;
}
Это открытый текст, который дополнен, и вы никогда не увидите заполнения открытого текста, если не приложите все усилия, чтобы попытаться взглянуть на него.