Получите серию AES за 30 минут (Часть 1): основные функции
Получите серию AES за 30 минут (Часть 1): основные функции

Что такое АЕС

AES — симметричный алгоритм шифрования, его полное название — РАСШИРЕННЫЙ СТАНДАРТ ШИФРОВАНИЯ.

Расширенный стандарт шифрования (AES) назначенный FIPS одобренный Криптографические алгоритмы, которые можно использовать для защиты электронных данных. АЕС 算法是一种Симметричный блочный шифр,Информация может быть зашифрована (зашифрована) и расшифрована (расшифрована). Шифрование преобразует данные в непонятную форму,называется зашифрованным текстом; расшифровка зашифрованного текста преобразует данные обратно в исходную форму;,называется открытым текстом.

Подробное описание стандарта AES можно найти в «Публикации 197 Федеральных стандартов обработки информации», оно здесь не повторяется.

Поддерживаемые режимы AES

AES поддерживает следующие режимы шифрования:

Язык кода:javascript
копировать
1. Режим ЕЦБ ( Electronic Codebook Mode)
2. Режим CBC ( Cipher Block Chaining Mode)
3. Режим CTR(The Counter Mode)
4. Режим GCM(The Galois/Counter Mode)
5. Режим CFB (режим Cipher Feedback Mode)
6. Режим OFB (режим Output Feedback Mode)

Режим шифрования AES просто делится на режим блочного шифрования и режим потокового шифрования в соответствии с различными методами шифрования.

Режим блочного шифрования является наиболее распространенным и наиболее часто используемым в технике является режим CBC.

Наиболее репрезентативным режимом шифрования потока является Режим GCM.

Блокировать шифрование и заполнение данных

Заполнение данных открытого текста — одна из наиболее важных особенностей режима блочного шифрования.

Почему его необходимо заполнить? Очень важная причина этого заключается в том, что библиотека шифрования (или алгоритм шифрования) сама по себе не может предсказать длину открытого текста, введенного пользователем!

Для AES он знает только, что он сгруппирован и зашифрован по 16 байтам. Групповое шифрование здесь более строгое. Должно быть так, что простой текст группируется и шифруется по 16 байтам (каждую единицу мы пока обсуждать не будем). различия между режимами).

Если длина входного открытого текста не является целым числом, кратным 16 байтам, тогда открытый текст необходимо принудительно дополнить и выровнять, чтобы он мог соответствовать правилам группировки.

Некоторые из наиболее распространенных правил заполнения следующие:

  • NoPadding: Как следует из названия,Просто не наполняется. Недостатком является то, что он может шифровать только информацию, кратную BlockSize.,Обычно не используется
  • ZerosPadding: все дополнено 0x00.,Сколько бы не хватало, заполните все 0x00,Он уже кратен BlockSize и его еще нужно заполнить.,Этот метод не используется в общем машиностроении.
  • PKCS#5: заполните недостающие байты.,Значение каждого байта — это количество пропущенных байтов.;Строго говоря, PKCS#5 нельзя использовать в шифровании AES, поскольку размер блока AES составляет 16 байт, а PKCS#5 можно использовать только для 8 байт.
  • PKCS#7:Заполните несколько недостающих байтов, а значение каждого байта — это количество недостающих байтов; когда длины не выровнены, заполните данные до длины группы, когда длины только что выровнены, в конце; исходные данные Добавлен блок заполнения OpenSSL по умолчанию использует PKCS#7 в шифровании AES;
  • ISO 10126: Значение последнего байта — это количество байтов, которые необходимо заполнить (количество байтов, которые необходимо заполнить, включает последний байт), а все остальные заполняются случайными числами.
  • ANSI X9.23: Очень похоже на ISO 10126, за исключением того, что остальные байты в ANSI X9.23 заполнены нулями вместо случайных чисел.

Эффект заполнения PKCS#7:

image.png
image.png
Язык кода:javascript
копировать
    def padding_check(self, origin: str, block_size: int):
        """ 
            Предполагая, что BlockSize равен 128 или 16 байт, тогда:
            Если длина исходного текста меньше 16 байт, он будет заполнен в соответствии с размером блока, равным 16 байтам (128 бит). 
        """
        padder = padding.PKCS7(block_size).padder()
        ret = padder.update(origin.encode('utf-8'))
        ret += padder.finalize()
        print("origin=", list(origin),
              "after padding=", list(ret))
        
        """ Убедитесь, что количество байтов после заполнения соответствует ожидаемому. """
        self.assertEqual(len(ret) % get_bytes_len(block_size), 0)
        
        """ Значение заполнения также является длиной заполнения в байтах. """
        padding_value = get_padding_value(
            get_bytes_len(block_size), len(origin))
        
        """ Длина заполнения составляет padding_value байт, значение каждого байта должно быть padding_value """
        for i in range(1, padding_value + 1):
            self.assertEqual(int(ret[0 - i]), int(padding_value))

В оставшейся части этой статьи мы будем использовать PKCS#7 по умолчанию для избыточного заполнения.

Небезопасное блочное шифрование: ЕЦБ

Режим ECB небезопасен и не рекомендуется к использованию в инженерной практике.

Язык кода:javascript
копировать
    def test_ecb_cipher(self):
        origin_1 = "aaaaaaaaaaaaaaaa"
        origin_2 = "bbbbbbbbbbbbbbbb"
        origin_3 = (origin_1 + origin_2)
        key = "1234567890123456".encode('utf-8')
        aes_obj = aes_encryption.aes_encryption("ecb", key)
        print("Current AES Mode:", aes_obj.current_mode)
        cipher_1, cipher_1_len = aes_obj.encrypt(origin_1.encode('utf-8'))
        cipher_2, cipher_2_len = aes_obj.encrypt(origin_2.encode('utf-8'))
        cipher_3, cipher_3_len = aes_obj.encrypt(origin_3.encode('utf-8'))
        print("cipher_1:{}".format(list(cipher_1)))
        print("cipher_2:{}".format(list(cipher_2)))
        print("cipher_3:{}".format(list(cipher_3)))
        """ В режиме ECB между зашифрованным и открытым текстом существует взаимно однозначное соответствие, что небезопасно. """
        self.assertEqual(cipher_1_len % 16, 0)
        self.assertEqual(cipher_2_len % 16, 0)
        self.assertEqual(cipher_3_len % 16, 0)
        self.assertEqual(
            cipher_1[:cipher_1_len - 16] + cipher_2[:cipher_2_len - 16], cipher_3[:cipher_3_len - 16])
        self.assertEqual(len(aes_obj.key_value) % 16, 0)
        self.assertLessEqual(len(aes_obj.key_value), 32)

когда мы используемECBоткрытый текст:aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbа такжеaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb做шифрование时,Если мы немного понаблюдаем, то обнаружим,Зашифрованный текст фактически появляется неоднократно.

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

Внимательные друзья, возможно, обнаружили, что повторяющиеся данные состоят из трех частей, так почему же они состоят из трех частей?

Этот вопрос оставлен для размышления для всех, и вы можете общаться в области комментариев.

Классический режим блочного шифрования: CBC

Для режима блочного шифрования,Всегда есть:Длина зашифрованного текста равна длине дополненного открытого текста! ! !

Язык кода:javascript
копировать
    def test_aes_cbc_encryption(self):
        origin = os.urandom(random.randint(17, 256))
        key = os.urandom(32)
        iv = os.urandom(16)
        aes_obj = aes_encryption.aes_encryption("cbc", key, iv)
        print("Current AES Mode:", aes_obj.current_mode)
        cipher, cipher_len = aes_obj.encrypt(origin)
        plain, plain_len = aes_obj.decrypt(cipher)
        self.assertEqual(plain, origin)
        self.assertEqual(len(origin), plain_len)
        self.assertGreaterEqual(cipher_len, len(origin))
        print("cipher_len:", cipher_len)
        print("origin_len:", len(origin))
        print("len(origin) % 16 = ", len(origin) % 16)
        """
            Если исходная длина данных равна BlockSize * n,
            затем используйте NoPadding Когда длина зашифрованных данных равна BlockSize * n, в остальных случаях длина зашифрованных данных равна BlockSize * (n+1)。
            Если исходная длина данных равна BlockSize*n+m [в m меньше, чем BlockSize],
            Кроме NoPadding Любыми способами, кроме заполнения, длина зашифрованных данных равна BlockSize*(n+1);
        """
        if len(origin) % 16 == 0:
            self.assertEqual(cipher_len, len(origin))
        else:
            self.assertEqual(cipher_len, (len(origin) // 16 + 1) * 16)

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

В режиме шифрования CBC первый входной блок формируется путем операции XOR первого блока открытого текста с IV.

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

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

Функция прямого шифрования применяется к каждому входному блоку для создания блока зашифрованного текста.

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

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

Классическая логическая абстрактная диаграмма AES-CBC:

Обычно для восстановления любого блока открытого текста (кроме первого) обратная криптографическая функция применяется к соответствующему блоку зашифрованного текста, и полученный блок объединяется с помощью операции XOR с предыдущим блоком зашифрованного текста.

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

Цепная реакция режима CBC относится к процессу шифрования, поскольку при изменении IV все блоки зашифрованного текста в процессе шифрования изменятся;

При расшифровке IV повлияет только на первый блок открытого текста.

Язык кода:javascript
копировать
    def test_aes_cbc_decrypt_by_wrong_iv(self):
        """
            Цепная реакция в режиме CBC обычно относится к процессу шифрования, поскольку при изменении IV все блоки зашифрованного текста в процессе шифрования изменяются.
            При расшифровке IV повлияет только на первый блок открытого текста.
        """
        # 64-байтовый простой текст
        origin_plain = os.urandom(64)
        print("origin_plain[0:16]:{}".format(list(origin_plain[0:16])))
        print("origin_plain[16:32]:{}".format(list(origin_plain[16:32])))
        print("origin_plain[32:48]:{}".format(list(origin_plain[32:48])))
        print("origin_plain[48:64]:{}".format(list(origin_plain[48:64])))
        # Ключ длиной 32 байта
        key = os.urandom(32)
        # print("key:{}".format(list(key)))
        # Длина 16 байт iv
        iv = os.urandom(16)
        print("correct iv:{}".format(list(iv)))
        aes_obj = aes_encryption.aes_encryption("cbc", key, iv)
        cipher, cipher_len = aes_obj.encrypt(origin_plain)
        # Используйте правильный ключ и iv для расшифровки
        plain, plain_len = aes_obj.decrypt(cipher)
        self.assertEqual(plain, origin_plain)
        self.assertEqual(len(origin_plain), plain_len)
        self.assertGreaterEqual(cipher_len, len(origin_plain))
        # Установите неправильное значение iv
        aes_obj.iv_value = os.urandom(16)
        self.assertNotEqual(iv, aes_obj.iv_value)
        print("wrong iv:{}".format(list(aes_obj.iv_value)))
        wrong_plain, wrong_plain_len = aes_obj.decrypt(cipher)
        print("wrong_plain[0:16]:{}".format(list(wrong_plain[0:16])))
        print("wrong_plain[16:32]:{}".format(list(wrong_plain[16:32])))
        print("wrong_plain[32:48]:{}".format(list(wrong_plain[32:48])))
        print("wrong_plain[48:64]:{}".format(list(wrong_plain[48:64])))
        # Даже при использовании неправильного внутривенного введения для расшифровки,Но полученная длина по-прежнему верна.,只是Расшифровать出来的内容会有不同
        self.assertNotEqual(wrong_plain, origin_plain)
        self.assertNotEqual(wrong_plain, plain)
        self.assertEqual(len(origin_plain), wrong_plain_len)
        # При расшифровке неправильный IV влияет только на первый 16-байтовый блок блока открытого текста.
        self.assertNotEqual(wrong_plain[0:16], origin_plain[0:16])
        self.assertEqual(wrong_plain[16:], origin_plain[16:])

Режим потокового шифрования отличается от режима блочного шифрования: CTR и GCM.

Режим CTR

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

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

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

Режим CTR имеет множество преимуществ: прост в понимании,Высокая эффективность,Никакой прокладки не требуется,Поддерживает распараллеливание,произвольный доступ,А также просто функции шифрования и так далее.

Однако у CTR есть и очевидные недостатки:

Язык кода:javascript
копировать
Целостность сообщения не может быть гарантирована:
    Не имея аутентификации сообщения, злоумышленник может легко перевернуть перехваченное зашифрованное сообщение и воспроизвести его без расшифровки.
    И поскольку Режим Из-за податливости CTR изменение одного бита приведет к разрушительным результатам.
Повторное использование блока счетчика приводит к утечке открытого текста:
    Если блок счетчика (nonce) используется повторно, это может привести к утечке открытого текста;
    Особенно Шифрование CTR требует ввода уникального случайного числа, которое нельзя использовать повторно для двух разных сообщений, зашифрованных одним и тем же ключом, поэтому метод генерации случайных чисел особенно важен.
Длина зашифрованного текста известна (длину исходного текста можно скрыть с помощью заполнения):
    Потому что Режим CTR не требует заполнения, поэтому длина зашифрованного зашифрованного текста может быть известна.
    Хотя длина сообщения не считается секретом во многих методах шифрования, благодаря характеристикам симметричного шифрования можно получить соответствующую длину открытого текста:
    Это приводит к риску утечки открытого текста на высоком уровне.

Режим GCM

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

Следовательно, мы можем добавить эту часть в качестве дополнительного сообщения к расчету значения MAC.

Режим GCM — это очень классический AEAD (аутентифицированный Encryption with Associated Data)。

AEAD — это форма шифрования, сочетающая конфиденциальность, целостность и аутентификацию.

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

Распространенными алгоритмами AEAD являются:

Язык кода:javascript
копировать
AES-128-GCM
AES-192-GCM
AES-256-GCM
ChaCha20-IETF-Poly1305
ChaCha20-IETF-Poly1305

На процессорах (настольных компьютерах, серверах) с ускорением AES рекомендуется использовать серию AES-XXX-GCM, а для мобильных устройств — серию ChaCha20-IETF-Poly1305.

Сравнение CBC и GCM

AES-GCM может шифровать и дешифровать параллельно, тогда как режим AES-CBC определяет, что он может шифровать только последовательно.

Поскольку шифрование — это трудоемкий этап, а метод шифрования один и тот же, при параллельной реализации алгоритма AES-GCM его эффективность выше, чем у AES-CBC.

AES-GCM предоставляет код проверки информации GMAC для проверки целостности зашифрованного текста. AES-CBC его не имеет и не может эффективно проверять целостность зашифрованного текста;

AES-GCM — это режим потокового шифрования, не требующий заполнения открытого текста. AES-CBC — это режим блочного шифрования, требующий заполнения открытого текста (счетчик используется для шифрования AES в AES-GCM, а блоки открытого текста используются для шифрования AES в AES-CBC).

Поскольку в AES-CBC необходимо использовать заполнение, последний блок открытого текста отличается от других блоков зашифрованного текста, поэтому он может подвергаться атакам Oracle с заполнением, поэтому открытый текст можно получить непосредственно через исходный вектор IV и пароль.

Эта часть будет подробно описана в других статьях этой серии, так что следите за обновлениями!

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