Symetrické šifrování AES/Rijndael v .NET

Současným průmyslovým standardem pro symetrické šifrování je algoritmus AES (Advanced Encryption Standard), nazývaný též Rijndael. Je k dispozici i jako součást Microsoft .NET Frameworku a je k dispozici všem programátorům.

Rijndael nebo AES?

Jako většina šifrovacích algoritmů, i Rijndael odvozuje svůj název od jmen svých autorů: Joan Daemen a Vincent Rijmen. Rijndael je původní název algoritmu, který byl v roce 2001 přijat americkým Národním institutem pro standardizaci a technologie (NIST) pod názvem Advanced Encryption Standard jako standard pro šifrování v rámci amerických vládních úřadů a nazván. V této roli nahradil algoritmus DES (Data Encryption Standard), považovaný již za zastaralý. V praxi tedy názvy "Rijdael" a "AES" odkazují na totéž.

Symetrické a asymetrické algoritmy

V případě Rijndael/AES jde o symetrický algoritmus, což znamená, že používá sdílený klíč: tentýž klíč slouží k zašifrování i dešifrování dat. Délka klíče je 128, 196 nebo 256 bitů.

Hlavní výhodou symetrického šifrování je, že je obecně velmi rychlé a dá se použít pro šifrování velkého objemu dat. Zásadní nevýhodou (pro řadu aplikací) je ale samotné použití sdíleného klíče: ten kdo data zašifroval je umí i dešifrovat a komunikují-li spolu dvě strany, je nutné, aby si klíč bezpečně předaly důvěrnou cestou. Bezpečnost šifry také závisí na kvalitě použitého klíče, musí být dostatečně komplexní a dostatečně náhodný, jinak je šifra snadno prolomitelná.

Oproti tomu asymetrické šifrování (jako např. RSA) využívá dvou klíčů: veřejného (ten se použije pro zašifrování dat) a soukromého (používá se pro dešifrování), přičemž tyto klíče mohou být od sebe odděleny. Asymetrické algoritmy jsou ale velmi pomalé a prakticky nepoužitelné pro šifrování velkého objemu dat.

V praxi se často obě metody kombinují: vlastní data se zašifrují pomocí symetrického algoritmu (AES) a použitý jednorázový klíč (který je dosti malý) se zašifruje pomocí asymetrického algoritmu (RSA). Na toto použití se podíváme v samostatném článku, v tomto se zaměříme na vlastní použití symetrického algoritmu.

I samostatné použití symetrického šifrovacího algoritmu má samozřejmě svůj význam. V případě že ho dokážete bezpečně uschovat/předat a oddělení šifrovacího a dešifrovacího klíče pro vás nemá význam, protože z hlediska architektury systému by stejně obě části musely být uschovány na tomtéž místě.

Modes of operation, IV, padding

Rijndael je blokový šifrovací algoritmus. Je aplikován na data s pevně danou délkou - v tomto konkrétním případě 128 bitů. Pokud jsou šifrovaná data delší, zpracovávají se po jednotlivých blocích. Pokud jsou data kratší (typicky v případě posledního bloku se zbytkem dat), je potřeba je doplnit na odpovídající délku.

Onomu doplnění na odpovídající délku se říká "padding". Existuje pro něj několik algoritmů, od primitivního doplnění nulami po složitější schémata. Nejčastěji se používá mechanismus podle PKCS #7 (podrobnější popis najdete např v RFC2315, ale nemusí vás příliš zajímat, tento mechanismus je vestavěn v .NET Frameworku).

Naivní implementace blokové šifry by spočívala v tom, že by se šifra aplikovala na jednotlivé bloky tak, jak jdou za sebou. Tomuto postupu se říká ECB (Electronic Codebook) a jeho použití se nedoporučuje. Jeho výsledkem totiž je, že stejné bloky otevřeného textu budou zašifrovány vždy stejně. Vzhledem k poměrně malé velikosti jednotlivých bloků to může znamenat, že ve výsledné zprávě budou patrná stejná schémata jako v původním dokumentu (viz např. vizuální reprezentace na Wikipedii).

V praxi se tedy používají takové algoritmy, které toto nebezpečí odstraňují. Nejjednodušším z nich je CBC - Cipher Block Chaining. Ten funguje tak, že před zašifrováním se odpovídající blok otevřeného textu XORuje předcházejícím blokem zašifrovaného textu. To znamená, že jednotlivé bloky jsou na sobě závislé, abyste dešifrovali konkrétní blok, musíte dešifrovat i všechny předchozí.

V tomto případě ale máte problém - čím XORovat první blok, když ještě nemáte k dispozici blok předchozí? Inu, použijeme osvědčenou metodu socialistických vlád: když informace nemáme, prostě si je vymyslíme. Jeden blok se vygeneruje náhodně a přidá se k zašifrovaným datům jako "nultý blok". Tomuto bloku se pak říká "inicializační vektor" (IV). Tento blok se použije k dešifrování prvního bloku a pak zahodí.

Zdrojové kódy

Šifrování dat

Nyní již máme dostatek informací k tomu, abychom pomocí třídy System.Web.Security.Cryptography.RijndaelManaged dokázali zašifrovat libovolná data. Pro pořádek poznamenávám, že vstupem i výstupem všech těchto operací je pole bajtů. Chci-li pracovat s textem, musím ho na pole bajtů převést (třeba pomocí System.Text.Encoding.UTF8.GetBytes) a výsledek zakódovat, třeba pomocí Base64.

// Vytvo it instanci AES/Rijdael algoritmu

System.Security.Cryptography.RijndaelManaged aes = new System.Security.Cryptography.RijndaelManaged();

 

// Nastavit CBC block mode

aes.Mode = System.Security.Cryptography.CipherMode.CBC;

 

// Nastavit standardní PKCS7 padding posledního bloku

aes.Padding = System.Security.Cryptography.PaddingMode.PKCS7;

 

// Vygenerovat náhodný klíč

aes.GenerateKey();

 

// Vygenerovat inicializační vektor (IV)

aes.GenerateIV();

 

// Převést text na pole bajtů

byte[] plainData = System.Text.Encoding.UTF8.GetBytes(this.TextBoxPlaintext.Text);

 

// Zašifrovat data

byte[] cipherData;

using (System.Security.Cryptography.ICryptoTransform enc = aes.CreateEncryptor()) {

    cipherData = enc.TransformFinalBlock(plainData, 0, plainData.Length);

}

 

// Zobrazit výsledek

this.MultiViewPage.SetActiveView(this.ViewResult);

this.LiteralKey.Text = System.Convert.ToBase64String(aes.Key);

this.LiteralIV.Text = System.Convert.ToBase64String(aes.IV);

this.LiteralCiphertext.Text = System.Convert.ToBase64String(cipherData, Base64FormattingOptions.InsertLineBreaks).Replace("\r\n", "<br />");

V uvedeném kódu náhodně vygeneruji klíč (mohl bych ho samozřejmě i výslovně definovat, pokud by byl pevně zadaný) a inicializační vektor. Poté zašifruji data a vše uvedené (klíč, IV a ciphertext) zobrazím v Base64 formě uživateli. Za normálních okolností bývá IV prostě přidán na začátek šifrovaného textu, neukládá se zvlášť, ale já ho zde pro názornost vypisuji zvlášť.

Dešifrování

Dešifrování je realizováno obdobným kódem:

// Vytvořit instanci AES/Rijdael algoritmu

System.Security.Cryptography.RijndaelManaged aes = new System.Security.Cryptography.RijndaelManaged();

 

// Nastavit CBC block mode

aes.Mode = System.Security.Cryptography.CipherMode.CBC;

 

// Nastavit standardní PKCS7 padding posledního bloku

aes.Padding = System.Security.Cryptography.PaddingMode.PKCS7;

 

// Načíst klíč

aes.Key = System.Convert.FromBase64String(this.TextBoxKey.Text);

 

// Načíst IV

aes.IV = System.Convert.FromBase64String(this.TextBoxIV.Text);

 

// Načíst  šifrovaná data

byte[] cipherData = System.Convert.FromBase64String(this.TextBoxCiphertext.Text);

 

// Dešifrovat data

byte[] plainData;

using (System.Security.Cryptography.ICryptoTransform dec = aes.CreateDecryptor()) {

    plainData = dec.TransformFinalBlock(cipherData, 0, cipherData.Length);

}

string plainText = System.Text.Encoding.UTF8.GetString(plainData);

 

// Zobrazit výsledek

plainText = plainText.Replace("\r\n", "<br />");

this.MultiViewPage.SetActiveView(this.ViewResult);

this.LiteralPlaintext.Text = plainText;

Slovo závěrem

Uvědomte si prosím, že zašifrováním tajemství nezmizí. Pouze se zmenší. Místo abyste chránili velký dokument, musíte chránit jenom malý dokument - klíč. Jeho ochrana je ale to nejdůležitější - v praxi je u moderních algoritmů mnohem důležitější ochrana klíče, než jeho délka. K tomuto tématu se ještě vrátím v dalších článcích.

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET