Тестирование дешифрования бинарных файлов
Попытаемся расшифровать бинарный файл, дважды зашифрованный при выполнении тестирования шифрования файлов. Будем использовать неверную парольную фразу. Программа выдаст сообщение об ошибке. (Рис. 3.16.). Тот же результат будет, если попытаться расшифровать файл, используя другую длину ключа. (Рис. 3.17.).
Рис. 3.16. Попытка расшифрования файла с использованием неверной парольной фразы.
Рис. 3.17. Попытка расшифрования файла с использование ключа с длиной отличной от длины ключа, использовавшегося при шифровании.
Расшифруем бинарный файл, который был зашифрован дважды при выполнении тестирования шифрования бинарных файлов. Будем использовать опцию удаления файла после дешифрации. Последовательность действий отражена на рисунках 3.18, 3.19, 3.20.
Рис. 3.18. Выбор дважды зашифрованного файла.
Рис. 3.19. Процесс дешифрации файла с включенной опцией удаления исходного файла по окончании процесса.
Рис. 3.20. Исходный открытый файл, и дважды зашифрованный и один раз дешифрованный файл.
Еще раз дешифруем полученный файл. Получим файл, каким он был до двукратного шифрования. (Рис. 3.21).
Рис. 3.21. Исходный файл, и файл, полученный в результате двукратного шифрования и двукратного дешифрования.
Заключение
В результате выполнения курсового проекта был реализован программный продукт, способный справиться со всеми поставленными задачами. Полученная программа способна шифровать/дешифровать сообщения и произвольные файлы. Программа весьма экономична по памяти, но неэкономична по временным ресурсам. Имеется множество путей для дальнейшего совершенствования программы. Расширение внедрения механизма многопоточности может сильно сократить затраты временных ресурсов. Косметические улучшения кода и максимально возможное применение побитовых операций так же способно дать некоторый выигрыш во временных ресурсах и в затрачиваемой памяти.
Список использованных источников
1. FIPS-197, https://csrc.nist.gov/csrc/media/publications/fips/197/final/documents/fips-197.pdf
2. Алгоритм шифрования AESдля самых маленьких, http://teh-box.ru/programming/algoritm-shifrovaniya-aes-dlya-samyx-malenkix.html
3. Алгоритм блочного симметричного шифрования Advanced Encryption Standard (AES). Технический отчет CELLAES-01 К.С. Пан, М.Л. Цымблер, http://crypto.pp.ua/wp-content/uploads/2010/03/aes.pdf
4. Как устроен AES, https://habrahabr.ru/post/112733/
5. Advanced Encryption Standard, https://ru.wikipedia.org/wiki/Advanced_Encryption_Standard
Приложение
Класс AES.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Threading;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
namespace AESProject
{
class AES
{
//режимAES(ключ: 128,192,256 бит)
public enum aesSubtype
{
shortKey = 128,
mediumKey = 192,
longKey = 256
};
//шифрованиевходногооткрытогоблока
public byte[] EncryptBlock(byte[] block, byte[] key, aesSubtype keyDimension)
{
int Nk, Nb, Nr;
Nk = (int)keyDimension / 32;
Nb = 4;
Nr = Nk + Nb + 2;
byte[][] state = new byte[4][];
byte[][] w = KeyExpansion(key, Nk, Nb, Nr);
//заполнение формы(состояния блока)
for (int i = 0; i < 4; i++)
{
byte[] column = new byte[4];
column[0] = block[i * 4];
column[1] = block[i * 4 + 1];
column[2] = block[i * 4 + 2];
column[3] = block[i * 4 + 3];
state[i] = column;
}
state = AddRoundKey(new byte[][] { w[0], w[1], w[2], w[3] }, state);
for (int round = 1; round <= Nr; round++)
{
state = SubBytes(state);
state = ShiftRows(state);
if (round == Nr)
{
state = AddRoundKey(new byte[][] { w[round * 4], w[round * 4 + 1], w[round * 4 + 2], w[round * 4 + 3] }, state);
}
else
{
state = MixColumns(state);
state = AddRoundKey(new byte[][] { w[round * 4], w[round * 4 + 1], w[round * 4 + 2], w[round * 4 + 3] }, state);
}
}
byte[] cipherBlock = new byte[16];
for (int i = 0; i < 4; i++)
{
cipherBlock[i * 4] = state[i][0];
cipherBlock[i * 4 + 1] = state[i][1];
cipherBlock[i * 4 + 2] = state[i][2];
cipherBlock[i * 4 + 3] = state[i][3];
}
return cipherBlock;
}
//расшифровывание входного закрытого блока
public byte[] DecryptBlock(byte[] block, byte[] key, aesSubtype keyDimension)
{
int Nk, Nb, Nr;
Nk = (int)keyDimension / 32;
Nb = 4;
Nr = Nk + Nb + 2;
byte[][] state = new byte[4][];
byte[][] w = KeyExpansion(key, Nk, Nb, Nr);
//расположение раундовых ключей в обратном порядке
byte[][] rW = new byte[Nb * (Nr + 1)][];
for (int i = 0; i < Nr + 1; i++)
{
rW[Nb * (Nr + 1) - 1 - (i * 4) - 3] = w[i * 4];
rW[Nb * (Nr + 1) - 1 - (i * 4) - 2] = w[i * 4 + 1];
rW[Nb * (Nr + 1) - 1 - (i * 4) - 1] = w[i * 4 + 2];
rW[Nb * (Nr + 1) - 1 - (i * 4)] = w[i * 4 + 3];
}
w = rW;
//заполнение формы(состояния блока)
for (inti = 0; i< 4; i++)
{
byte[] column = new byte[4];
column[0] = block[i * 4];
column[1] = block[i * 4 + 1];
column[2] = block[i * 4 + 2];
column[3] = block[i * 4 + 3];
state[i] = column;
}
state = AddRoundKey(new byte[][] { w[0], w[1], w[2], w[3] }, state);
for (int round = 1; round <= Nr; round++)
{
state = InvShiftRows(state);
state = InvSubBytes(state);
if (round == Nr)
{
state = AddRoundKey(new byte[][] { w[round * 4], w[round * 4 + 1], w[round * 4 + 2], w[round * 4 + 3] }, state);
}
else
{
state = AddRoundKey(new byte[][] { w[round * 4], w[round * 4 + 1], w[round * 4 + 2], w[round * 4 + 3] }, state);
state = InvMixColumns(state);
}
}
byte[] openBlock = new byte[16];
for (int i = 0; i < 4; i++)
{
openBlock[i * 4] = state[i][0];
openBlock[i * 4 + 1] = state[i][1];
openBlock[i * 4 + 2] = state[i][2];
openBlock[i * 4 + 3] = state[i][3];
}
returnopenBlock;
}
//получениешифротекстаизоткрытоготекста
public byte[] Encrypt(byte[] openText, string password, aesSubtype keyDimension)
{
byte[] key = PassToKey(password, keyDimension);
//добавление в начало текста сиганутры (строка "AES")
openText = (StrToBytes("AES").Concat(openText)).ToArray();
List<byte[]> openBlocksList = ByteArrToByteBlocksList(openText);
List<byte[]> cipherBlocksList = new List<byte[]>();
//в конце списка открытых блоков добавит еще один блок, в котором записана строка,
//означающаая количество байтов в исходном открытом тексте
int l = openText.Length;
string lStr = Convert.ToString(l);
List<byte> endBlockBytesLst = StrToBytes(lStr).ToList();
int cnt = endBlockBytesLst.Count;
for (int i = 0; i < 16 - cnt; i++)
{
endBlockBytesLst.Insert(0, Convert.ToByte('x'));
}
openBlocksList.Add(endBlockBytesLst.ToArray());
//-----------------------
int j = 0;
foreach (byte[] block in openBlocksList)
{
cipherBlocksList.Add(EncryptBlock(block, key, keyDimension));
j++;
}
return ByteBlocksListToByteArr(cipherBlocksList);
}
//получениеоткрытоготекстаизшифротекста
public byte[] Decrypt(byte[] cipherText, string password, aesSubtype keyDimension)
{
byte[] key = PassToKey(password, keyDimension);
List<byte[]> cipherBlocksList = ByteArrToByteBlocksList(cipherText);
List<byte[]> openBlocksList = new List<byte[]>();
foreach (byte[] block in cipherBlocksList)
{
openBlocksList.Add(DecryptBlock(block, key, keyDimension));
}
byte[] openBytes = ByteBlocksListToByteArr(openBlocksList);
//проверканаличиясигнатурывначалеоткрытоготекста
if (BytesToStr(openBytes.Take(3).ToArray()) != "AES")
return null;
//обрезание полученного открытого текста до длинны исходного открытого текста
string lstBlockStr = BytesToStr(openBlocksList[openBlocksList.Count - 1]);
int byteN = Convert.ToInt32(lstBlockStr.Substring(lstBlockStr.LastIndexOf("x") + 1));
openBytes = openBytes.Take(byteN).ToArray();
//изъятиебайтовсигнатуры
openBytes = openBytes.Skip(3).ToArray();
return openBytes;
}
//Замена байтов формы с помощью прямой таблицы подстановок
byte[][] SubBytes(byte[][] state)
{
GaluaFunctions gfInstance = new GaluaFunctions();
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
state[i][j] = gfInstance.SubByte(state[i][j]);
}
return state;
}
//Замена байтов формы с помощью обратной таблицы подстановок
byte[][] InvSubBytes(byte[][] state)
{
GaluaFunctions gfInstance = new GaluaFunctions();
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
state[i][j] = gfInstance.InvSubByte(state[i][j]);
}
return state;
}
//Прибавление раундового ключа к форме
byte[][] AddRoundKey(byte[][] roundKey, byte[][] state)
{
GaluaFunctions gfInstance = new GaluaFunctions();
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
state[i][j] = gfInstance.Sum(state[i][j], roundKey[i][j]);
}
return state;
}
//перемешиваниестолбцовформы
byte[][] MixColumns(byte[][] state)
{
for (int i = 0; i < 4; i++)
state[i] = ColumnConverting(state[i]);
return state;
}
byte[][] InvMixColumns(byte[][] state)
{
for (int i = 0; i < 4; i++)
state[i] = InvColumnConverting(state[i]);
return state;
}
//Умножение специфической матрицы на столбец
byte[] ColumnConverting(byte[] column)
{
byte[] newColumn = new byte[4];
byte[,] SpecMatrix = {
{0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02}
};
GaluaFunctions gFInstance = new GaluaFunctions();
for (int i = 0; i < 4; i++)
{
newColumn[i] = gFInstance.Sum((gFInstance.Sum((gFInstance.Sum(gFInstance.Mul(SpecMatrix[i, 0], column[0]), gFInstance.Mul(SpecMatrix[i, 1], column[1]))), gFInstance.Mul(SpecMatrix[i, 2], column[2]))), gFInstance.Mul(SpecMatrix[i, 3], column[3]));
}
return newColumn;
}
byte[] InvColumnConverting(byte[] column)
{
byte[] newColumn = new byte[4];
byte[,] SpecMatrix = {
{0x0e, 0x0b, 0x0d, 0x09},
{0x09, 0x0e, 0x0b, 0x0d},
{0x0d, 0x09, 0x0e, 0x0b},
{0x0b, 0x0d, 0x09, 0x0e}
};
GaluaFunctions gFInstance = new GaluaFunctions();
for (int i = 0; i < 4; i++)
{
newColumn[i] = gFInstance.Sum((gFInstance.Sum((gFInstance.Sum(gFInstance.Mul(SpecMatrix[i, 0], column[0]), gFInstance.Mul(SpecMatrix[i, 1], column[1]))), gFInstance.Mul(SpecMatrix[i, 2], column[2]))), gFInstance.Mul(SpecMatrix[i, 3], column[3]));
}
return newColumn;
}
//ступенчатыйциклическийсдвигстрокформывлево
byte[][] ShiftRows(byte[][] state)
{
state = ShiftNRow(1, 1, state);
state = ShiftNRow(2, 2, state);
state = ShiftNRow(3, 3, state);
return state;
}
//ступенчатый циклический сдвиг строк формы вправо
byte[][] InvShiftRows(byte[][] state)
{
state = InvShiftNRow(1, 1, state);
state = InvShiftNRow(2, 2, state);
state = InvShiftNRow(3, 3, state);
return state;
}
byte[][] ShiftNRow(int rowN, int n, byte[][] state)
{
if (n == 0) return state;
byte tmp = state[0][rowN];
for (int i = 0; i < 3; i++)
{
state[i][rowN] = state[i + 1][rowN];
}
state[3][rowN] = tmp;
return ShiftNRow(rowN, n - 1, state);
}
byte[][] InvShiftNRow(int rowN, int n, byte[][] state)
{
if (n == 0) return state;
byte tmp = state[3][rowN];
for (int i = 3; i > 0; i--)
{
state[i][rowN] = state[i - 1][rowN];
}
state[0][rowN] = tmp;
return InvShiftNRow(rowN, n - 1, state);
}
//получение расписания ключей из исходного ключа
public byte[][] KeyExpansion(byte[] key, int Nk, int Nb, int Nr)
{
//--------------------
string[] rconLog = new string[Nr];
string[] rotWordLog = new string[Nr];
string[] subWLog = new string[Nr];
string[] xorRcon = new string[Nr];
//--------------------
byte[][] w = new byte[Nb * (Nr + 1)][];
for (int i = 0; i < Nk; i++)
{
byte[] arr = { key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3] };
w[i] = arr;
}
byte[] temp;
GaluaFunctions gFInstance = new GaluaFunctions();
for (int i = Nk; i < Nb * (Nr + 1); i++)
{
temp = new byte[] { w[i - 1][0], w[i - 1][1], w[i - 1][2], w[i - 1][3] };
if (i % Nk == 0)
{
byte[] rCon = Rcon(i / Nk);
rconLog[i / Nk - 1] = BitConverter.ToString(rCon);
temp = RotWord(temp, 1);
rotWordLog[i / Nk - 1] = BitConverter.ToString(temp);
temp = SubWord(temp);
subWLog[i / Nk - 1] = BitConverter.ToString(temp);
temp[0] = gFInstance.Xor(temp[0], rCon[0]);
temp[1] = gFInstance.Xor(temp[1], rCon[1]);
temp[2] = gFInstance.Xor(temp[2], rCon[2]);
temp[3] = gFInstance.Xor(temp[3], rCon[3]);
xorRcon[i / Nk - 1] = BitConverter.ToString(temp);
}
else if (Nk > 6 && i % Nk == 4)
temp = SubWord(temp);
byte[] arr = { gFInstance.Xor(w[i - Nk][0], temp[0]), gFInstance.Xor(w[i - Nk][1], temp[1]), gFInstance.Xor(w[i - Nk][2], temp[2]), gFInstance.Xor(w[i - Nk][3], temp[3]) };
w[i] = arr;
}
//---------------------------
string[] keySchedule = new string[Nb * (Nr + 1)];
for (int i = 0; i < Nb * (Nr + 1); i++)
{
byte[] ar = w[i];
keySchedule[i] = BitConverter.ToString(ar);
}
//---------------------------
return w;
}
//замена байтов слова с помощью прямой таблицы подстанвоок
byte[] SubWord(byte[] word)
{
GaluaFunctions gFinstance = new GaluaFunctions();
for (int i = 0; i < word.Length; i++)
{
word[i] = gFinstance.SubByte(word[i]);
}
return word;
}
//циклический сдвиг слова в лево
byte[] RotWord(byte[] word, intn)
{
if (n == 0) return word;
byte tmp = 0;
for (int i = 0; i < word.Length - 1; i++)
{
if (i == 0)
{
tmp = word[word.Length - 1];
word[word.Length - 1] = word[0];
}
else
{
word[i - 1] = word[i];
}
}
word[word.Length - 2] = tmp;
return RotWord(word, n - 1);
}
byte[] Rcon(int i)
{
byte[] word = { 01, 0, 0, 0 };
if (i <= 1)
return word;
if (i == 2)
{
word[0] = 0x02;
return word;
}
GaluaFunctions gFinstance = new GaluaFunctions();
byte x = 0x02;
for (int k = 2; k <= i - 1; k++)
{
x = gFinstance.Mul(x, 0x02);
}
word[0] = x;
return word;
}
//преобразование строки в массив байтов
public byte[] StrToBytes(string inpString)
{
return Encoding.UTF8.GetBytes(inpString);
}
//преобразованиемассивабайтоввстроку
public string BytesToStr(byte[] inpBytes)
{
return new string(Encoding.UTF8.GetChars(inpBytes));
}
//разбиение массива байтов на список байтблоков фикс.длинны. (длиннаблока = 128 бит = 16 байт)
List<byte[]> ByteArrToByteBlocksList(byte[] openText)
{
/* int l = openText.Length;
string lStr = Convert.ToString(l);
List<byte> endBlockBytesLst = StrToBytes(lStr).ToList();
int cnt = endBlockBytesLst.Count;
for (int i =0; i<16-cnt; i++)
{
endBlockBytesLst.Insert(0, Convert.ToByte('x'));
}*/
List<byte[]> blocksList = new List<byte[]>((int)Math.Ceiling((decimal)(openText.Length / 16F)));
while (openText != null)
{
if (openText.Length >= 16)
{
if (openText.Length == 16)
{
blocksList.Add(openText.Take(16).ToArray());
return blocksList;
}
blocksList.Add(openText.Take(16).ToArray());
openText = openText.Skip(16).ToArray();
}
else
{
byte[] newBlock = new byte[16];
for (int i = 0; i < 16; i++)
{
newBlock[i] = (i < openText.Length ? openText[i] : (byte)'x');
}
blocksList.Add(newBlock);
openText = null;
}
}
// blocksList.Add(endBlockBytesLst.ToArray());
return blocksList;
}
//слияние списка байтблоков в массив байтов
byte[] ByteBlocksListToByteArr(List<byte[]> blocksList)
{
List<byte> byteArr = new List<byte>();
foreach (byte[] block in blocksList)
foreach (byte bt in block)
{
byteArr.Add(bt);
}
return byteArr.ToArray();
}
//(построение ключа)хэширование пароля в зависимости от выбранной длинны ключа (128/192/256)
public byte[] PassToKey(string password, aesSubtype keyDimension)
{
byte[] key = Encoding.UTF8.GetBytes(password);
byte[] keyHash = null;
switch (keyDimension)
{
case aesSubtype.shortKey:
keyHash = MD5.Create().ComputeHash(key);
break;
case aesSubtype.mediumKey:
keyHash = SHA256.Create().ComputeHash(key);
keyHash = keyHash.Take(24).ToArray();
break;
case aesSubtype.longKey:
keyHash = SHA256.Create().ComputeHash(key);
break;
}
return keyHash;
}
}
}
КлассGF.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AESProject
{
//классалгебрыГалуаполя 2^8
class GaluaFunctions
{
//суммированиедвухмногочленов
public byte Sum (byte a, byte b)
{
return Convert.ToByte(Convert.ToInt32(a)^Convert.ToInt32(b));
}
public byte Xor(byte a, byte b)
{
return Convert.ToByte(Convert.ToInt32(a) ^ Convert.ToInt32(b));
}
//умножение многочлена на многочлен 0x02
byte XMul(byte a)
{
var highbit = a & 0x80;
var shl = (a << 1) & 0xff;
return Convert.ToByte(highbit == 0 ? shl : shl ^ 0x1b);
}
//n-кратное умножение многочлена a на многочлен 0x02
public byte NXMul(byte a,int n)
{
if (n > 0)
{
var highbit = a & 0x80;
var shl = (a << 1) & 0xff;
return NXMul(Convert.ToByte(highbit == 0 ? shl : shl ^ 0x1b),n-1);
}
else return a;
}
//умножение многочлена a на многочлен b
public byte Mul(byte a, byte b)
{
byte res = 0x00;
List<int> binBList = DecToBin(b);
for (int i = 0; i < 8; i++)
{
if (binBList[i] == 1) res = Sum(res, NXMul(a, i));
}
return res;
}
public List<int> DecToBin(int decNum)
{
List<int> result = new List<int>();
while (decNum >= 2)
{
int rest = decNum % 2;
decNum = (int)(decNum / 2);
result.Add(rest);
}
result.Add(decNum);
result.Reverse();
if (result.Count < 8)
{
int rC = result.Count;
for(int i = 0; i< 8 - rC;i++) {
result.Insert(0, 0);
}
}
result.Reverse();
return result;
}
//нахождение обратного многочлена
byte getInverseByte(byte a)
{
if (a == 0) return a;
for (byte b = byte.MinValue; b <= byte.MaxValue; b += 1)
if (Mul(b, a) == 0x01) return b;
return a;
}
//таблицы прямой и боратной подстановки(для AES)
public byte SubByte(byte b)
{
byte[,] sbox = {
{0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76},
{0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0},
{0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15},
{0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75},
{0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84},
{0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf},
{0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8},
{0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2},
{0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73},
{0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb},
{0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79},
{0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08},
{0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a},
{0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e},
{0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf},
{0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16}
};
string hexStr = Convert.ToString(b, 16);
int i1;
int i2;
if (hexStr.Length == 2)
{
i1 = Convert.ToInt32(hexStr[0].ToString(), 16);
i2 = Convert.ToInt32(hexStr[1].ToString(), 16);
}
else
{
i1 = 0;
i2 = Convert.ToInt32(hexStr[0].ToString(), 16);
}
return sbox[i1, i2];
}
public byte InvSubByte(byte b)
{
byte[,] sbox = {
{0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb},
{0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb},
{0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e},
{0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25},
{0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92},
{0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84},
{0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06},
{0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b},
{0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73},
{0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e},
{0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b},
{0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4},
{0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f},
{0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef},
{0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61},
{0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}
};
string hexStr = Convert.ToString(b, 16);
int i1;
int i2;
if (hexStr.Length == 2)
{
i1 = Convert.ToInt32(hexStr[0].ToString(), 16);
i2 = Convert.ToInt32(hexStr[1].ToString(), 16);
}
else
{
i1 = 0;
i2 = Convert.ToInt32(hexStr[0].ToString(), 16);
}
return sbox[i1, i2];
}
}
}
Классглавногоокна
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Win32;
namespace AESProject
{
/// <summary>
/// Логикавзаимодействиядля MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
System.Windows.Threading.DispatcherTimer timer;
AES aesInstance;
AES.aesSubtype encrMode = AES.aesSubtype.shortKey;
AES.aesSubtype decrMode = AES.aesSubtype.shortKey;
AES.aesSubtype encrFMode = AES.aesSubtype.shortKey;
AES.aesSubtype decrFMode = AES.aesSubtype.shortKey;
byte[] encryptedMsg=null;
byte[] decryptedMsg;
PassPhrConstr passPhrConstrInstance;
List<Action> listToDo = new List<Action>();
float blockEncrProgress;
string encryptingFileName;
int encrBlockI = 0;
int encrBlockTotal = 0;
float blockDecrProgress;
string decryptingFileName;
int decrBlockI = 0;
int decrBlockTotal = 0;
public MainWindow()
{
timer = new System.Windows.Threading.DispatcherTimer();
timer.Tick += new EventHandler(timerTick);
timer.Interval = new TimeSpan(0, 0, 0,0,100);
InitializeComponent();
passPhrConstrInstance = new PassPhrConstr();
aesInstance = new AES();
timer.Start();
}
void ThreadingFileEncryption (object inpInfo)
{
CryptoInfo newInst = (CryptoInfo)inpInfo;
StreamFileEncrypt(newInst.inpFileName, newInst.outFileName, newInst.key, newInst.cryptoMode);
}
void ThreadingFileDecryption (object inpInfo)
{
CryptoInfo newInst = (CryptoInfo)inpInfo;
StreamFileDecrypt(newInst.inpFileName, newInst.outFileName, newInst.key, newInst.cryptoMode);
}
private void timerTick(object sender, EventArgs e)
{
while (listToDo.Count > 0)
{
Action someFunc = listToDo[0];
listToDo.RemoveAt(0);
try { someFunc.Invoke(); }
catch { }
}
}
private void radioButton2_Click(object sender, RoutedEventArgs e)
{
encrMode = AES.aesSubtype.longKey;
keyTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(encr1PasswordBox.Password, encrMode)).Replace("-", "");
}
private void radioButton1_Click(object sender, RoutedEventArgs e)
{
encrMode = AES.aesSubtype.mediumKey;
keyTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(encr1PasswordBox.Password, encrMode)).Replace("-", "");
}
private void radioButton_Click(object sender, RoutedEventArgs e)
{
encrMode = AES.aesSubtype.shortKey;
keyTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(encr1PasswordBox.Password, encrMode)).Replace("-", "");
}
private void encryptMsgButton_Click(object sender, RoutedEventArgs e)
{
if (encr1PasswordBox.Password != "" && encr1PasswordBox.Password == encr2PasswordBox.Password)
{
if (!passPhrConstrInstance.CheckStringForCorrectness(encr1PasswordBox.Password))
{
MessageBox.Show("The passphrase does not satisfy the constraints");
return;
}
encryptedMsg = aesInstance.Encrypt(aesInstance.StrToBytes(msgTextBox.Text), encr1PasswordBox.Password, encrMode);
cipherTextBlock.Text = aesInstance.BytesToStr(encryptedMsg);
cipherHexTextBlock.Text = BitConverter.ToString(encryptedMsg).Replace("-", ".");
saveCipherButton.IsEnabled = true;
}else
{
MessageBox.Show("Passphrases should not be empty.\nPassphrases must match.");
}
}
private void radioButton3_Click(object sender, RoutedEventArgs e)
{
decrMode = AES.aesSubtype.shortKey;
keyDecrTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(decrPasswordBox.Password, decrMode)).Replace("-", "");
}
private void radioButton4_Click(object sender, RoutedEventArgs e)
{
decrMode = AES.aesSubtype.mediumKey;
keyDecrTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(decrPasswordBox.Password, decrMode)).Replace("-", "");
}
private void radioButton5_Click(object sender, RoutedEventArgs e)
{
decrMode = AES.aesSubtype.longKey;
keyDecrTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(decrPasswordBox.Password, decrMode)).Replace("-", "");
}
private void decryptMsgButton_Click(object sender, RoutedEventArgs e)
{
if (encryptedMsg != null)
{
decryptedMsg = aesInstance.Decrypt(encryptedMsg, decrPasswordBox.Password, decrMode);
if (decryptedMsg != null)
openTextBlock.Text = aesInstance.BytesToStr(decryptedMsg);
else
MessageBox.Show("Invalid passphrase.");
}
else MessageBox.Show("Encrypt message before.");
}
private void encrFileButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
Nullable<bool> result = openFileDialog.ShowDialog();
AES aESInstance = new AES();
if (result == true)
{
if (fileEncrPasswordBox1.Password != "" && fileEncrPasswordBox1.Password == fileEncrPasswordBox2.Password)
{
if (!passPhrConstrInstance.CheckStringForCorrectness(fileEncrPasswordBox1.Password))
{
MessageBox.Show("The passphrase does not satisfy the constraints");
return;
}
string filename = openFileDialog.FileName;
encryptingFileName = filename.Insert(filename.LastIndexOf('.'), "ENCR");
CryptoInfo cryptoInfoInstance = new CryptoInfo(filename, filename.Insert(filename.LastIndexOf('.'), "ENCR"), aesInstance.PassToKey(fileEncrPasswordBox1.Password, encrFMode), encrFMode);
Stream sourceFStr = File.OpenRead(filename);
blockEncrProgress = 100F/( sourceFStr.Length / 16F);
encrBlockTotal = (int)(Math.Round(sourceFStr.Length / 16F));
Thread newEncrThread = new Thread(new ParameterizedThreadStart(ThreadingFileEncryption));
newEncrThread.Start(cryptoInfoInstance);
}
else MessageBox.Show("Passphrases should not be empty.\nPassphrases must match.");
}
else
encrFileInfoTextBlock.Text = "An error occurred when opening the file. Try again.";
}
private void decrFileButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
Nullable<bool> result = openFileDialog.ShowDialog();
if (result == true)
{
if (fileDecrPasswordBox.Password != "")
{
string filename = openFileDialog.FileName;
decryptingFileName = filename.Insert(filename.LastIndexOf('.'), "DECR");
CryptoInfo cryptoInfoInstance = new CryptoInfo(filename, filename.Insert(filename.LastIndexOf('.'), "DECR"), aesInstance.PassToKey(fileDecrPasswordBox.Password, decrFMode), decrFMode);
Stream sourceFStr = File.OpenRead(filename);
blockDecrProgress = 100F / (sourceFStr.Length / 16F);
decrBlockTotal = (int)(Math.Round(sourceFStr.Length / 16F));
Thread newDecrThread = new Thread(new ParameterizedThreadStart(ThreadingFileDecryption));
newDecrThread.Start(cryptoInfoInstance);
}
else MessageBox.Show("Passphrase should not be empty.");
}
else
decrFileInfoTextBlock.Text = "An error occurred when opening the file. Try again.";
}
void StreamFileEncrypt(string inputFileName, string encrFileName, byte[] key, AES.aesSubtype encrMode)
{
Stream dest = File.OpenWrite(encrFileName);
AES aESInstance = new AES();
byte[] signBlock;
signBlock = aESInstance.StrToBytes("AESxxxxxxxxxxxxx");
signBlock = aesInstance.EncryptBlock(signBlock, key, encrMode);
dest.Write(signBlock, 0, 16);
using (Stream source = File.OpenRead(inputFileName))
{
listToDo.Add(new Action(() =>
{
encrFileButton.IsEnabled = false;
encrFileInfoTextBlock.Text = "Encryption in procces. Please stand by.";
}));
byte[] buffer = new byte[16];
int bytesRead;
long blockI = 0;
long totalBlocks = source.Length / 16;
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
if (bytesRead < 16)
{
byte[] tail = new byte[16 - bytesRead];
for (int i = 0; i < 16 - bytesRead; i++)
tail[i] = Convert.ToByte('x');
buffer = (buffer.Take(bytesRead).Concat(tail)).ToArray();
byte[] encrBlock = aesInstance.EncryptBlock(buffer, key, encrMode);
dest.Write(encrBlock, 0, 16);
}
else
{
byte[] encrBlock = aesInstance.EncryptBlock(buffer, key, encrMode);
dest.Write(encrBlock, 0, bytesRead);
}
blockI++;
listToDo.Add(new Action(() =>
{
this.encrProgressBar.Value += blockEncrProgress;
encrBlockI++;
this.encrProgressTextBlock.Text = encrBlockI + " / "+encrBlockTotal + " blocks encrypted";
}));
}
source.Close();
}
dest.Close();
listToDo.Add(new Action(()=>
{
encrProgressTextBlock.Text = "";
encrBlockI = 0;
this.encrProgressBar.Value = 0;
if (encrDelFileCheckBox.IsChecked == true) File.Delete(inputFileName);
encrFileInfoTextBlock.Text = "Encrypted file has been saved as " + encryptingFileName;
encrFileButton.IsEnabled = true;
}));
}
void StreamFileDecrypt(string inputFileName, string decrFileName, byte[] key, AES.aesSubtype decrMode)
{
Stream dest = File.OpenWrite(decrFileName);
AES aESInstance = new AES();
using (Stream source = File.OpenRead(inputFileName))
{
listToDo.Add(new Action(() =>
{
decrFileButton.IsEnabled = false;
decrFileInfoTextBlock.Text = "Decryption in procces. Please stand by.";
}));
byte[] buffer = new byte[16];
int bytesRead;
long blockI=0;
long totalBlocks = source.Length / 16;
while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
byte[] decrBlock = aesInstance.DecryptBlock(buffer, key, decrMode);
if (blockI == totalBlocks - 1)
{
string blockStr = aesInstance.BytesToStr(decrBlock);
int xPos = blockStr.IndexOf('x');
if (xPos >= 0)
{
decrBlock = decrBlock.Take(xPos).ToArray();
bytesRead = xPos;
}
}
if(blockI == 0)
{
if(aESInstance.BytesToStr(decrBlock.Take(3).ToArray()) != "AES")
{
dest.Close();
File.Delete(decrFileName);
MessageBox.Show("Invalid passphrase.");
listToDo.Add(new Action(() =>
{
decrFileInfoTextBlock.Text = "Decryption procces was interrupted";
decrFileButton.IsEnabled = true;
}));
return;
// break;
}else
{
blockI++;
continue;
}
}
dest.Write(decrBlock, 0, bytesRead);
blockI++;
listToDo.Add(new Action(() =>
{
this.decrProgressBar.Value += blockDecrProgress;
decrBlockI++;
this.decrProgressTextBlock.Text = decrBlockI + " / " + decrBlockTotal + " blocks decrypted";
}));
}
source.Close();
}
dest.Close();
listToDo.Add(new Action(() =>
{
decrProgressTextBlock.Text = "";
decrBlockI = 0;
this.decrProgressBar.Value = 0;
if (decrDelFileCheckBox.IsChecked == true) File.Delete(inputFileName);
decrFileInfoTextBlock.Text = "Decrypted file has been saved as " + decrFileName;
decrFileButton.IsEnabled = true;
}));
}
private void radioButton6_Click(object sender, RoutedEventArgs e)
{
encrFMode = AES.aesSubtype.shortKey;
}
private void radioButton7_Click(object sender, RoutedEventArgs e)
{
encrFMode = AES.aesSubtype.mediumKey;
}
private void radioButton8_Click(object sender, RoutedEventArgs e)
{
encrFMode = AES.aesSubtype.longKey;
}
private void radioButton9_Click(object sender, RoutedEventArgs e)
{
decrFMode = AES.aesSubtype.shortKey;
}
private void radioButton10_Click(object sender, RoutedEventArgs e)
{
decrFMode = AES.aesSubtype.mediumKey;
}
private void radioButton11_Click(object sender, RoutedEventArgs e)
{
decrFMode = AES.aesSubtype.longKey;
}
private void aboutMenuItem_Click(object sender, RoutedEventArgs e)
{
AboutWindow aboutWndInstance = new AboutWindow();
aboutWndInstance.Owner = this;
this.IsEnabled = false;
aboutWndInstance.Show();
}
private void shutDownMenuItem_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void passwordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
keyTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(encr1PasswordBox.Password, encrMode)).Replace("-", "");
}
private void passwordBox_PasswordChanged_1(object sender, RoutedEventArgs e)
{
keyDecrTextBlock.Text = BitConverter.ToString(aesInstance.PassToKey(decrPasswordBox.Password, decrMode)).Replace("-", "");
}
private void saveCipherButton_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
Nullable<bool> result = saveFileDialog.ShowDialog();
if (result == true)
{
string filename = saveFileDialog.FileName;
File.WriteAllBytes(filename, encryptedMsg);
}
}
private void passPhrConstrMenuItem_Click(object sender, RoutedEventArgs e)
{
PassPhraseConstraintsWindow newConstrWnd = new PassPhraseConstraintsWindow(passPhrConstrInstance);
newConstrWnd.Owner = this;
newConstrWnd.Show();
}
}
}