Подробное введение и реализация алгоритма шифрования AES.
Подробное введение и реализация алгоритма шифрования AES.

Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.

Введение в AES

Advanced Encryption Standard (AES, Advanced Encryption Standard) — наиболее распространенный алгоритм симметричного шифрования (апплет WeChat использует этот алгоритм шифрования для зашифрованной передачи). Симметричный алгоритм шифрования означает, что для шифрования и дешифрования используется один и тот же ключ. Конкретный процесс шифрования выглядит следующим образом:

Ниже приводится краткое описание функций и значения каждой части:

  • Обычный текст P

Зашифрованных данных нет.

  • Ключ К

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

  • Функция шифрования AES

Предположим, что функция шифрования AES равна E, тогда C = E(K, P), где P — открытый текст, K — ключ, а C — зашифрованный текст. Другими словами, если открытый текст P и ключ K вводятся в качестве параметров функции шифрования, функция шифрования E выведет зашифрованный текст C.

  • Шифрованный текст C

Данные обрабатываются функцией шифрования

  • AES-расшифровкафункция

Предположим, что функция AES-расшифровки равна D, тогда P = D(K, C), где C — зашифрованный текст, K — ключ, а P — открытый текст. Другими словами, если зашифрованный текст C и ключ K вводятся в качестве параметров функции дешифрования, функция дешифрования выведет открытый текст P.

Вот краткое введение в разницу между симметричными алгоритмами шифрования и асимметричными алгоритмами шифрования.

  • Симметричный алгоритм шифрования

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

  • Нет Симметричный алгоритм шифрования

Ключи, используемые для шифрования и дешифрования, различаются. Этот метод шифрования основан на математически сложных задачах. Обычно скорость шифрования и дешифрования относительно низкая, что подходит для случаев, когда данные передаются время от времени. Преимущество в том, что передача ключей удобна. Распространенными алгоритмами асимметричного шифрования являются RSA, ECC и EIGamal.

На практике ключ AES обычно шифруется с помощью RSA и передается получателю. Получатель расшифровывает его, чтобы получить ключ AES, а затем отправитель и получатель используют ключ AES для связи.

Представление принципов AES, приведенное ниже в этой статье, относится к «Руководству по современной криптографии». Реализация AES начинается после ознакомления с принципами.

Базовая структура AES

AES — это блочный шифр. Блочный шифр делит открытый текст на группы, каждая группа имеет одинаковую длину, и шифрует одну группу данных за раз, пока не будет зашифрован весь открытый текст. В спецификации стандарта AES длина пакета может составлять только 128 бит, то есть каждый пакет имеет длину 16 байт (8 бит на байт). Длина ключа может составлять 128 бит, 192 бит или 256 бит. Длина ключа разная, и рекомендуемое количество раундов шифрования также разное, как показано в следующей таблице:

AES

Длина ключа (32-битное слово)

Длина пакета (32-битное слово)

Количество раундов шифрования

AES-128

4

4

10

AES-192

6

4

12

AES-256

8

4

14

Раунды представлены ниже,Здесь реализован AES-128.,То есть длина ключа 128 бит.,Количество раундов Скорость составляет 10 раундов. Как упоминалось выше, формула шифрования AES — C. = E(K,P), в функции шифрования E будет выполнена функция раунда, и эта функция раунда будет выполнена 10 раз. Первые 9 операций этой функции раунда одинаковы, и только 10-й раз отличается. Другими словами, пакет с открытым текстом будет шифроваться в течение 10 раундов. Суть AES заключается в реализации всех операций за один раунд.

Единицей обработки AES является байт. 128-битный входной пакет открытого текста P и входной ключ K разделены на 16 байтов, которые записываются как P = P0 P1... P15 и K = K0 K1... K15 соответственно. . Например, группировка открытого текста имеет вид P = abcdefghijklmnop, где символ a соответствует P0, а p соответствует P15. Обычно пакеты открытого текста описываются квадратной байтовой матрицей, называемой матрицей состояний. В каждом раунде алгоритма содержимое матрицы состояний постоянно меняется, и окончательный результат выводится в виде зашифрованного текста. Порядок байтов в этой матрице сверху вниз и слева направо, как показано на рисунке ниже:

Теперь предположив, что группа открытого текста P равна «abcdefghijklmnop», соответствующая диаграмма матрицы состояний, сгенерированная выше, будет выглядеть следующим образом:

На рисунке выше 0x61 — это шестнадцатеричное представление символа a. Как видите, простой текст изменился до неузнаваемости после шифрования с помощью AES.

Аналогично, 128-битный ключ также представлен матрицей байтов, и каждый столбец матрицы называется 32-битным словом. С помощью функции расположения ключей матрица ключей расширяется до последовательности из 44 слов W[0], W[1],… ,W[43], первые 4 элемента этой последовательности, W[0], W[1], W[2], W[3] — исходные ключи, используемые для начального добавления ключа в операции шифрования ( описано ниже) ); следующие 40 слов разделены на 10 групп, и каждая группа из 4 слов (128 бит) используется для добавления раундового ключа в 10 раундах операций шифрования, как показано на рисунке ниже:

На приведенном выше рисунке, если предположить K = «abcdefghijklmnop», тогда K0 = a, K15 = p, W[0] = K0 K1 K2 K3 = «abcd».

Общая структура AES показана на рисунке ниже, где W[0,3] относится к 128-битному ключу, состоящему из объединенных W[0], W[1], W[2] и W[3]. Раундовые функции с 1-го по 9-й раунды шифрования одинаковы и включают 4 операции: подстановку байтов, смещение строки, смешивание столбцов и добавление раундового ключа. Последняя итерация не выполняет смешивание столбцов. Кроме того, перед первым раундом итерации над открытым текстом и исходным ключом выполняется операция шифрования XOR.

На рисунке выше также показан процесс AES-расшифровки.,Процесс расшифровки по-прежнему длится 10 раундов.,Каждый раунд операции является операцией, обратной операции шифрования. Поскольку все четыре раунда операций AES обратимы.,поэтому,Один раунд операции дешифрования заключается в последовательном выполнении обратной последовательности. строки、обеспечить регресс Замена байта、круглый ключ плюсиобеспечить регресссмесь колонок. Аналогично операциям шифрования,最后一轮不执行обеспечить регресссмесь колонок,Перед первым раундом расшифровки,Необходимо выполнить операцию добавления ключей.

Ниже представлены четыре этапа работы одного раунда в AES. Эти четыре этапа операции позволяют полностью перепутать входные биты.

1. Замена байта

1. Операция замены байтов

Замена байтов в AES на самом деле представляет собой простую операцию поиска в таблице. AES определяет S-блок и обратный S-блок. Коробка AES S:

строка/столбец

0

1

2

3

4

5

6

7

8

9

A

B

C

D

E

F

0

0x63

0x7c

0x77

0x7b

0xf2

0x6b

0x6f

0xc5

0x30

0x01

0x67

0x2b

0xfe

0xd7

0xab

0x76

1

0xca

0x82

0xc9

0x7d

0xfa

0x59

0x47

0xf0

0xad

0xd4

0xa2

0xaf

0x9c

0xa4

0x72

0xc0

2

0xb7

0xfd

0x93

0x26

0x36

0x3f

0xf7

0xcc

0x34

0xa5

0xe5

0xf1

0x71

0xd8

0x31

0x15

3

0x04

0xc7

0x23

0xc3

0x18

0x96

0x05

0x9a

0x07

0x12

0x80

0xe2

0xeb

0x27

0xb2

0x75

4

0x09

0x83

0x2c

0x1a

0x1b

0x6e

0x5a

0xa0

0x52

0x3b

0xd6

0xb3

0x29

0xe3

0x2f

0x84

5

0x53

0xd1

0x00

0xed

0x20

0xfc

0xb1

0x5b

0x6a

0xcb

0xbe

0x39

0x4a

0x4c

0x58

0xcf

6

0xd0

0xef

0xaa

0xfb

0x43

0x4d

0x33

0x85

0x45

0xf9

0x02

0x7f

0x50

0x3c

0x9f

0xa8

7

0x51

0xa3

0x40

0x8f

0x92

0x9d

0x38

0xf5

0xbc

0xb6

0xda

0x21

0x10

0xff

0xf3

0xd2

8

0xcd

0x0c

0x13

0xec

0x5f

0x97

0x44

0x17

0xc4

0xa7

0x7e

0x3d

0x64

0x5d

0x19

0x73

9

0x60

0x81

0x4f

0xdc

0x22

0x2a

0x90

0x88

0x46

0xee

0xb8

0x14

0xde

0x5e

0x0b

0xdb

A

0xe0

0x32

0x3a

0x0a

0x49

0x06

0x24

0x5c

0xc2

0xd3

0xac

0x62

0x91

0x95

0xe4

0x79

B

0xe7

0xc8

0x37

0x6d

0x8d

0xd5

0x4e

0xa9

0x6c

0x56

0xf4

0xea

0x65

0x7a

0xae

0x08

C

0xba

0x78

0x25

0x2e

0x1c

0xa6

0xb4

0xc6

0xe8

0xdd

0x74

0x1f

0x4b

0xbd

0x8b

0x8a

D

0x70

0x3e

0xb5

0x66

0x48

0x03

0xf6

0x0e

0x61

0x35

0x57

0xb9

0x86

0xc1

0x1d

0x9e

E

0xe1

0xf8

0x98

0x11

0x69

0xd9

0x8e

0x94

0x9b

0x1e

0x87

0xe9

0xce

0x55

0x28

0xdf

F

0x8c

0xa1

0x89

0x0d

0xbf

0xe6

0x42

0x68

0x41

0x99

0x2d

0x0f

0xb0

0x54

0xbb

0x16

Элементы матрицы состояний отображаются в новый байт следующим образом: старшие 4 бита байта используются в качестве значения строки, младшие 4 бита используются в качестве значения столбца, а элементы соответствующей строки в S-блок или инверсный S-блок выводятся в качестве выхода. Например, при шифровании выходной байт S1 равен 0x12, затем проверьте строку 0x01 и столбец 0x02 поля S, чтобы получить значение 0xc9, а затем замените исходный 0x12 S1 на 0xc9. Схема матрицы состояний после замены байтов выглядит следующим образом:

2. Обратная операция замены байтов.

Обратная замена байтов заключается в поиске обратного S-блока для преобразования. Обратный S-блок выглядит следующим образом:

строка/столбец

0

1

2

3

4

5

6

7

8

9

A

B

C

D

E

F

0

0x52

0x09

0x6a

0xd5

0x30

0x36

0xa5

0x38

0xbf

0x40

0xa3

0x9e

0x81

0xf3

0xd7

0xfb

1

0x7c

0xe3

0x39

0x82

0x9b

0x2f

0xff

0x87

0x34

0x8e

0x43

0x44

0xc4

0xde

0xe9

0xcb

2

0x54

0x7b

0x94

0x32

0xa6

0xc2

0x23

0x3d

0xee

0x4c

0x95

0x0b

0x42

0xfa

0xc3

0x4e

3

0x08

0x2e

0xa1

0x66

0x28

0xd9

0x24

0xb2

0x76

0x5b

0xa2

0x49

0x6d

0x8b

0xd1

0x25

4

0x72

0xf8

0xf6

0x64

0x86

0x68

0x98

0x16

0xd4

0xa4

0x5c

0xcc

0x5d

0x65

0xb6

0x92

5

0x6c

0x70

0x48

0x50

0xfd

0xed

0xb9

0xda

0x5e

0x15

0x46

0x57

0xa7

0x8d

0x9d

0x84

6

0x90

0xd8

0xab

0x00

0x8c

0xbc

0xd3

0x0a

0xf7

0xe4

0x58

0x05

0xb8

0xb3

0x45

0x06

7

0xd0

0x2c

0x1e

0x8f

0xca

0x3f

0x0f

0x02

0xc1

0xaf

0xbd

0x03

0x01

0x13

0x8a

0x6b

8

0x3a

0x91

0x11

0x41

0x4f

0x67

0xdc

0xea

0x97

0xf2

0xcf

0xce

0xf0

0xb4

0xe6

0x73

9

0x96

0xac

0x74

0x22

0xe7

0xad

0x35

0x85

0xe2

0xf9

0x37

0xe8

0x1c

0x75

0xdf

0x6e

A

0x47

0xf1

0x1a

0x71

0x1d

0x29

0xc5

0x89

0x6f

0xb7

0x62

0x0e

0xaa

0x18

0xbe

0x1b

B

0xfc

0x56

0x3e

0x4b

0xc6

0xd2

0x79

0x20

0x9a

0xdb

0xc0

0xfe

0x78

0xcd

0x5a

0xf4

C

0x1f

0xdd

0xa8

0x33

0x88

0x07

0xc7

0x31

0xb1

0x12

0x10

0x59

0x27

0x80

0xec

0x5f

D

0x60

0x51

0x7f

0xa9

0x19

0xb5

0x4a

0x0d

0x2d

0xe5

0x7a

0x9f

0x93

0xc9

0x9c

0xef

E

0xa0

0xe0

0x3b

0x4d

0xae

0x2a

0xf5

0xb0

0xc8

0xeb

0xbb

0x3c

0x83

0x53

0x99

0x61

F

0x17

0x2b

0x04

0x7e

0xba

0x77

0xd6

0x26

0xe1

0x69

0x14

0x63

0x55

0x21

0x0c

0x7d

2. Сдвиг рядов

1. Операция смены рядов

Сдвиг строк — это простая операция смещения влево. При длине ключа 128 бит строка 0 матрицы состояний смещается влево на 0 байт, строка 1 смещается влево на 1 байт, строка 2 смещается влево на 2 байта, строка 3 смещается влево на 3 байта. , как показано ниже.

2. Обратное преобразование сдвига строки.

Обратное преобразование сдвига строк заключается в выполнении обратной операции сдвига над каждой строкой матрицы состояний. Например, в AES-128 строка 0 матрицы состояний сдвигается вправо на 0 байт, строка 1 сдвигается. вправо на 1 байт, а строка 2 сдвигается вправо на 1 байт. Строки сдвигаются вправо на 2 байта, а строка 3 сдвигается вправо на 3 байта.

3. Смешивание колонки

1. Операция смешивания колонки

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

Смесь столбцов j-го столбца (0 ≤ j ≤ 3) в матрице состояний может быть выражена, как показано на следующем рисунке:

Среди них умножение и сложение матричных элементов являются двоичными операциями, определенными на основе GF(2^8), и не являются умножением и сложением в обычном смысле. Это предполагает некоторые математические знания в области информационной безопасности, но ничего страшного, если вы не понимаете эти знания. Фактически сложение этой бинарной операции эквивалентно XOR двух байтов, а умножение немного сложнее. Для 8-битного двоичного числа использование умножения поля на (00000010) эквивалентно сдвигу влево на 1 бит (младшие биты заполняются 0), а затем выполнению операции XOR с (00011011) в зависимости от ситуации. Предположим, S1 =. (a7 a6 a5 a4 a3 a2 a1 a0), просто 0x02 * S1, как показано ниже:

То есть, если a7 равно 1, операция XOR выполняется, в противном случае она не выполняется. Аналогично, умножение на (00000100) можно разделить на две операции путем умножения (00000010):

Умножение (0000 0011) можно разделить на умножение (0000 0001) и (0000 0010) соответственно, а затем выполнить операцию XOR между двумя произведениями:

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

C9

E5

FD

2B

7A

F2

78

6E

63

9C

26

67

B0

A7

82

E5

Далее выполните операции перемешивания колонок: В качестве примера возьмем работу первого столбца:

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

D4

E7

CD

66

28

02

E5

BB

BE

C6

D6

BF

22

0F

DF

A5

2. Колонка смешанного обратного действия.

Обратное преобразование смеси столбцов можно определить путем умножения матриц, как показано на следующем рисунке:

Можно проверить, что произведение матрицы обратного преобразования и матрицы прямого преобразования является в точности единичной матрицей.

4. Добавление ключа раунда

Добавление раундового ключа заключается в выполнении побитовой операции XOR над данными в той же матрице состояний 128-битного раундового ключа Ki, как показано на рисунке ниже. Среди них каждое слово W[4i], W[4i+1], W[4i+2], W[4i+3] в ключе Ki представляет собой 32-битное слово, содержащее 4 байта. Алгоритм их генерации представлен ниже. . Процесс добавления раундового ключа можно рассматривать как результат побитового XOR или как операцию на уровне байта или бита. Другими словами, это можно рассматривать как операцию XOR 32-битного слова, состоящего из S0 S1 S2 S3 и W[4i].

Обратная операция добавления раундового ключа точно такая же, как и прямая операция добавления раундового ключа, поскольку обратная операция XOR сама по себе является обратной операцией. Добавление раундового ключа очень простое, но оно может повлиять на каждый бит массива S.

Ключевое расширение

AESпервый Воля Первоначальный ключ вводится в44 в матрице состояний, как показано на рисунке ниже.

Это 44для каждого столбца матрицы4байты составляют слово,4 слова в 4 столбцах матрицы последовательно называются W[0], W[1], W[2] и W[3].,Они образуют массив W в словах. Например,настраивать Ключ К — «abcdefghijklmnop», тогда K0 = ‘a’,K1 = ‘b’, K2 = ‘c’,K3 = ‘d’,W[0] = “abcd”。 Затем массив W расширяется на 40 новых столбцов, образуя расширенный массив ключей, содержащий в общей сложности 44 столбца. Новые столбцы генерируются рекурсивно следующим образом: 1. Если i не кратно 4, то i-й столбец определяется по следующему уравнению: W[i]=W[i-4]⨁W[i-1] 2. Если i кратно 4, то i-й столбец определяется по следующему уравнению: W[i]=W[i-4]⨁T(W[i-1]) Среди них T — довольно сложная функция. Функция T состоит из трех частей: цикла слов, замены байтов и операции XOR. Функции этих трех частей следующие. а. Ротация слова: круговой сдвиг 4 байтов в слове на 1 байт влево. Слово [b0, b1, b2, b3] преобразуется в [b1, b2, b3, b0]. б. Замена байтов: используйте поле S для замены байтов в результате цикла слов. c. Константа раунда XOR: XOR результатов первых двух шагов с константой раунда Rcon[j], где j представляет количество раундов. Круглая константа Rcon[j] — это слово, его значение показано в таблице ниже.

j

1

2

3

4

5

Rcon[j]

01 00 00 00

02 00 00 00

04 00 00 00

08 00 00 00

10 00 00 00

j

6

7

8

9

10

Rcon[j]

20 00 00 00

40 00 00 00

80 00 00 00

1B 00 00 00

36 00 00 00

Вот пример: Пусть исходный 128-битный ключ будет: 3C A1 0B 21 57 F0 19 16 90 2E 13 80 AC C1 07 BD Тогда 4 начальных значения: W[0] = 3C A1 0B 21 W[1] = 57 F0 19 16 W[2] = 90 2E 13 80 W[3] = AC C1 07 BD Затем найдите расширенные подразделы первого раунда (W[4], W[5], W[6], W[7]). Поскольку 4 кратно 4, то: W[4] = W[0] ⨁ Т(W[3]) Шаги расчета T(W[3]) следующие:

  1. Циклически сдвигаем элементы W[3]: AC C1 07 BD становится C1 07 BD AC;
  2. Приняв C1 07 BD AC в качестве входа блока S, выход будет 78 C5 7A 91;
  3. Операция исключающее ИЛИ между 78 C5 7A 91 и константой первого раунда Rcon[1] приведет к результату 79 C5 7A 91. Следовательно, T(W[3])=79 C5 7A 91, поэтому W[4] = 3C A1 0B 21 ⨁ 79 C5 7A 91 = 45 64 71 B0 Остальные 3 сегмента подраздела рассчитываются следующим образом: W[5] = W[1] ⨁ W[4] = 57 F0 19 16 ⨁ 45 64 71 B0 = 12 94 68 A6 W[6] = W[2] ⨁ W[5] =90 2E 13 80 ⨁ 12 94 68 A6 = 82 BA 7B 26 W[7] = W[3] ⨁ W[6] = AC C1 07 BD ⨁ 82 BA 7B 26 = 2E 7B 7C 9B Итак, ключ для первого раунда — 45 64 71 B0 12 94 68 A6 82 BA 7B 26 2E 7B 7C 9B.

AES-расшифровка

На картинке в начале статьи,Есть блок-схема AES-расшифровки.,Расшифровка может быть выполнена в соответствии с этой блок-схемой. Ниже представлен еще один эквивалентный режим расшифровки.,Блок-схема показана ниже. Этот эквивалентный режим дешифрования обеспечивает согласованность порядка использования преобразований в процессе дешифрования с порядком в процессе шифрования.,Просто замените исходное преобразование обратным преобразованием.

На этом принцип AES заканчивается. Далее будет описана реализация каждого раздела приведенного выше принципа. Полный код можно найти в конце статьи. В конце статьи представлены две полные программы: одну можно скомпилировать и запустить под Linux, а другую — под VC6.0.

Реализация алгоритма AES

Предварительный просмотр функции шифрования AES

В функции шифрования aes сначала выполняется Ключевое расширение, а затем прочитать строку длиной 128 бит в целочисленный массив 4*4. Этот массив является матрицей состояний. Например, pArray[0][0] = S0,pArray[1][0] = S1, pArray[0][1] = С4. Этот процесс чтения проходит через Это реализуется функцией ConvertToIntArray(). Функция каждого раунда работы модифицирует pArray, то есть запутывает матрицу состояний. После 10 раундов шифрования pArray будет преобразован обратно в строку и затем сохранен в массиве символов обычного текста p. Таким образом, после шифрования символы в строке открытого текста p являются зашифрованными символами. Этот процесс преобразования реализуется с помощью функции ConvertArrayToStr().

Язык кода:javascript
копировать
/**
 * параметр p: Массив простых текстовых строк.
 * параметр plen: Длина открытого текста.
 * параметр key: Строковый массив ключей.
 */
void aes(char *p, int plen, char *key){

    int keylen = strlen(key);
    if(plen == 0 || plen % 16 != 0) {
        printf("Длина символов обычного текста должна быть кратна 16!\n");
        exit(0);
    }

    if(!checkKeyLen(keylen)) {
        printf("Длина ключевого символа неправильная! Длина должна быть 16, 24и32. Текущая длина: %d\n",keylen);
        exit(0);
    }

    ExtendKey(key);//расширяем ключ
    int pArray[4][4];

    for(int k = 0; k < plen; k += 16) {
        convertToIntArray(p + k, pArray);

        addRoundKey(pArray, 0); //Добавляем ключ начального раунда

        for(int i = 1; i < 10; i++){//Первые 9 раундов

            subBytes(pArray);//Подстановка байтов

            shiftRows(pArray);//сдвиг строки

            mixColumns(pArray);//Смешение столбцов

            addRoundKey(pArray, i);

        }

        //Раунд 10
        subBytes(pArray);//Подстановка байтов

        shiftRows(pArray);//сдвиг строки

        addRoundKey(pArray, 10);

        convertArrayToStr(pArray, p + k);
    }
}

1.Ключевое расширение Реализация

перед началом шифрования,Сначала вы должны получить ключ, используемый для первого раунда шифрования.,Так что сначала осознай это Ключевое расширение Ниже Ключевое Реализация функции расширения. Эта функция передает строковое представление ключа ключа, а затем считывает от W[0] до W[3] из строки. Для реализации этой функции используется функция getWordFromStr(). После чтения ключ начинает расширяться. Когда i кратно 4, будет вызвана функция T() для расширения. Поскольку поведение функции T связано с количеством раундов шифрования, количество раундов шифрования. должно быть j Передайте его как параметр.

Язык кода:javascript
копировать
//Расширенный массив, соответствующий ключу
static int w[44];

/**
 * Расширение ключевых результатов приводит к инициализации каждого элемента в w[44]
 */
static void extendKey(char *key) {
    for(int i = 0; i < 4; i++)
        w[i] = getWordFromStr(key + i * 4); 

    for(int i = 4, j = 0; i < 44; i++) {
        if( i % 4 == 0) {
            w[i] = w[i - 4] ^ T(w[i - 1], j); 
            j++;//Следующий раунд
        }else {
            w[i] = w[i - 4] ^ w[i - 1]; 
        }
    }   

}

Ниже приведена кодовая реализация функции T(). Функция T() получает два параметра. Параметр num — это W[i – 1], переданный выше, а round — это количество раундов шифрования. Во-первых, используйте numArray для хранения 4 байтов из 32-битного W[i-1]. Если W[i-1] равен 0x12ABCDEF, то numArray[0] = 0x12, numArray[1] = 0xAB. Функция SplitIntToArray() используется для чтения этих четырех байтов из 32-битного целого числа. Это сделано потому, что с целочисленными массивами проще работать. Затем вызовите функцию leftLoop4int(), чтобы по кругу сдвинуть четыре элемента массива numArray влево на 1 позицию. Затем выполните замену байтов и используйте функцию getNumFromSBox(), чтобы получить соответствующее значение в поле S для замены значения в numArray. Затем объедините байт-замещенный numArray обратно в 32-битное целое число с помощью функции mergeArrayToInt() и вернитесь после выполнения округленной константы XOR.

Язык кода:javascript
копировать
/**
 * список постоянных дежурств
 */
static const int Rcon[10] = { 0x01000000, 0x02000000,
    0x04000000, 0x08000000,
    0x10000000, 0x20000000,
    0x40000000, 0x80000000,
    0x1b000000, 0x36000000 };
/**
 * Ключевое Т-функция в расширении
 */
static int T(int num, int round) {
    int numArray[4];
    splitIntToArray(num, numArray);
    leftLoop4int(numArray, 1);//Словарный цикл

    //Подстановка байтов
    for(int i = 0; i < 4; i++)
        numArray[i] = getNumFromSBox(numArray[i]);

    int result = mergeArrayToInt(numArray);
    return result ^ Rcon[round];
}

2. Реализация подстановки байтов

Код подстановки байтов очень прост: каждый элемент матрицы состояний передается в функцию getNumFromSBox(), затем получаются старшие 4 бита из первых 8 бит в качестве значения строки, а младшие 4 бита — в качестве значения столбца. , а затем верните S [row][col], где S — массив, в котором хранятся S блоков.

Язык кода:javascript
копировать
/**
 * Получить элементы из S-блока на основе индекса
 */
static int getNumFromSBox(int index) {
    int row = getLeft4Bit(index);
    int col = getRight4Bit(index);
    return S[row][col];
}

/**
 * Замена байта
 */
static void subBytes(int array[4][4]){
    for(int i = 0; i < 4; i++)
        for(int j = 0; j < 4; j++)
            array[i][j] = getNumFromSBox(array[i][j]);
}

3. Реализация сдвига строк

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

Язык кода:javascript
копировать
/**
 * Элементы массива Воля циклически сдвигаются влево по битам.
 */
static void leftLoop4int(int array[4], int step) {
    int temp[4];
    for(int i = 0; i < 4; i++)
        temp[i] = array[i];

    int index = step % 4 == 0 ? 0 : step % 4;
    for(int i = 0; i < 4; i++){
        array[i] = temp[index];
        index++;
        index = index % 4;
    }
}

/**
 * сдвиг строки
 */
static void shiftRows(int array[4][4]) {
    int rowTwo[4], rowThree[4], rowFour[4];
    //2-я, 3-я и 4-я строки //копировать матрицу состояний
    for(int i = 0; i < 4; i++) {
        rowTwo[i] = array[1][i];
        rowThree[i] = array[2][i];
        rowFour[i] = array[3][i];
    }
    // Цикл влево и сдвиг на соответствующее количество цифр
    leftLoop4int(rowTwo, 1);
    leftLoop4int(rowThree, 2);
    leftLoop4int(rowFour, 3);

    //Вернем сдвинутую влево строку в матрицу состояний
    for(int i = 0; i < 4; i++) {
        array[1][i] = rowTwo[i];
        array[2][i] = rowThree[i];
        array[3][i] = rowFour[i];
    }
}

4. Осуществление смешивания колонок

В функции смешивания столбцов сначала скопируйте исходное состояние матрицы состояний в tempArray, а затем умножьте tempArray на матрицу colM. colM — это массив, в котором хранится постоянная матрица, подлежащая умножению. Функция GFMul() определяет умножение при умножении матриц, а сложение реализуется непосредственно через XOR. GFMul() реализует умножение, вызывая функцию, соответствующую умножению каждого числа. Например, S1 * 2 просто реализуется вызовом GFMul2(S1). S1 * 3 был только что реализован через GFMul3(S1). Здесь достаточно в основном реализовать функцию GFMul2(), а все остальное можно реализовать через комбинацию GFMul2(). Например, чтобы вычислить следующее уравнение, вам нужно вызвать функцию следующим образом:

s = GFMul3(0xC9) ^ 0x7A ^ 0x63 ^ GFMul2(0xB0)

Язык кода:javascript
копировать
/**
 * Матрица, которая будет использоваться для смешивания колонок
 */
static const int colM[4][4] = { 2, 3, 1, 1,
    1, 2, 3, 1,
    1, 1, 2, 3,
    3, 1, 1, 2 };

static int GFMul2(int s) {
    int result = s << 1;
    int a7 = result & 0x00000100;

    if(a7 != 0) {
        result = result & 0x000000ff;
        result = result ^ 0x1b;
    }

    return result;
}

static int GFMul3(int s) {
    return GFMul2(s) ^ s;
}

/**
 * Бинарные операции над GF
 */
static int GFMul(int n, int s) {
    int result;

    if(n == 1)
        result = s;
    else if(n == 2)
        result = GFMul2(s);
    else if(n == 3)
        result = GFMul3(s);
    else if(n == 0x9)
        result = GFMul9(s);
    else if(n == 0xb)//11
        result = GFMul11(s);
    else if(n == 0xd)//13
        result = GFMul13(s);
    else if(n == 0xe)//14
        result = GFMul14(s);

    return result;
}

/**
 * смесь колонок
 */
static void mixColumns(int array[4][4]) {

    int tempArray[4][4];

    for(int i = 0; i < 4; i++)
        for(int j = 0; j < 4; j++)
            tempArray[i][j] = array[i][j];

    for(int i = 0; i < 4; i++)
        for(int j = 0; j < 4; j++){
            array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j])
                ^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
        }
}

5. Реализация добавления ключей раунда.

Реализация добавления ключей раунда очень проста: выполняется XOR матрицы состояний с соответствующим W[i] в ​​соответствии с номером входящего раунда.

Язык кода:javascript
копировать
/**
 * круглый ключ плюс
 */
static void addRoundKey(int array[4][4], int round) {
    int warray[4];
    for(int i = 0; i < 4; i++) {

        splitIntToArray(w[ round * 4 + i], warray);

        for(int j = 0; j < 4; j++) {
            array[j][i] = array[j][i] ^ warray[j];
        }
    }
}

AES-расшифровкафункция

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

Язык кода:javascript
копировать
/**
 * параметр c: Строковый массив зашифрованного текста.
 * параметр clen: Длина зашифрованного текста.
 * параметр key: Строковый массив ключей.
 */
void deAes(char *c, int clen, char *key) {

    int keylen = strlen(key);
    if(clen == 0 || clen % 16 != 0) {
        printf("Длина символов зашифрованного текста должна быть кратна 16! Текущая длина: %d\n", clen);
        exit(0);
    }

    if(!checkKeyLen(keylen)) {
        printf("Длина ключевого символа неправильная! Длина должна быть 16, 24и32. Текущая длина: %d\n",keylen);
        exit(0);
    }

    ExtendKey(key);//расширяем ключ
    int cArray[4][4];
    for(int k = 0; k < clen; k += 16) {
        convertToIntArray(c + k, cArray);


        addRoundKey(cArray, 10);

        int wArray[4][4];
        for(int i = 9; i >= 1; i--) {
            deSubBytes(cArray);

            deShiftRows(cArray);

            deMixColumns(cArray);
            getArrayFrom4W(i, wArray);
            deMixColumns(wArray);

            addRoundTowArray(cArray, wArray);
        }

        deSubBytes(cArray);

        deShiftRows(cArray);

        addRoundKey(cArray, 0);

        convertArrayToStr(cArray, c + k);

    }
}

Полный код программы

версия Linux

aes.h

Язык кода:javascript
копировать
#ifndef AES_H
#define AES_H

/**
 * параметр p: Массив простых текстовых строк.
 * параметр plen: Длина открытого текста должна быть кратна 16.
 * параметр key: Строковый массив ключей.
 */
void aes(char *p, int plen, char *key);

/**
 * параметр c: Строковый массив зашифрованного текста.
 * параметр clen: Длина зашифрованного текста должна быть кратна 16.
 * параметр key: Строковый массив ключей.
 */
void deAes(char *c, int clen, char *key);

#endif

aes.c

Язык кода:javascript
копировать
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"

/**
 * S-бокс
 */
static const int S[16][16] = { 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 };

/**
 * обеспечить регрессS-бокс
 */
static const int S2[16][16] = { 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 };

/**
 * Получите левые 4 бита младших 8 бит целочисленных данных.
 */
static int getLeft4Bit(int num) {
	int left = num & 0x000000f0;
	return left >> 4;
}

/**
 * Получите правильные 4 бита из младших 8 бит целочисленных данных.
 */
static int getRight4Bit(int num) {
	return num & 0x0000000f;
}
/**
 * Получить элементы из S-блока на основе индекса
 */
static int getNumFromSBox(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S[row][col];
}

/**
 * Преобразовать символ в целое число
 */
static int getIntFromChar(char c) {
	int result = (int) c;
	return result & 0x000000ff;
}

/**
 * Преобразуйте 16 символов в массив 4X4,
 * Порядок байтов в этой матрице сверху вниз,
 * Расположено слева направо.
 */
static void convertToIntArray(char *str, int pa[4][4]) {
	int k = 0;
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * Распечатать массив 4X4
 */
static void printArray(int a[4][4]) {
	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
		printf("\n");
	}
	printf("\n");
}

/**
 * Распечатайте ASCII строки,
 * Отображается в шестнадцатеричном формате.
 */
static void printASSCI(char *str, int len) {
	for(int i = 0; i < len; i++)
		printf("0x%x ", getIntFromChar(str[i]));
	printf("\n");
}

/**
 * Объедините 4 последовательных символа в 4-байтовое целое число.
 */
static int getWordFromStr(char *str) {
	int one = getIntFromChar(str[0]);
	one = one << 24;
	int two = getIntFromChar(str[1]);
	two = two << 16;
	int three = getIntFromChar(str[2]);
	three = three << 8;
	int four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * Выньте первый, второй, третий и четвертый байты 4-байтового числа,
 * Введите целочисленный массив из 4 элементов.
 */
static void splitIntToArray(int num, int array[4]) {
	int one = num >> 24;
	array[0] = one & 0x000000ff;
	int two = num >> 16;
	array[1] = two & 0x000000ff;
	int three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * Элементы массива Воля циклически сдвигаются влево по битам.
 */
static void leftLoop4int(int array[4], int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	for(int i = 0; i < 4; i++){
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * Возьмите первый, второй, третий и четвертый элементы массива как
 * Первый, второй, третий и четвертый байты 4-байтового целого числа объединяются в 4-байтовое целое число.
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * список постоянных дежурств
 */
static const int Rcon[10] = { 0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 };
/**
 * Ключевое Т-функция в расширении
 */
static int T(int num, int round) {
	int numArray[4];
	splitIntToArray(num, numArray);
	leftLoop4int(numArray, 1);//Словарный цикл

	//Подстановка байтов
	for(int i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	int result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//Расширенный массив, соответствующий ключу
static int w[44];

/**
 * Расширение ключевых результатов приводит к инициализации каждого элемента в w[44]
 */
static void extendKey(char *key) {
	for(int i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for(int i = 4, j = 0; i < 44; i++) {
		if( i % 4 == 0) {
			w[i] = w[i - 4] ^ T(w[i - 1], j);
			j++;//Следующий раунд
		}else {
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}

}

/**
 * круглый ключ плюс
 */
static void addRoundKey(int array[4][4], int round) {
	int warray[4];
	for(int i = 0; i < 4; i++) {

		splitIntToArray(w[ round * 4 + i], warray);

		for(int j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * Замена байта
 */
static void subBytes(int array[4][4]){
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * сдвиг строки
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	//2-я, 3-я и 4-я строки //копировать матрицу состояний
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}
	// Цикл влево и сдвиг на соответствующее количество цифр
	leftLoop4int(rowTwo, 1);
	leftLoop4int(rowThree, 2);
	leftLoop4int(rowFour, 3);

	//Вернем сдвинутую влево строку в матрицу состояний
	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * Матрица, которая будет использоваться для смешивания колонок
 */
static const int colM[4][4] = { 2, 3, 1, 1,
	1, 2, 3, 1,
	1, 1, 2, 3,
	3, 1, 1, 2 };

static int GFMul2(int s) {
	int result = s << 1;
	int a7 = result & 0x00000100;

	if(a7 != 0) {
		result = result & 0x000000ff;
		result = result ^ 0x1b;
	}

	return result;
}

static int GFMul3(int s) {
	return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
	return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
	return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
	return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
	return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
	return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
	return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
	return GFMul12(s) ^ GFMul2(s);
}

/**
 * Бинарные операции над GF
 */
static int GFMul(int n, int s) {
	int result;

	if(n == 1)
		result = s;
	else if(n == 2)
		result = GFMul2(s);
	else if(n == 3)
		result = GFMul3(s);
	else if(n == 0x9)
		result = GFMul9(s);
	else if(n == 0xb)//11
		result = GFMul11(s);
	else if(n == 0xd)//13
		result = GFMul13(s);
	else if(n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * смесь колонок
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j]) 
				^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
		}
}
/**
 * Преобразовать массив 4X4 обратно в строку
 */
static void convertArrayToStr(int array[4][4], char *str) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			*str++ = (char)array[j][i];	
}
/**
 * Проверьте длину ключа
 */
static int checkKeyLen(int len) {
	if(len == 16)
		return 1;
	else
		return 0;
}

/**
 * параметр p: Массив простых текстовых строк.
 * параметр plen: Длина открытого текста.
 * параметр key: Строковый массив ключей.
 */
void aes(char *p, int plen, char *key){

	int keylen = strlen(key);
	if(plen == 0 || plen % 16 != 0) {
		printf("Длина символов обычного текста должна быть кратна 16!\n");
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("Длина ключевого символа неправильная! Длина должна быть 16, 24и32. Текущая длина: %d\n",keylen);
		exit(0);
	}

	ExtendKey(key);//расширяем ключ
	int pArray[4][4];

	for(int k = 0; k < plen; k += 16) {	
		convertToIntArray(p + k, pArray);

		addRoundKey(pArray, 0); //Добавляем ключ начального раунда

		for(int i = 1; i < 10; i++){//Первые 9 раундов

			subBytes(pArray);//Подстановка байтов

			shiftRows(pArray);//сдвиг строки

			mixColumns(pArray);//Смешение столбцов

			addRoundKey(pArray, i);

		}

		//Раунд 10
		subBytes(pArray);//Подстановка байтов

		shiftRows(pArray);//сдвиг строки

		addRoundKey(pArray, 10);

		convertArrayToStr(pArray, p + k);
	}
}
/**
 * по индексу从обеспечить регрессS-бокс中获取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * Обратное преобразование байтов
 */
static void deSubBytes(int array[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * Круговой сдвиг массива из 4 элементов вправо по битам.
 */
static void rightLoop4int(int array[4], int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for(int i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * обеспечить регресссдвиг строки
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo, 1);
	rightLoop4int(rowThree, 2);
	rightLoop4int(rowFour, 3);

	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * обеспечить регресссмесь Матрица, используемая колонками
 */
static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
	0x9, 0xe, 0xb, 0xd,
	0xd, 0x9, 0xe, 0xb,
	0xb, 0xd, 0x9, 0xe };

/**
 * обеспечить регресссмесь колонок
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(deColM[i][0],tempArray[0][j]) ^ GFMul(deColM[i][1],tempArray[1][j]) 
				^ GFMul(deColM[i][2],tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
		}
}
/**
 * Исключающее ИЛИ два массива 4X4
 */
static void addRoundTowArray(int aArray[4][4],int bArray[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * Получите массив 4X4 из четырех 32-битных ключевых слов:
 * 用于进行обеспечить регресссмесь колонок
 */
static void getArrayFrom4W(int i, int array[4][4]) {
	int index = i * 4;
	int colOne[4], colTwo[4], colThree[4], colFour[4];
	splitIntToArray(w[index], colOne);
	splitIntToArray(w[index + 1], colTwo);
	splitIntToArray(w[index + 2], colThree);
	splitIntToArray(w[index + 3], colFour);

	for(int i = 0; i < 4; i++) {
		array[i][0] = colOne[i];
		array[i][1] = colTwo[i];
		array[i][2] = colThree[i];
		array[i][3] = colFour[i];
	}

}

/**
 * параметр c: Строковый массив зашифрованного текста.
 * параметр clen: Длина зашифрованного текста.
 * параметр key: Строковый массив ключей.
 */
void deAes(char *c, int clen, char *key) {

	int keylen = strlen(key);
	if(clen == 0 || clen % 16 != 0) {
		printf("Длина символов зашифрованного текста должна быть кратна 16! Текущая длина: %d\n", clen);
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("Длина ключевого символа неправильная! Длина должна быть 16, 24и32. Текущая длина: %d\n",keylen);
		exit(0);
	}

	ExtendKey(key);//расширяем ключ
	int cArray[4][4];
	for(int k = 0; k < clen; k += 16) {
		convertToIntArray(c + k, cArray);


		addRoundKey(cArray, 10);

		int wArray[4][4];
		for(int i = 9; i >= 1; i--) {
			deSubBytes(cArray);

			deShiftRows(cArray);

			deMixColumns(cArray);
			getArrayFrom4W(i, wArray);
			deMixColumns(wArray);

			addRoundTowArray(cArray, wArray);
		}

		deSubBytes(cArray);

		deShiftRows(cArray);

		addRoundKey(cArray, 0);

		convertArrayToStr(cArray, c + k);

	}
}

main.c

Язык кода:javascript
копировать
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "aes.h"

#define MAXLEN 1024

void getString(char *str, int len){

	int slen = read(0, str, len);
	for(int i = 0; i < slen; i++,str++){
		if(*str == '\n'){
			*str = '\0';
			break;
		}
	}
}

void printASCCI(char *str, int len) {
	int c;
	for(int i = 0; i < len; i++) {
		c = (int)*str++;
		c = c & 0x000000ff;
		printf("0x%x ", c);
	}
	printf("\n");
}

/**
 * Чтение введенной пользователем строки из стандартного ввода
 */
void readPlainText(char *str, int *len) {
	int plen;
	while(1) {
		getString(str, MAXLEN);
		plen = strlen(str);
		if(plen != 0 && plen % 16 == 0) {
			printf("Введенный вами простой текст: %s\n", str);
			break;
		}else{
			printf("Длина символов обычного текста должна быть кратна 16, текущая длина – %d\n", plen);
		}
	}
	*len = plen;
}
/**
 * Записать строку в файл
 */
void writeStrToFile(char *str, int len, char *fileName) {
	FILE *fp;
	fp = fopen(fileName, "wb");
	for(int i = 0; i < len; i++)
		putc(str[i], fp);
	fclose(fp);
}


void aesStrToFile(char *key) {

	char p[MAXLEN];
	int plen;
	printf("Пожалуйста, введите открытый текст, длина символов открытого текста должна быть кратна 16\n");
	readPlainText(p,&plen);
	printf("Выполнить шифрование AES.............\n");

	aes(p, plen, ключ);//AES-шифрование

	printf("ASCCI зашифрованного открытого текста:\n");
	printASCCI(p, plen);
	char fileName[64];
	printf("Пожалуйста, введите имя файла, который вы хотите записать, например, 'test.txt':\n");
	if(scanf("%s", fileName) == 1) {	
		writeStrToFile(p, plen, fileName);
		printf("Зашифрованный текст записан в %s и его можно найти в текущем каталоге, где запущена программа.\n", fileName);
	}
}
/**
 * Читать строку из файла
 */
int readStrFromFile(char *fileName, char *str) {
	FILE *fp = fopen(fileName, "rb");
	if(fp == NULL) {
		printf("Ошибка открытия файла. Убедитесь, что файл существует в текущем каталоге!\n");
		exit(0);
	}

	int i;
	for(i = 0; i < MAXLEN && (str[i] = getc(fp)) != EOF; i++);

	if(i >= MAXLEN) {
		printf("Расшифрованный файл слишком велик!\n");
		exit(0);
	}

	str[i] = '\0';
	fclose(fp);
	return i;
}


void deAesFile(char *key) {
	char fileName[64];
	char c[MAXLEN];//Частная строка
	printf("Пожалуйста, введите имя файла, который нужно расшифровать. Файл должен находиться в том же каталоге, что и эта программа\n");
	if(scanf("%s", fileName) == 1) {
		int clen = readStrFromFile(fileName, c);
		printf("Начать расшифровку...\n");
		deAes(c, clen, key);
		printf("Расшифрованный открытый текст ASCII:\n");
		printASCCI(c, clen);
		printf("Обычный текст: %s\n", c);
		writeStrToFile(c,clen,fileName);
		printf("Теперь вы можете открыть %s, чтобы просмотреть расшифрованный зашифрованный текст!\n",fileName);
	}
}

void aesFile(char *key) {
	char fileName[64];
	char fileP[MAXLEN];

	printf("Пожалуйста, введите имя файла, который нужно зашифровать. Файл должен находиться в том же каталоге, что и эта программа\n");
	if(scanf("%s", fileName) == 1) {
		readStrFromFile(fileName, fileP);
		int plen = strlen(fileP);
		printf("Начать шифрование...\n");
		printf("Символы ASCII в файле перед шифрованием: \n");
		printASCCI(fileP, plen);

		aes(fileP, plen, ключ);//Начать шифрование

		printf("Зашифрованный зашифрованный текст ASCII:\n");
		printASCCI(fileP, plen);
		writeStrToFile(fileP,plen,fileName);
		printf("Зашифрованный текст записан в %s\n", fileName);
	}
}

int main(int argc, char const *argv[]) {

	char key[17];
	printf("Пожалуйста, введите 16-значный ключ:\n");
	int klen;
	while(1){
		getString(key,17);
		klen = strlen(key);
		if(klen != 16){
			printf("Пожалуйста, введите 16-значный ключ, длина текущего ключа — %d\n",klen);
		}else{
			printf("Введенный вами ключ: %s\n",key);
			break;
		}
	}

	printf("Введите 's', чтобы зашифровать входную строку и записать зашифрованное содержимое в файл\n");
	printf("Введите нужные параметры функции и нажмите Enter. Введите 'f', чтобы зашифровать файл\n");
	printf("Введите 'p', чтобы расшифровать файл\n");
	char c;
	if(scanf("%s",&c) == 1) {
		if(c == 's')
			aesStrToFile(key);//Используйте AES для шифрования строки и записи строки в файл
		else if(c == 'p')
			deAesFile(key);//Расшифруем зашифрованный текст в файле и запишем его обратно в файл
		else if(c == 'f')//Шифрование файлов с помощью AES
			aesFile(key);
	}
	return 0;
}

Скомпилируйте и выполните следующую команду gcc:

Язык кода:javascript
копировать
gcc -o aes aes.c main.c

Версия ВК6.0

Поскольку компилятор VC6.0 довольно сложен, переменные необходимо сначала объявить, а затем использовать, поэтому код необходимо соответствующим образом изменить.

aes.h

Язык кода:javascript
копировать
#ifndef MY_AES_H
#define MY_AES_H

/**
 * параметр p: Массив простых текстовых строк.
 * параметр plen: Длина открытого текста должна быть кратна 16.
 * параметр key: Строковый массив ключей.
 */
void aes(char *p, int plen, char *key);

/**
 * параметр c: Строковый массив зашифрованного текста.
 * параметр clen: Длина зашифрованного текста должна быть кратна 16.
 * параметр key: Строковый массив ключей.
 */
void deAes(char *c, int clen, char *key);

#endif

aes.cpp

Язык кода:javascript
копировать
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "aes.h"

/**
 * S-бокс
 */
static const int S[16][16] = { 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 };

/**
 * обеспечить регрессS-бокс
 */
static const int S2[16][16] = { 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 };

/**
 * Получите левые 4 бита младших 8 бит целочисленных данных.
 */
static int getLeft4Bit(int num) {
	int left = num & 0x000000f0;
	return left >> 4;
}

/**
 * Получите правильные 4 бита из младших 8 бит целочисленных данных.
 */
static int getRight4Bit(int num) {
	return num & 0x0000000f;
}
/**
 * Получить элементы из S-блока на основе индекса
 */
static int getNumFromSBox(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S[row][col];
}

/**
 * Преобразовать символ в целое число
 */
static int getIntFromChar(char c) {
	int result = (int) c;
	return result & 0x000000ff;
}

/**
 * Преобразуйте 16 символов в массив 4X4,
 * Порядок байтов в этой матрице сверху вниз,
 * Расположено слева направо.
 */
static void convertToIntArray(char *str, int pa[4][4]) {
	int k = 0;
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * Распечатать массив 4X4
 */
static void printArray(int a[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++){
		for(j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
		printf("\n");
	}
	printf("\n");
}

/**
 * Распечатайте ASCII строки,
 * Отображается в шестнадцатеричном формате.
 */
static void printASSCI(char *str, int len) {
	int i;
	for(i = 0; i < len; i++)
		printf("0x%x ", getIntFromChar(str[i]));
	printf("\n");
}

/**
 * Объедините 4 последовательных символа в 4-байтовое целое число.
 */
static int getWordFromStr(char *str) {
	int one, two, three, four;
	one = getIntFromChar(str[0]);
	one = one << 24;
	two = getIntFromChar(str[1]);
	two = two << 16;
	three = getIntFromChar(str[2]);
	three = three << 8;
	four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * Выньте первый, второй, третий и четвертый байты 4-байтового числа,
 * Введите целочисленный массив из 4 элементов.
 */
static void splitIntToArray(int num, int array[4]) {
	int one, two, three;
	one = num >> 24;
	array[0] = one & 0x000000ff;
	two = num >> 16;
	array[1] = two & 0x000000ff;
	three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * Элементы массива Воля циклически сдвигаются влево по битам.
 */
static void leftLoop4int(int array[4], int step) {
	int temp[4];
	int i;
	int index;
	for(i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	for(i = 0; i < 4; i++){
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * Возьмите первый, второй, третий и четвертый элементы массива как
 * Первый, второй, третий и четвертый байты 4-байтового целого числа объединяются в 4-байтовое целое число.
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * список постоянных дежурств
 */
static const int Rcon[10] = { 0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 };
/**
 * Ключевое Т-функция в расширении
 */
static int T(int num, int round) {
	int numArray[4];
	int i;
	int result;
	splitIntToArray(num, numArray);
	leftLoop4int(numArray, 1);//Словарный цикл

	//Подстановка байтов
	for(i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//Расширенный массив, соответствующий ключу
static int w[44];
/**
 * Распечатать массив W
 */
static void printW() {
	int i, j;
	for(i = 0, j = 1; i < 44; i++,j++){
		printf("w[%d] = 0x%x ", i, w[i]);
		if(j % 4 == 0)
			printf("\n");
	}
	printf("\n");
}


/**
 * Расширение ключевых результатов приводит к инициализации каждого элемента в w[44]
 */
static void extendKey(char *key) {
	int i,j;
	for(i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for(i = 4, j = 0; i < 44; i++) {
		if( i % 4 == 0) {
			w[i] = w[i - 4] ^ T(w[i - 1], j);
			j++;//Следующий раунд
		}else {
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}

}

/**
 * круглый ключ плюс
 */
static void addRoundKey(int array[4][4], int round) {
	int warray[4];
	int i,j;
	for(i = 0; i < 4; i++) {

		splitIntToArray(w[ round * 4 + i], warray);

		for(j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * Замена байта
 */
static void subBytes(int array[4][4]){
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * сдвиг строки
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	int i;
	for(i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	leftLoop4int(rowTwo, 1);
	leftLoop4int(rowThree, 2);
	leftLoop4int(rowFour, 3);

	for(i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * Матрица, которая будет использоваться для смешивания колонок
 */
static const int colM[4][4] = { 2, 3, 1, 1,
	1, 2, 3, 1,
	1, 1, 2, 3,
	3, 1, 1, 2 };

static int GFMul2(int s) {
	int result = s << 1;
	int a7 = result & 0x00000100;

	if(a7 != 0) {
		result = result & 0x000000ff;
		result = result ^ 0x1b;
	}

	return result;
}

static int GFMul3(int s) {
	return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
	return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
	return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
	return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
	return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
	return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
	return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
	return GFMul12(s) ^ GFMul2(s);
}

/**
 * Бинарные операции над GF
 */
static int GFMul(int n, int s) {
	int result;

	if(n == 1)
		result = s;
	else if(n == 2)
		result = GFMul2(s);
	else if(n == 3)
		result = GFMul3(s);
	else if(n == 0x9)
		result = GFMul9(s);
	else if(n == 0xb)//11
		result = GFMul11(s);
	else if(n == 0xd)//13
		result = GFMul13(s);
	else if(n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * смесь колонок
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++){
			array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j])
				^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
		}
}
/**
 * Преобразовать массив 4X4 обратно в строку
 */
static void convertArrayToStr(int array[4][4], char *str) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			*str++ = (char)array[j][i];
}
/**
 * Проверьте длину ключа
 */
static int checkKeyLen(int len) {
	if(len == 16)
		return 1;
	else
		return 0;
}


/**
 * параметр p: Массив простых текстовых строк.
 * параметр plen: Длина открытого текста.
 * параметр key: Строковый массив ключей.
 */
void aes(char *p, int plen, char *key){

	int keylen = strlen(key);
	int pArray[4][4];
	int k,i;

	if(plen == 0 || plen % 16 != 0) {
		printf("Длина символов обычного текста должна быть кратна 16!\n");
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("Длина ключевого символа неправильная! Длина должна быть 16. Текущая длина: %d\n",keylen);
		exit(0);
	}

	ExtendKey(key);//расширяем ключ

	for(k = 0; k < plen; k += 16) {
		convertToIntArray(p + k, pArray);

		addRoundKey(pArray, 0); //Добавляем ключ начального раунда

		for(i = 1; i < 10; i++){

			subBytes(pArray);//Подстановка байтов

			shiftRows(pArray);//сдвиг строки

			mixColumns(pArray);//Смешение столбцов

			addRoundKey(pArray, i);

		}

		subBytes(pArray);//Подстановка байтов

		shiftRows(pArray);//сдвиг строки

		addRoundKey(pArray, 10);

		convertArrayToStr(pArray, p + k);
	}
}
/**
 * по индексу从обеспечить регрессS-бокс中获取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * Обратное преобразование байтов
 */
static void deSubBytes(int array[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * Круговой сдвиг массива из 4 элементов вправо по битам.
 */
static void rightLoop4int(int array[4], int step) {
	int temp[4];
	int i;
	int index;
	for(i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for(i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * обеспечить регресссдвиг строки
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	int i;
	for(i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo, 1);
	rightLoop4int(rowThree, 2);
	rightLoop4int(rowFour, 3);

	for(i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * обеспечить регресссмесь Матрица, используемая колонками
 */
static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
	0x9, 0xe, 0xb, 0xd,
	0xd, 0x9, 0xe, 0xb,
	0xb, 0xd, 0x9, 0xe };

/**
 * обеспечить регресссмесь колонок
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++){
			array[i][j] = GFMul(deColM[i][0],tempArray[0][j]) ^ GFMul(deColM[i][1],tempArray[1][j])
				^ GFMul(deColM[i][2],tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
		}
}
/**
 * Исключающее ИЛИ два массива 4X4
 */
static void addRoundTowArray(int aArray[4][4],int bArray[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * Получите массив 4X4 из четырех 32-битных ключевых слов:
 * 用于进行обеспечить регресссмесь колонок
 */
static void getArrayFrom4W(int i, int array[4][4]) {
	int index,j;
	int colOne[4], colTwo[4], colThree[4], colFour[4];
	index = i * 4;
	splitIntToArray(w[index], colOne);
	splitIntToArray(w[index + 1], colTwo);
	splitIntToArray(w[index + 2], colThree);
	splitIntToArray(w[index + 3], colFour);

	for(j = 0; j < 4; j++) {
		array[j][0] = colOne[j];
		array[j][1] = colTwo[j];
		array[j][2] = colThree[j];
		array[j][3] = colFour[j];
	}

}

/**
 * параметр c: Строковый массив зашифрованного текста.
 * параметр clen: Длина зашифрованного текста.
 * параметр key: Строковый массив ключей.
 */
void deAes(char *c, int clen, char *key) {

	int cArray[4][4];
	int keylen,k;
	keylen = strlen(key);
	if(clen == 0 || clen % 16 != 0) {
		printf("Длина символов зашифрованного текста должна быть кратна 16! Текущая длина: %d\n", clen);
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("Длина ключевого символа неправильная! Длина должна быть 16, 24и32. Текущая длина: %d\n",keylen);
		exit(0);
	}

	ExtendKey(key);//расширяем ключ

	for(k = 0; k < clen; k += 16) {
		int i;
		int wArray[4][4];

		convertToIntArray(c + k, cArray);

		
		
		

		addRoundKey(cArray, 10);

		for(i = 9; i >= 1; i--) {
			deSubBytes(cArray);

			deShiftRows(cArray);

			deMixColumns(cArray);
			getArrayFrom4W(i, wArray);
			deMixColumns(wArray);

			addRoundTowArray(cArray, wArray);
		}

		deSubBytes(cArray);

		deShiftRows(crray);

		addRoundKey(cArray, 0);

		convertArrayToStr(cArray, c + k);

	}
}

Многие новички могут столкнуться с проблемой отсутствия основной функции при использовании версии VC, реализованной AES. Фактически невозможно напрямую импортировать VC для компиляции. int main(int argc, char const *argv[]) { // Шифрование, где Plain — массив символов открытого текста, len — длина, а key — ключ. aes(обычный, len, ключ); //Расшифровать, где зашифрованный текст — это массив символов зашифрованного текста, len — длина, а key — ключ deAes(зашифрованный текст, длина, ключ); }

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose