Szyfry blokowe w C#

0

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:

  1. Liczba od 8 do 64, podzielna przez 8 - wynik zawsze taki sam niezależnie od wartości liczby.
  2. Liczba powyżej 64, podzielna przez 8 - na Linux "CFB with Feedbaack > 64 bits", na Windows "Złe dane".
  3. 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?

0

Ten kod działa na Ideone, ale tam też jest ten sam błąd. https://ideone.com/ftAaHC

0

Zrobiłem jeszcze następującą próbę:

Na maszynie wirtualnej z Windows 10 mam .NET 5.0 i w nim skompilowałem powyższy kod. Okazuje się, że tryb CFB działa tylko dla wielkosci sprzężenia 8 i 128, przy czym sprzężenie o wielkości 128 nie działało w .NET 4.8, za to w .NET 5.0 działa i daje taki sam rezultat, jak ECB i OFB, czyli to, co chciałem.

Natomiast OFB nie działa, daje komunikat "Specified cipher mode is not valid for this algorithm".

Reasumując, przejście na .NET rozwiązuje problem z CFB, ale z OFB wciąż jest problem. Jak zmusić OFB do pracy, ale nie poprzez własną implementację tego trybu?

1 użytkowników online, w tym zalogowanych: 0, gości: 1