Вы находитесь на странице: 1из 13

РОССИЙСКИЙ УНИВЕРСИТЕТ ДРУЖБЫ НАРОДОВ

Факультет физико-математических и естественных наук

ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ №2

на тему
«Основы блочного шифрования.»

Дисциплина: Математические основы защиты информации и информационной


безопасности.

Выполнил

студент группы

МОСКВА
2020
1. Название и цель работы.

Цель работы: Изучение симметричных алгоритмов шифрования, основанных на


блочных шифрах

Задание: Реализовать варианта симметричного блочного криптографического алгоритма


(структура, расширение ключа, анализ):

 Rijndael

2. Исходные данные.

Краткое описание алгоритма Rijndael.


Данный блочный шифр развивает нетрадиционную для последнего десятилетия
структуру: прямоугольные KASLT – сети. Эта структура получила свое название изза
того, что шифруемый блок представляется в виде прямоугольника, например, 44 или 4
6 байт, а затем над ним построчно, по столбцам и побайтно производятся
криптопреобразования. KASLT представляет собой аббревиатуру английских терминов
Key Addition (добавление ключа), Substitution (табличная подстановка) и Linear
Transposition ( линейное перемешивание).
Rijndael (допускаются различные варианты произношения, например, англ.
"Ра"йндэл", интерн. "Рийнд'эл") описан авторами (бельгийцами Vincent Rijmen и Joan
Daemen) как шифр с изменяемым размером блока и длиной ключа. Единственные
налагаемые требования на эти два параметра – их кратность 32 битам. Размер блока
далее определяется как Nb32, длина ключа – Nk32.Количество раундов сети ,
необходимое для стойкого шифрования зависит от двух параметров. Для конкурса AES в
соответствии с требованиями авторы зафиксировали Nb = 4 (то есть шифруемый блок
представляет собой квадрат 4 на 4 байта) и Nk = 4, 6, и 8. В этом случае количество
раундов сети Nr соответственно равно 10, 12 и 14. Потенциально шифр может работать и
с большими по размерам блоками.
К достоинства алгоритма необходимо отнести очень хорошее быстродействие на
все платформах от 8- до 64-битных. Структура шифра позволяет использовать его при
любых комбинациях размера блока и длины ключа – единственным необходимым
изменением будет только смена количества раундов.
Возможность распараллеливания у Rijndael очень высока по сравнению с другими
конкурсантами. Коэффициент ускорения вычислений при наличии неограниченного (но
разумного) числа процессоров в системе у Rijndael достигает 6-8 раз, что более чем в два
раза выше ближайшего соперника.
Несомненно, что все перечисленные преимущества – заслуга в большей степени
архитектуры KASLT по сравнению с ограниченными возможностями сети Файстеля.
Однако ни в коем случае нельзя недооценивать и вклад авторов, т. е. конкретную
специфику шифра. Во-первых, без оригинального построения внутри раунда
оптимизация 32-битными табличными подстановками и высокое распараллеливание
были бы невозможны. А во-вторых, огромная заслуга авторов в том, что на конкурс был
представлен один из первых KASLT- шифров с надежно доказанной криптостойкостью
при малом числе раундов, т. е. был найден вариант достижения
"стойкость/быстродействие", что и у традиционной сети Файстеля, но на другой, гараздо
более универсальной для вычислительной техники, архитектуре KASLT. К недостаткам
шифра относят необходимость разработки двух независимых процедур – шифрования и
дешифрования (объединить их, как, например, в сети Файстеля, уже невозможно).
3. Контрольный пример.

Более подробно с реализацией алгоритма можно познакомиться по ссылке тут (теория и


псевдокод) и тут (наглядное видео).

Код программы представлен в Приложении 1 и Приложении 2.

4. Результаты работы программы с различными исходными текстами, ключами и


другими параметрами.

Для входящего И ключа


сообщения
00 44 88 CC 00 04 08 0C
11 55 99 DD 01 05 09 0D
22 66 AA EE 02 06 0A 0E
33 77 BB FF 03 07 0B 0F

Результат работы программы


5. Выводы.

Вывод: благодаря лабораторной работе мы изучили симметричные алгоритмы


шифрования, основанные на блочных шифрах

6. Ответы на контрольные вопросы.

1. Что называется ключом шифрования?

Ключ — секретная информация, используемая


криптографическим алгоритмом при зашифровании/расшифровании сообщений,
постановке и проверке цифровой подписи, вычислении кодов аутентичности (MAC). При
использовании одного и того же алгоритма результат шифрования зависит от ключа. Для
современных алгоритмов сильной криптографии утрата ключа приводит к практической
невозможности расшифровать информацию.
2. Какие шифры называются шифрами на открытом ключе и какие - шифрами на
секретном ключе?

Все методы преобразования информации с целью защиты от несанкционированного


доступа делятся на две большие группы: методы шифрования с закрытым ключом и
методы шифрования с открытым ключом . Шифрование с закрытым
ключом шифрование с секретным ключом или симметричное шифрование) используется
человеком уже довольно долгое время. Для шифрования и расшифрования данных в этих
методах используется один и тот же ключ, который обе стороны стараются хранить в
секрете от противника.
Шифрование с открытым ключом (ассимметричное шифрование) стало использоваться
для криптографического закрытия информации лишь во второй половине ХХ века. В эту
группу относятся методы шифрования, в которых для шифрования и расшифрования
данных используются два разных ключа. При этом один из ключей (открытый ключ)
может передаваться по открытому (незащищенному) каналу связи.

3. Чем отличается поточное шифрование от блочного?

Поточное шифрование состоит в том, что каждый бит открытого текста и


соответствующий бит ключа преобразовываются по определенному алгоритму
(например, складываются по модулю 2). 

При блочном шифровании открытый текст сначала разбивается на равные по длине


блоки, затем применяется зависящая от ключа функция шифрования для преобразования
блока открытого текста длиной  бит в блок шифротекста такой же длины. При
блочном шифровании каждый бит блока шифротекста зависит от значений всех битов
соответствующего блока открытого текста и никакие два блока открытого текста не
могут быть представлены одним и тем же блоком шифротекста. При этом небольшие
изменения в шифротексте вызывают большие и непредсказуемые изменения в
соответствующем открытом тексте и наоборот.

4. Чему равна длина секретного ключа алгоритма DES (с учетом контрольных


разрядов, без учета контрольных разрядов)?

Размер ключа алгоритма DES – 56 бит без учета контрольных разрядов и с учетом
контрольных разрядов – 64 бита.

5. Чему может быть равна длина секретных ключей алгоритма Rijndael?

Длина ключа – Nk32 , Nk = 4, 6, 8. Другими словами,

Длина секретных ключей алгоритма Rijndael может быть равна 128, 192 и 256 бита.

6. Какая архитектура лежит в основе алгоритма DES?


Алгоритм DES (Data Encription Standart) был принят в 1980 году в качестве
национального стандарта шифрования данных. Параметры алгоритма таковы:
разрядность блока – 64 бита, размер ключа – 56 бит (с учетом контрольных разрядов – 64
бита).

Алгоритм представляет собой классическую сеть Файстеля из 16 раундов с добавлением


входной и выходной перестановки бит.
Разрядность ключей раундов, применяемых в DES равна 48 битам. Для симметричности
алгоритма шифрования/дешифрования в DES, как и в классической сети Файстеля,
производится дополнительный размен (перестановка) последнего обмена ветвей. Это
позволяет производить шифрование и дешифрование одним и тем же программным
кодом, передавая ему в первом случае ключи раундов в прямом порядке (k0, k1, …, k15).
Во втором случае в обратном (k15, k14, …, k0).

7. Какой параметр определяет криптостойкость симметричного алгоритма?

Криптографическая стойкость (или криптостойкость) —
способность криптографического алгоритма противостоять криптоанализу. Стойким
считается алгоритм, атака на который требует от атакующего наличия столь
значительных вычислительных ресурсов или огромных затрат времени на расшифровку
перехваченных сообщений, что к моменту их расшифровки защищённая информация
потеряет свою актуальность.
Криптостойкость симметричного алгоритма определяется трудоемкостью вычисления
дискретного логарифма в конечном поле
Приложение 1.
Листинг программы для шифровки и дешифровки при помощи алгоритма Rijndael

import aes128.S_box;

import static aes128.S_box.mul;

public class AesEnCrypt {


private static byte[][] Rcon = {{0x00, 0x00, 0x00, 0x00},
{0x01, 0x00, 0x00, 0x00},
{0x02, 0x00, 0x00, 0x00},
{0x04, 0x00, 0x00, 0x00},
{0x08, 0x00, 0x00, 0x00},
{0x10, 0x00, 0x00, 0x00},
{0x20, 0x00, 0x00, 0x00},
{0x40, 0x00, 0x00, 0x00},
{(byte) 0x80, 0x00, 0x00, 0x00},
{(byte) 0x1b, 0x00, 0x00, 0x00},
{(byte) 0x36, 0x00, 0x00, 0x00}};
public int Nb ;
public int Nk ;
public int Nr ;
private byte[][] w;
private int[] s_box_one_dime ;
private S_box sboxgenerator ;
private int[] inv_s_box_one_dime ;

public AesEnCrypt() {

Nb = 4;
Nk = 4;
Nr = 10;
w = new byte[Nb*(Nr+1)][Nb];
sboxgenerator = new S_box();
s_box_one_dime = sboxgenerator.generate_one_dime(true);
inv_s_box_one_dime = sboxgenerator.generate_inverse_one_dime();
}
public byte[][] encryptparsestring(String input, String inputkey) {
String sb;
int start = 0;
int end = 2;
byte[] key = new byte[Nk*4];
for (int i = 0; i < key.length; i++) {
sb = inputkey.substring(start, end);
key[i] = (byte)(Integer.parseInt(sb.toString(), 16));
start += 2;
end += 2;
}

Keyexpansion(key);
start = 0;
end = 2;
byte[][] state = new byte[Nb][Nb];

for (int i = 0; i < state.length; i++) {


for (int j = 0; j < state[0].length; j++) {
sb = input.substring(start, end);
state[j][i] = (byte)(Integer.parseInt(sb.toString(), 16));
start += 2;
end += 2;
}
}
state = encryptstate(state);
System.out.println(statetostring(state));
System.out.println("------------------------------------------\n\n");
return state ;
}
public void Keyexpansion(byte[] key) {
byte[] temp = new byte[4];
int i = 0;
while(i < Nk) {
w[i][0] = key[4*i];
w[i][1] = key[4*i + 1];
w[i][2] = key[4*i + 2];
w[i][3] = key[4*i + 3];
i++;
}
i = Nk;
while(i < Nb *(Nr+1)) {
temp = w[i - 1];
if(i%Nk == 0) {
temp = xorbytes(SubWord(RotWord(temp)), Rcon[i/Nk]);
}
else if (Nk > 6 && i%Nk == 4) {
temp = SubWord(RotWord(temp));
}
w[i] = xorbytes(w[i-Nk], temp);
i++;
}
return;
}

private String statetostring(byte[][] state) {


StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
stringBuilder.append(String.format("%02X", state[j][i]));
}
}
String string = stringBuilder.toString();
return string;
}

public byte[][] encryptstate(byte[][] state) {

System.out.println("\nВходящее сообщение:");
printstate(state);
AddRoundKey(0,state);
for (int i = 1; i < Nr ; i++) {
System.out.println("Цикловое преобразование № " + i +":");
printstate(state);
SubBytes(state);
System.out.println("После замены байт:");
printstate(state);

state = ShiftRows(state);
System.out.println("После сдвига строк:");
printstate(state);

state = MixColumns(state,true);
System.out.println("После замешивания столбцов:");
printstate(state);

AddRoundKey(i,state);
System.out.println("После добавления циклового ключа:");
printstate(state);
}
System.out.println("Цикловое преобразование № " + Nr +":");
printstate(state);
SubBytes(state);
System.out.println("После замены байт:");
printstate(state);

state = ShiftRows(state);
System.out.println("После сдвига строк:");
printstate(state);

AddRoundKey(Nr,state);
System.out.println("-----------------------------------------");
System.out.println("Получившееся зашифрованное сообщение :");

return state;
}
private void SubBytes(byte[][] state) {
for (int i = 0; i < Nb; i++) {
for (int j = 0; j < Nb; j++) {
state[i][j] = (byte)(s_box_one_dime[state[i][j] & 0x00ff] & 0x00ff);
}
}
}
private byte[][] ShiftRows(byte[][] state) {
byte[][] tmp = new byte[Nb][Nb];
for (int i = 0; i < Nb; i++) {
for (int j = 0; j < Nb; j++) {
tmp[i][j] = state[i][(Nb+i+j)%Nb];
}
}
return tmp;
}
private void AddRoundKey(int currentroundnum,byte[][] state) {
for (int i = 0; i < 4; i++) {
state[0][i] = (byte) (state[0][i] ^ w[Nb*currentroundnum + i][0]);
state[1][i] = (byte) (state[1][i] ^ w[Nb*currentroundnum + i][1]);
state[2][i] = (byte) (state[2][i] ^ w[Nb*currentroundnum + i][2]);
state[3][i] = (byte) (state[3][i] ^ w[Nb*currentroundnum + i][3]);
}
}
private byte[][] MixColumns(byte[][] state, boolean type) {
byte[] a;
if (type) a = new byte[]{2, 3, 1, 1};
else a = new byte[]{0x0e, 0x0b, 0x0d, 0x09};

byte[][] tmp = new byte[Nb][Nb];


for(int j = 0 ; j < Nb ; j++) {
tmp[0][j] = (byte)(mul(a[0], state[0][j])^mul(a[1], state[1][j])^
mul(a[2], state[2][j])^mul(a[3], state[3][j]));
tmp[1][j] = (byte)(mul(a[3], state[0][j])^mul(a[0], state[1][j])^
mul(a[1], state[2][j])^mul(a[2], state[3][j]));
tmp[2][j] = (byte)(mul(a[2], state[0][j])^mul(a[3], state[1][j])^
mul(a[0], state[2][j])^mul(a[1], state[3][j]));
tmp[3][j] = (byte)(mul(a[1], state[0][j])^mul(a[2], state[1][j])^
mul(a[3], state[2][j])^mul(a[0], state[3][j]));
}
return tmp;
}

private byte[] RotWord(byte[] A) {


byte[] result = new byte[A.length];
for (int i = 0; i < A.length; i++) {
result[i] = A[(i+1)%A.length];
}
return result;
}
private byte[] SubWord(byte[] A) {
byte[] result = new byte[A.length];
for (int i = 0; i < A.length; i++) {
result[i] = (byte)s_box_one_dime[A[i]&0x00ff];
}
return result;
}
private byte[] xorbytes(byte[] A, byte[] B) {
byte[] result = new byte[A.length];
for (int i = 0; i < A.length; i++) {
result[i] = (byte)(A[i]^B[i]);
}
return result;

}
private void printstate(byte[][] state) {
int i = 0;
int j = 0;
for(; i < 4 ; i++) {
for(j = 0; j < 4 ; j++) {
String string = String.format("%02X", state[i][j]);
System.out.print(string+" ");
}
System.out.println();
}
}

public String decryptstate(byte[][] state) {

AddRoundKey(Nr,state);
for (int i = Nr - 1; i > 0 ; i--) {
state = InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(i,state);
state = MixColumns(state,false);
}
state = InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(0,state);
System.out.println("\n-----------------------------------------");
System.out.println("Получившееся расшифрованное сообщение: ");
System.out.println(statetostring(state));
System.out.println("\n-----------------------------------------");
return statetostring(state);
}

public void InvSubBytes(byte[][] state) {


for (int i = 0; i < Nb; i++) {
for (int j = 0; j < Nb; j++) {
state[i][j] = (byte)(inv_s_box_one_dime[state[i][j] & 0x00ff] & 0x00ff);
}
}
}
public byte[][] InvShiftRows(byte[][] state) {
byte[][] tmp = new byte[Nb][Nb];
for (int i = 0; i < Nb; i++) {
for (int j = 0; j < Nb; j++) {
tmp[i][j] = state[i][(Nb-i+j)%Nb];
}
}
return tmp;
}

public static void main(String[] args) {


String keyString = "000102030405060708090a0b0c0d0e0f";
System.out.println("Ключ шифрования: "+ keyString);

String plaintextString = "00112233445566778899aabbccddeeff";


System.out.println("Исходный текст: " + plaintextString);

AesEnCrypt cipher = new AesEnCrypt();


@SuppressWarnings("deprecation")
byte[][] state = cipher.encryptparsestring(plaintextString, keyString);

System.out.println("\nДЕШИФРОВКА\n");

System.out.println("Ключ шифрования:"+ keyString);

System.out.print("Зашифрованное Сообщение: ");


cipher.statetostring(state);

System.out.println(cipher.statetostring(state));

cipher.decryptstate(state);
}

}
Приложение 2.
Листинг класса S_box для формирования Таблицы замены

package aes128;
package aes128;
public class S_box {

public int[] c = {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0} ;


public int[] irreducible = {1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0};
private int[] quotient = new int[16];
private int[] final_quotient = new int[16];
private int[] remainder = new int[16];
private int[][] s_box = new int[16][16];
private int[] s_box_one_dimen = new int[256];
private int[] inv_s_box_one_dimen = new int[256];

private int get_bit(byte b,int i) {


int bit = (int)((b>>i) & 0x1);
return bit;
}

public int[] byte_to_intarry(byte data) {


int[] bits = new int[16];
for (int i = 0; i < 8; i++) {
bits[i] = get_bit(data, i);
}
return bits;
}

public byte intarry_to_byte(int[] A) {


int result = 0;
int exp = 1;
for (int i = 0; i < 8; i++) {
result = result + A[i]*exp;
exp = exp * 2;
}
return (byte)result;
}

private int get_exponent(int[] num) {


int exponent = 0;
for (int i = 1; i <= num.length; i++) {
if(num[i-1] == 1)
exponent = i;
}
return exponent;
}

private void calc_quotient(int diff_exponent) {


quotient[diff_exponent] = 1;
}

private void division(int[] A, int[] B){


int a_exponent = get_exponent(A);
int b_exponent = get_exponent(B);
int[] one_quotient = new int[16];
int diff_exponent = 0;

if(a_exponent >= b_exponent) {


diff_exponent = a_exponent - b_exponent;
for (int i = 0; i < B.length; i++) {
if(i+diff_exponent < A.length)
one_quotient[i+diff_exponent] = B[i];
if(i < diff_exponent)
one_quotient[i] = 0;
}

for (int i = 0; i < A.length; i++) {


A[i] = A[i]^one_quotient[i];
}

calc_quotient(diff_exponent);
division(A, B);
}
else {
final_quotient = quotient;
remainder = A;
quotient = new int[16];
}
}

public int[] multiplication(int[] A, int[] B) {


int[] product = new int[16];
int[] modeled_product = new int[16];
for(int i = 0 ; i < 8 ; i++) {
if(A[i] == 1) {
for (int j = 0; j < 8; j++) {
product[i+j] = product[i+j]^B[j];
}
}
}
modeled_product = product;
if(get_exponent(product) >= get_exponent(irreducible) -1 ) {
division(product, irreducible.clone());
modeled_product = remainder;
}
return modeled_product;
}

public static int mul(byte A, byte B) {


S_box s_box = new S_box();
int[] AA = s_box.byte_to_intarry(A);
int[] BB = s_box.byte_to_intarry(B);
int[] result = s_box.multiplication(AA, BB);
return s_box.intarry_to_byte(result);
}

private int[] subtract(int[] A, int[] B) {


int[] result = new int[A.length];
for (int i = 0; i < result.length; i++) {
result[i] = A[i]^B[i];
}
return result;
}

private int[] get_inverse(int[] bits) {


int[] inverse = new int[16];
int[][] r = new int[3][16];
int[] q = new int[16];
int[][] v = new int[3][16];
int[][] w = new int[3][16];

int[] terminator = new int[16];

r[0] = irreducible.clone();
r[1] = bits;

v[0][0] = 1;
w[1][0] = 1;

division(r[0], r[1]);
q = final_quotient;
r[2] = remainder;
while(!equals(remainder, terminator)) {
v[2] = subtract(v[0], multiplication(q, v[1]));
w[2] = subtract(w[0], multiplication(q, w[1]));
v[0] = v[1];
v[1] = v[2];
w[0] = w[1];
w[1] = w[2];
r[0] = r[1];
r[1] = r[2];
division(r[0], r[1]);
q = final_quotient;
r[2] = remainder;
}
inverse = w[1];

return inverse;
}

private boolean equals(int[] A, int[] B) {


for (int i = 0; i < B.length; i++) {
if(A[i] != B[i])
return false;
}
return true;
}
private int convert(int[] bits) {
int[] converted = new int[16];
int result = 0;
int exp = 1;
for(int i = 0 ; i < 8 ; i++) {
converted[i] = bits[i]^bits[(i+4)%8]^bits[(i+5)%8]^bits[(i+6)%8]^bits[(i+7)%8]^c[i];
result = result + converted[i]*exp;
exp = exp * 2;
}
return result;
}

public int[][] generate() {


int i = 0;
int j = 0;

for(; i < 16 ; i++) {


for(j = 0; j < 16 ; j++) {
int[] bits = new int[16];
if(i == 0 && j == 0) {

}
else {
byte data = (byte) ((i<<4) + j);
bits = get_inverse(byte_to_intarry(data));
}
s_box[i][j] = convert(bits);
// String string = String.format("%02X", s_box[i][j]);
// System.out.print(string+" ");
}
// System.out.println();
}

return s_box;
}

public int[] generate_one_dime(boolean show) {


generate();
if (show) System.out.println("s_box:");
for (int i = 0; i < 256; i++) {
s_box_one_dimen[i] = s_box[i/16][i%16];
if (show){
if(i%16 == 0 && i != 0)
System.out.println();
String string = String.format("%02X", s_box_one_dimen[i]);
System.out.print(string+" ");}
}
System.out.println();

return s_box_one_dimen;
}

public int[] generate_inverse_one_dime() {


generate_one_dime(false);
System.out.println("Invser s_box:");
for (int i = 0; i < inv_s_box_one_dimen.length; i++) {
inv_s_box_one_dimen[s_box_one_dimen[i]] = i;
}
for (int i = 0; i < inv_s_box_one_dimen.length; i++) {

if(i%16 == 0 && i != 0)
System.out.println();
String string = String.format("%02X", inv_s_box_one_dimen[i]);
System.out.print(string+" ");
}
System.out.println();
return inv_s_box_one_dimen;
}

public static void main(String[] args) {

}
}