Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.
Advanced Encryption Standard (AES, Advanced Encryption Standard) — наиболее распространенный алгоритм симметричного шифрования (апплет WeChat использует этот алгоритм шифрования для зашифрованной передачи). Симметричный алгоритм шифрования означает, что для шифрования и дешифрования используется один и тот же ключ. Конкретный процесс шифрования выглядит следующим образом:
Ниже приводится краткое описание функций и значения каждой части:
Зашифрованных данных нет.
Пароль, используемый для шифрования открытого текста. В алгоритмах симметричного шифрования ключи шифрования и дешифрования одинаковы. Ключ генерируется путем переговоров между получателем и отправителем, но его нельзя передать непосредственно в сети, иначе это приведет к утечке ключа. Ключ обычно шифруется с помощью алгоритма асимметричного шифрования, а затем передается другой стороне через него. сети, или обсуждается непосредственно лицом к лицу с ключом. Ключ не должен быть утек, иначе злоумышленник восстановит зашифрованный текст и украдет конфиденциальные данные.
Предположим, что функция шифрования AES равна E, тогда C = E(K, P), где P — открытый текст, K — ключ, а C — зашифрованный текст. Другими словами, если открытый текст P и ключ K вводятся в качестве параметров функции шифрования, функция шифрования E выведет зашифрованный текст C.
Данные обрабатываются функцией шифрования
Предположим, что функция AES-расшифровки равна D, тогда P = D(K, C), где C — зашифрованный текст, K — ключ, а P — открытый текст. Другими словами, если зашифрованный текст C и ключ K вводятся в качестве параметров функции дешифрования, функция дешифрования выведет открытый текст P.
Вот краткое введение в разницу между симметричными алгоритмами шифрования и асимметричными алгоритмами шифрования.
Ключи, используемые для шифрования и дешифрования, одинаковы. Этот метод шифрования очень быстрый и подходит для ситуаций, когда данные передаются часто. Недостатком является то, что передача ключа более затруднительна.
Ключи, используемые для шифрования и дешифрования, различаются. Этот метод шифрования основан на математически сложных задачах. Обычно скорость шифрования и дешифрования относительно низкая, что подходит для случаев, когда данные передаются время от времени. Преимущество в том, что передача ключей удобна. Распространенными алгоритмами асимметричного шифрования являются RSA, ECC и EIGamal.
На практике ключ AES обычно шифруется с помощью RSA и передается получателю. Получатель расшифровывает его, чтобы получить ключ 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. Эти четыре этапа операции позволяют полностью перепутать входные биты.
Замена байтов в 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. Схема матрицы состояний после замены байтов выглядит следующим образом:
Обратная замена байтов заключается в поиске обратного 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 |
Сдвиг строк — это простая операция смещения влево. При длине ключа 128 бит строка 0 матрицы состояний смещается влево на 0 байт, строка 1 смещается влево на 1 байт, строка 2 смещается влево на 2 байта, строка 3 смещается влево на 3 байта. , как показано ниже.
Обратное преобразование сдвига строк заключается в выполнении обратной операции сдвига над каждой строкой матрицы состояний. Например, в AES-128 строка 0 матрицы состояний сдвигается вправо на 0 байт, строка 1 сдвигается. вправо на 1 байт, а строка 2 сдвигается вправо на 1 байт. Строки сдвигаются вправо на 2 байта, а строка 3 сдвигается вправо на 3 байта.
Смешанное преобразование столбцов реализуется посредством умножения матриц. Матрица состояний со сдвигом по строкам умножается на фиксированную матрицу для получения матрицы запутанных состояний, как показано в формуле ниже:
Смесь столбцов 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 |
Обратное преобразование смеси столбцов можно определить путем умножения матриц, как показано на следующем рисунке:
Можно проверить, что произведение матрицы обратного преобразования и матрицы прямого преобразования является в точности единичной матрицей.
Добавление раундового ключа заключается в выполнении побитовой операции 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]) следующие:
На картинке в начале статьи,Есть блок-схема AES-расшифровки.,Расшифровка может быть выполнена в соответствии с этой блок-схемой. Ниже представлен еще один эквивалентный режим расшифровки.,Блок-схема показана ниже. Этот эквивалентный режим дешифрования обеспечивает согласованность порядка использования преобразований в процессе дешифрования с порядком в процессе шифрования.,Просто замените исходное преобразование обратным преобразованием.
На этом принцип AES заканчивается. Далее будет описана реализация каждого раздела приведенного выше принципа. Полный код можно найти в конце статьи. В конце статьи представлены две полные программы: одну можно скомпилировать и запустить под Linux, а другую — под VC6.0.
В функции шифрования aes сначала выполняется Ключевое расширение, а затем прочитать строку длиной 128 бит в целочисленный массив 4*4. Этот массив является матрицей состояний. Например, pArray[0][0] = S0,pArray[1][0] = S1, pArray[0][1] = С4. Этот процесс чтения проходит через Это реализуется функцией ConvertToIntArray(). Функция каждого раунда работы модифицирует pArray, то есть запутывает матрицу состояний. После 10 раундов шифрования pArray будет преобразован обратно в строку и затем сохранен в массиве символов обычного текста p. Таким образом, после шифрования символы в строке открытого текста p являются зашифрованными символами. Этот процесс преобразования реализуется с помощью функции ConvertArrayToStr().
/**
* параметр 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);
}
}
перед началом шифрования,Сначала вы должны получить ключ, используемый для первого раунда шифрования.,Так что сначала осознай это Ключевое расширение Ниже Ключевое Реализация функции расширения. Эта функция передает строковое представление ключа ключа, а затем считывает от W[0] до W[3] из строки. Для реализации этой функции используется функция getWordFromStr(). После чтения ключ начинает расширяться. Когда i кратно 4, будет вызвана функция T() для расширения. Поскольку поведение функции T связано с количеством раундов шифрования, количество раундов шифрования. должно быть j Передайте его как параметр.
//Расширенный массив, соответствующий ключу
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.
/**
* список постоянных дежурств
*/
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];
}
Код подстановки байтов очень прост: каждый элемент матрицы состояний передается в функцию getNumFromSBox(), затем получаются старшие 4 бита из первых 8 бит в качестве значения строки, а младшие 4 бита — в качестве значения столбца. , а затем верните S [row][col], где S — массив, в котором хранятся S блоков.
/**
* Получить элементы из 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]);
}
При сдвиге строк сначала скопируйте 2-ю, 3-ю и 4-ю строки в матрице состояний, затем сдвиньте их влево на соответствующее количество цифр, а затем скопируйте их обратно в массив матрицы состояний.
/**
* Элементы массива Воля циклически сдвигаются влево по битам.
*/
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];
}
}
В функции смешивания столбцов сначала скопируйте исходное состояние матрицы состояний в tempArray, а затем умножьте tempArray на матрицу colM. colM — это массив, в котором хранится постоянная матрица, подлежащая умножению. Функция GFMul() определяет умножение при умножении матриц, а сложение реализуется непосредственно через XOR. GFMul() реализует умножение, вызывая функцию, соответствующую умножению каждого числа. Например, S1 * 2 просто реализуется вызовом GFMul2(S1). S1 * 3 был только что реализован через GFMul3(S1). Здесь достаточно в основном реализовать функцию GFMul2(), а все остальное можно реализовать через комбинацию GFMul2(). Например, чтобы вычислить следующее уравнение, вам нужно вызвать функцию следующим образом:
s = GFMul3(0xC9) ^ 0x7A ^ 0x63 ^ GFMul2(0xB0)
/**
* Матрица, которая будет использоваться для смешивания колонок
*/
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]);
}
}
Реализация добавления ключей раунда очень проста: выполняется XOR матрицы состояний с соответствующим W[i] в соответствии с номером входящего раунда.
/**
* круглый ключ плюс
*/
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 немного отличаются. Чтобы понять это, вы можете обратиться к эквивалентной блок-схеме дешифрования. Функция дешифрования вызывает обратную функцию каждого раунда операции. Обратная функция здесь не будет подробно объясняться, вы можете обратиться к окончательному полному коду.
/**
* параметр 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);
}
}
#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
#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);
}
}
#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:
gcc -o aes aes.c main.c
Поскольку компилятор VC6.0 довольно сложен, переменные необходимо сначала объявить, а затем использовать, поэтому код необходимо соответствующим образом изменить.
#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
#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(зашифрованный текст, длина, ключ); }