Właśnie robię sobie eksperymenty z szyframi blokowymi w Java i C#. W Java osiągnąłem zamierzony skutek, a w C# mam problem. Udało mi się zrobić "na piechotę", ale mi chodzi o to, jak użyć tego, co oferuje .NET 4.8 (nie Core i nie >= 5.0).
W obu językach zrealizowałem szyfrowanie AES w trybie ECB i CBC, programy w obu językach działają identycznie.
Chodzi o szyfry w trybach CFB i OFB, które teoretycznie są obsługiwane, ale z nimi mam problem.
Można w moment znaleźć, na czym polegają poszczególne tryby szyfrowania: http://www.crypto-it.net/pl/teoria/tryby-szyfrow-blokowych.html
Do testów szyfruję ciąg 16-bajtowy bez paddingu, który odpowiada jednemu blokowi. Losujemy klucz i IV (oba są również 16-bajtowe).
W trybie ECB nie używa się IV, a zaszyfruję ciąg wylosowany jako IV i zapamiętam szyfrogram jako wynik. W pozostałych trybach użyję przygotowanego IV, a zaszyfruję ciąg składających się z samych zer. Na podstawie analizy schematów można wywnioskować, że w tych trzech przypadkach powinienem uzyskać ten sam wynik, ponieważ blok tekstu jawnego jest podstawiany do funkcji XOR, to w tym przypadku nie zmienia danych wejściowych ani wyjściowych samej funkcji szyfrującej. W języku Java taki efekt uzyskałem dla wszystkich trybów.
Testy robię zarówno na Ubuntu 10.04, jak i Windows 10 i w obu przypadkach są dokładnie te same problemy, tylko komunikaty błędów inne. W CBC faktycznie wynik jest taki, jak w ECB, natomiast w CFB zgadza się tylko pierwszy bajt, a reszta jest zupełnie inna. W OFB w ogóle nie mogę zrealizować szyfrowania (komunikat "OFB isn't supported by the framework" w Linux lub "Wystąpił błąd wewnętrzny" w Windows).
Doczytałem, że do CFB jest parametr FeedbackSize
, który w praktyce nic nie daje. Działa następująco:
- Liczba od 8 do 64, podzielna przez 8 - wynik zawsze taki sam niezależnie od wartości liczby.
- Liczba powyżej 64, podzielna przez 8 - na Linux "CFB with Feedbaack > 64 bits", na Windows "Złe dane".
- Każda inna liczba - na Linux "Specified feedback size is invalid", na Windows "Określony rozmiar danych sprzężenia zwrotnego jest nieprawidłowy".
Zamieszczam kod programu testowego
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace UniCryptTester
{
class MainClass
{
static Random Rnd = new Random();
public static string PrintData(byte[] Data)
{
StringBuilder S = new StringBuilder();
for (int i = 0; i < Data.Length; i++)
{
S.Append(Data[i].ToString("X").PadLeft(2, '0'));
S.Append(" ");
}
return S.ToString();
}
public static void CipherTest()
{
int BlockSize = 16;
byte[] Zero = new byte[BlockSize];
byte[] DataECB = new byte[BlockSize];
byte[] DataCBC = new byte[BlockSize];
byte[] DataCFB = new byte[BlockSize];
byte[] DataOFB = new byte[BlockSize];
byte[] CipherKey = new byte[16];
byte[] CipherIV = new byte[BlockSize];
Rnd.NextBytes(CipherKey);
Rnd.NextBytes(CipherIV);
for (int i = 0; i < BlockSize; i++)
{
Zero[i] = 0;
DataECB[i] = 0;
DataCBC[i] = 0;
DataCFB[i] = 0;
DataOFB[i] = 0;
}
Console.WriteLine("Key: " + PrintData(CipherKey));
Console.WriteLine("IV: " + PrintData(CipherIV));
try
{
SymmetricAlgorithm TestECB = new AesCryptoServiceProvider();
TestECB.Mode = CipherMode.ECB;
TestECB.Padding = PaddingMode.None;
ICryptoTransform TransECB = TestECB.CreateEncryptor(CipherKey, Zero);
MemoryStream StrECB = new MemoryStream();
CryptoStream CStrECB = new CryptoStream(StrECB, TransECB, CryptoStreamMode.Write);
CStrECB.Write(CipherIV, 0, BlockSize);
CStrECB.FlushFinalBlock();
StrECB.Seek(0, SeekOrigin.Begin);
StrECB.Read(DataECB, 0, BlockSize);
CStrECB.Close();
StrECB.Close();
Console.WriteLine("ECB: " + PrintData(DataECB));
}
catch (Exception E)
{
Console.WriteLine("ECB: " + E.Message);
}
try
{
SymmetricAlgorithm TestCBC = new AesCryptoServiceProvider();
TestCBC.Mode = CipherMode.CBC;
TestCBC.Padding = PaddingMode.None;
ICryptoTransform TransCBC = TestCBC.CreateEncryptor(CipherKey, CipherIV);
MemoryStream StrCBC = new MemoryStream();
CryptoStream CStrCBC = new CryptoStream(StrCBC, TransCBC, CryptoStreamMode.Write);
CStrCBC.Write(Zero, 0, BlockSize);
CStrCBC.FlushFinalBlock();
StrCBC.Seek(0, SeekOrigin.Begin);
StrCBC.Read(DataCBC, 0, BlockSize);
CStrCBC.Close();
StrCBC.Close();
Console.WriteLine("CBC: " + PrintData(DataCBC));
}
catch (Exception E)
{
Console.WriteLine("CBC: " + E.Message);
}
try
{
SymmetricAlgorithm TestCFB = new AesCryptoServiceProvider();
TestCFB.Mode = CipherMode.CFB;
TestCFB.Padding = PaddingMode.None;
ICryptoTransform TransCFB = TestCFB.CreateEncryptor(CipherKey, CipherIV);
MemoryStream StrCFB = new MemoryStream();
CryptoStream CStrCFB = new CryptoStream(StrCFB, TransCFB, CryptoStreamMode.Write);
CStrCFB.Write(Zero, 0, BlockSize);
CStrCFB.FlushFinalBlock();
StrCFB.Seek(0, SeekOrigin.Begin);
StrCFB.Read(DataCFB, 0, BlockSize);
CStrCFB.Close();
StrCFB.Close();
Console.WriteLine("CFB: " + PrintData(DataCFB));
}
catch (Exception E)
{
Console.WriteLine("CFB: " + E.Message);
}
for (int i = 0; i <= 128; i++)
{
try
{
SymmetricAlgorithm TestCFB = new AesCryptoServiceProvider();
TestCFB.Mode = CipherMode.CFB;
TestCFB.Padding = PaddingMode.None;
TestCFB.FeedbackSize = i;
ICryptoTransform TransCFB = TestCFB.CreateEncryptor(CipherKey, CipherIV);
MemoryStream StrCFB = new MemoryStream();
CryptoStream CStrCFB = new CryptoStream(StrCFB, TransCFB, CryptoStreamMode.Write);
CStrCFB.Write(Zero, 0, BlockSize);
CStrCFB.FlushFinalBlock();
StrCFB.Seek(0, SeekOrigin.Begin);
StrCFB.Read(DataCFB, 0, BlockSize);
CStrCFB.Close();
StrCFB.Close();
Console.WriteLine("CFB" + i.ToString().PadRight(3) + ": " + PrintData(DataCFB));
}
catch (Exception E)
{
Console.WriteLine("CFB" + i.ToString().PadRight(3) + ": " + E.Message);
}
}
try
{
SymmetricAlgorithm TestOFB = new AesCryptoServiceProvider();
TestOFB.Mode = CipherMode.OFB;
TestOFB.Padding = PaddingMode.None;
ICryptoTransform TransOFB = TestOFB.CreateEncryptor(CipherKey, CipherIV);
MemoryStream StrOFB = new MemoryStream();
CryptoStream CStrOFB = new CryptoStream(StrOFB, TransOFB, CryptoStreamMode.Write);
CStrOFB.Write(Zero, 0, BlockSize);
CStrOFB.FlushFinalBlock();
StrOFB.Seek(0, SeekOrigin.Begin);
StrOFB.Read(DataOFB, 0, BlockSize);
CStrOFB.Close();
StrOFB.Close();
Console.WriteLine("OFB: " + PrintData(DataOFB));
}
catch (Exception E)
{
Console.WriteLine("OFB: " + E.Message);
}
}
public static void Main(string[] args)
{
CipherTest();
Console.ReadLine();
return;
}
}
}
Co tu jest jeszcze nie tak i jak zmusić CFB i OFB do prawidłowego działania?