AES — симметричный алгоритм шифрования, его полное название — РАСШИРЕННЫЙ СТАНДАРТ ШИФРОВАНИЯ.
Расширенный стандарт шифрования (AES) назначенный FIPS одобренный Криптографические алгоритмы, которые можно использовать для защиты электронных данных. АЕС 算法是一种Симметричный блочный шифр,Информация может быть зашифрована (зашифрована) и расшифрована (расшифрована). Шифрование преобразует данные в непонятную форму,называется зашифрованным текстом; расшифровка зашифрованного текста преобразует данные обратно в исходную форму;,называется открытым текстом.
Подробное описание стандарта AES можно найти в «Публикации 197 Федеральных стандартов обработки информации», оно здесь не повторяется.
AES поддерживает следующие режимы шифрования:
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 байтам, тогда открытый текст необходимо принудительно дополнить и выровнять, чтобы он мог соответствовать правилам группировки.
Некоторые из наиболее распространенных правил заполнения следующие:
Эффект заполнения PKCS#7:
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 небезопасен и не рекомендуется к использованию в инженерной практике.
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открытый текст:aaaaaaaaaaaaaaaa
、bbbbbbbbbbbbbbbb
а такжеaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb
做шифрование时,Если мы немного понаблюдаем, то обнаружим,Зашифрованный текст фактически появляется неоднократно.
Режим ECB имеет серьезную проблему безопасности: если используется один и тот же ключ, один и тот же блок открытого текста будет генерировать один и тот же блок зашифрованного текста, который не может хорошо скрыть шаблон данных.
Внимательные друзья, возможно, обнаружили, что повторяющиеся данные состоят из трех частей, так почему же они состоят из трех частей?
Этот вопрос оставлен для размышления для всех, и вы можете общаться в области комментариев.
Для режима блочного шифрования,Всегда есть:Длина зашифрованного текста равна длине дополненного открытого текста! ! !
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 повлияет только на первый блок открытого текста.
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-шифровании функция прямого шифрования вызывается для каждого блока счетчика, а полученный выходной блок подвергается операции XOR с соответствующим блоком открытого текста для создания блока зашифрованного текста. Для последнего блока, который может быть частичным блоком из u бит, самые старшие u бит последнего выходного блока используются в операции XOR, остальные b-u биты последнего выходного блока отбрасываются.
При расшифровке CTR функция прямого шифрования вызывается для каждого блока счетчика, а полученный выходной блок подвергается операции XOR с соответствующим блоком зашифрованного текста для восстановления блока открытого текста. Для последнего блока, который может быть частичным блоком из u бит, самые старшие u бит последнего выходного блока используются в операции XOR, остальные b-u биты последнего выходного блока отбрасываются.
При шифровании CTR и дешифровании CTR функция прямого шифрования может выполняться аналогичным образом: блок открытого текста, соответствующий любому конкретному блоку зашифрованного текста, может быть восстановлен независимо от других блоков открытого текста, если можно определить соответствующий блок счетчика; Кроме того, к счетчику можно применить функцию прямого шифрования до того, как станут доступны данные открытого или зашифрованного текста.
Режим CTR имеет множество преимуществ: прост в понимании,Высокая эффективность,Никакой прокладки не требуется,Поддерживает распараллеливание,произвольный доступ,А также просто функции шифрования и так далее.
Однако у CTR есть и очевидные недостатки:
Целостность сообщения не может быть гарантирована:
Не имея аутентификации сообщения, злоумышленник может легко перевернуть перехваченное зашифрованное сообщение и воспроизвести его без расшифровки.
И поскольку Режим Из-за податливости CTR изменение одного бита приведет к разрушительным результатам.
Повторное использование блока счетчика приводит к утечке открытого текста:
Если блок счетчика (nonce) используется повторно, это может привести к утечке открытого текста;
Особенно Шифрование CTR требует ввода уникального случайного числа, которое нельзя использовать повторно для двух разных сообщений, зашифрованных одним и тем же ключом, поэтому метод генерации случайных чисел особенно важен.
Длина зашифрованного текста известна (длину исходного текста можно скрыть с помощью заполнения):
Потому что Режим CTR не требует заполнения, поэтому длина зашифрованного зашифрованного текста может быть известна.
Хотя длина сообщения не считается секретом во многих методах шифрования, благодаря характеристикам симметричного шифрования можно получить соответствующую длину открытого текста:
Это приводит к риску утечки открытого текста на высоком уровне.
GCM может обеспечить шифрование и проверку целостности сообщений. Кроме того, он также может обеспечить проверку целостности дополнительных сообщений. В реальных сценариях приложений существует некоторая информация, которую нам не нужно сохранять конфиденциальной, но получатель информации должен подтвердить ее подлинность, например IP-адрес источника, порт источника, IP-адрес назначения, IV и т. д.
Следовательно, мы можем добавить эту часть в качестве дополнительного сообщения к расчету значения MAC.
Режим GCM — это очень классический AEAD (аутентифицированный Encryption with Associated Data)。
AEAD — это форма шифрования, сочетающая конфиденциальность, целостность и аутентификацию.
Причина AEAD очень проста. Шаг расшифровки простого алгоритма симметричного шифрования не может подтвердить правильность ключа. Другими словами, зашифрованные данные можно расшифровать с помощью любого ключа, чтобы получить набор подозрительных исходных данных. Мы не знаем, верен ли ключ и верны ли расшифрованные исходные данные. Поэтому нам необходимо добавить уровень метода проверки поверх простого алгоритма шифрования, чтобы подтвердить правильность шагов расшифровки.
Распространенными алгоритмами AEAD являются:
AES-128-GCM
AES-192-GCM
AES-256-GCM
ChaCha20-IETF-Poly1305
ChaCha20-IETF-Poly1305
На процессорах (настольных компьютерах, серверах) с ускорением AES рекомендуется использовать серию AES-XXX-GCM, а для мобильных устройств — серию ChaCha20-IETF-Poly1305.
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 и пароль.
Эта часть будет подробно описана в других статьях этой серии, так что следите за обновлениями!