Advanced Encryption Standard (AES, „подобрен стандарт за шифриране“) е стандарт за шифриране, приет през 2002 г. от американския NIST. Към 2009 г. това е един от най-широко използваните криптографски алгоритми. Стандартът включва приложение на алгоритъма Rijndael, разработен през 1998 г. от белгийските криптографи Винсент Реймен и Жоан Дамен, като използва шифроващи ключове с три дължини: 128, 192 или 256 бита.
Дължината на шифриращия ключ определя броя повторения на криптирането, тоест броят трансформации, необходими за превръщането на входа, наречен plaintext (прост текст), в изходния резултат, наречен ciphertext (шифрован текст). За различните дължини на ключовете повторенията са: 10 цикъла за 128-битов ключ. 12 цикъла за 192-битов ключ. 14 цикъла за 256-битов ключ.
Всеки цикъл се състои от няколко последователни стъпки. Стъпките се изграждат от пет подобни една на друга процедури, като една от тях зависи от криптиращия ключ. Криптираното съдържание може да се трансформира в оригиналния текст, като се използва същият ключ и се приложат набор от обратни стъпки.
Байтовете на входното съобщение (plaintext) се записват в матрица, най-често именувана state („статус“, „положение“). Върху тази матрица се извършват криптиращите трансформации на алгоритъма Rijndael.
Криптиращ алгоритъм (128-битова версия) Криптиране със 128-битов ключ се реализира чрез следния алгоритъм: PROCEDURE Cipher128(in:byte, out:byte, w:word) BEGIN state[4,4]:byte state = in AddRoundKey(state, w[0, 3]) FOR round = 1 to 10 SubBytes(state) ShiftRows(state) MixColumns(state) AddRoundKey(state, w[round*4, (round+1)*4-1]) END FOR SubBytes(state) ShiftRows(state) AddRoundKey(state, w[40, 43]) out = state END PROCEDURE
Процедурата Key Expansion Думите в масива w (key schedule) се генерират по следния начин:
//всяка дума се състои от 4 байта, пример: w = 0f 15 71 c9
Първите четири думи (w до w) взимат стойността на криптиращия ключ. За да се изчисли w[i] се използват операции от Rijndael key schedule:
FOR i = 4 to 43 temp = w[i-1] IF i = 4, 8, 12, 16, ..., 40 THEN //кратно на 4 Извършва се операция Rotate върху temp Всеки байт се изменя (използвайки процедурата SubBytes) temp=temp XOR Rcon[i] // всеки байт от temp се събира побитово с константна стойност от Rcon END IF w[i] = w[i-4] XOR temp END FOR
Процедурата SubBytes При тази процедура всеки байт от state се изменя по следния начин (Rijndael S-box):
FOR i=0 to 7 //промяна за всеки бит от даден байт на state B[i]= B[i] XOR B[(i+4) MOD 8] XOR B[(i+5) MOD 8] XOR B[(i+6) MOD 8] XOR B[(i+7) MOD 8] + C[i] //C = 63 hex. END FOR
Процедурата ShiftRows Тази процедура променя редовете на state матрицата. Най-горният ред остава непроменен, байтовете на следващия се преместват с една позиция наляво, на по-долния с две позиции, а на последния байтовете се изместват с три позиции наляво.
Процедурата MixColumns Колоните на state матрицата се разбъркват по следния начин:
B[0,c] =2 * B[0,c] XOR 3 * B[1,c] XOR B[2,c] XOR B[3,c] B[1,c] = B[0,c] XOR 2 * B[1,c] XOR 3 * B[2,c] XOR B[3,c] B[2,c] = B[0,c] XOR B[1,c] XOR 2 * B[2,c] XOR 3 * B[3,c] B[3,c] = 3 * B[0,c] XOR B[1,c] XOR B[2,c] XOR 2 * B[3,c]
Процедурата AddRoundKey Извършва се XOR между четирите колони на state и четири 32-битови думи от key schedule.
B[0,c]=B[0,c] XOR w[k,c] B[1,c]=B[1,c] XOR w[k+1,c] B[2,c]=B[2,c] XOR w[k+2,c] B[3,c]=B[3,c] XOR w[k+3,c]
Декриптиране Декриптирането представлява извършване на криптиращите операции в обратен ред: PROCEDURE InvCipher128 (in:byte,out:byte,w:word) BEGIN state[4,4]:byte state = in AddRoundKey(state, w[40, 43]) FOR round = 9 downto 1 InvShiftRows(state) InvSubBytes(state) AddRoundKey(state, w[round*3, (round+1)*3-1]) InvMixColumns(state) END FOR InvShiftRows(state) InvSubBytes(state) AddRoundKey(state, w[0, 3]) out = state END PROCEDURE
Всяка от Inv… функциите отговаря на обърната криптираща функция. Например InvMixColumns ще има следните преобразувания върху state:
B[0,c] = 0x0e * B[0,c] XOR 0x0b * B[1,c] XOR 0x0d * B[2,c] XOR 0x09 * B[3,c] B[1,c] = 0x09 * B[0,c] XOR 0x0e * B[1,c] XOR 0x0b * B[2,c] XOR 0x0d * B[3,c] B[2,c] = 0x0d * B[0,c] XOR 0x09 * B[1,c] XOR 0x0e * B[2,c] XOR 0x0b * B[3,c] B[3,c] = 0x0b * B[0,c] XOR 0x0d * B[1,c] XOR 0x09 * B[2,c] XOR 0x0e * B[3,c]
Примерна реализация на Java Script /** * [http://people.eku.edu/styere/Encrypt/JS-AES.html#subbytes @source] */
// функция за криптиране function aes_encrypt(message,crypt_key) { var w = new Array( 44 );// деклариране на key shedule var state = new Array( 16 );// деклариране на state var round; msg=get_value(message,true); key=get_value(crypt_key,false); // генериране на "round" ключове w = key_expand( key ); state = transpose( msg ); state = AddRoundKey(state, w, 0);
for( round=1; round<10; round++ )
state = SubBytes(state, S_enc); state = ShiftRows(state); state = MixColumns(state); state = AddRoundKey(state, w, round*4*4);
SubBytes(state, S_enc); ShiftRows(state); AddRoundKey(state, w, 10*4*4);
// обработване на изход AES_output = transpose( state ); return format_AES_output(); }
//функция за генериране на round ключове function key_expand( key ) { var temp = new Array(4); var i, j; var w = new Array( 4*11 );
for( i=0; i<16; i++ )
w[i] = key[i];
i = 4; while ( i < 44 )
for( j=0; j<4; j++ ) temp[j] = w[(i-1)*4+j]; if ( i % 4 == 0)
temp = RotWord( temp ); temp = SubWord( temp ); temp ^= Rcon( i>>>2 );
// word = word ^ temp for( j=0; j<4; j++ ) w[i*4+j] = w[(i-4)*4+j] ^ temp[j]; i++;
return w; }
// смесване на round ключ със state function AddRoundKey( state, w, base ) { var col; for( col=0; col<4; col++ )
state[I(0,col)] ^= w[base+col*4]; state[I(1,col)] ^= w[base+col*4+1]; state[I(2,col)] ^= w[base+col*4+2]; state[I(3,col)] ^= w[base+col*4+3];
return state; }
// функция за S-Box размяна function SubBytes(state, Sbox) { var i; for( i=0; i<16; i++ ) state[i] = Sbox[ state[i] ]; return state; } …
Примери Употреба в C#.NET using System; using System.IO; using System.Security.Cryptography;
namespace Aes_Example { class AesExample
public static void Main()
try
string original = "Here is some data to encrypt!"; // Създаване на нова инстанция на класа Aes //Създава се нов ключ и инициализиращ вектор(IV). using (Aes myAes = Aes.Create())
//Криптиране на съобщението в масив от байтове. byte[] encrypted = EncryptStringToBytes_Aes(original,myAes.Key, myAes.IV);
//Декриптиране на байтовете в низ. string roundtrip = DecryptStringFromBytes_Aes(encrypted,myAes.Key, myAes.IV);
//Отпечатване на оригиналното съобщение и декриптираното съобщение за сравнение. Console.WriteLine("Original: {0}", original); Console.WriteLine("Round Trip: {0}", roundtrip);
catch (Exception e)//Отпечатване на грешки.
Console.WriteLine("Error: {0}", e.Message);
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("Key"); byte[] encrypted;
// Създаване на AES обект с посочения ключ и инициализиращ вектор. 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("Key");
//Деклариране на низ, който ще съдържа декриптираното съобщение string plaintext = null;
// Създаване на AES обект с посочения ключ и инициализиращ вектор. 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;
Употреба в Java import java.security.MessageDigest; import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;
public class AES { static String IV = "AAAAAAAAAAAAAAAA"; static String plaintext = "test text 123\0\0\0"; /*Note null padding*/ static String encryptionKey = "0123456789abcdef";
public static void main(String [] args) { try { System.out.println("==Java=="); System.out.println("plain: " + plaintext); byte[] cipher = encrypt(plaintext, encryptionKey); System.out.print("cipher: "); for (int i=0; i<cipher.length; i++) System.out.print(new Integer(cipher[i])+" "); System.out.println(""); String decrypted = decrypt(cipher, encryptionKey); System.out.println("decrypt: " + decrypted);
catch (Exception e) { e.printStackTrace();
public static byte[] encrypt(String plainText, String encryptionKey) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE"); SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES"); cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8"))); return cipher.doFinal(plainText.getBytes("UTF-8"));
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE"); SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES"); cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8"))); return new String(cipher.doFinal(cipherText),"UTF-8");
Сигурност До май 2009 г. единствените успешни атаки срещу пълния AES, базирани на информация, придобита от физическата реализация на криптографията, са само срещу отделни конкретни имплементации. Националната агенция за сигурност (NSA) разглежда всички финални продукти за AES, включително Rijndael, и заявявва, че те са достатъчно сигурни за правителствено некласифицирани данни на САЩ. През юни 2003 г. правителството на САЩ съобщава, че AES би могъл да се използва за защита и на класифицирана информация: