Это простая картинка,Там есть какой-то текстовый контент:Конфиденциальная информация СОВЕРШЕННО СЕКРЕТНО
Предположим, что это изображение содержит конфиденциальную информацию, и теперь мне нужно скрыть эту информацию. Что мне делать?
Да, я зашифрую его AES!
Далее мы могли бы попытаться зашифровать его и увидеть эффект.
Прежде чем перейти к обсуждению роли эффекта шифрования IVдля,Без лишних слов, давайте сначала продемонстрируем вывод, который был сделан ранее.:Режим шифрования AES-ECB небезопасен и не рекомендуется для использования в проектах.。
Так насколько это небезопасно? То, что мы видим, — это верить!
Ключ, который мы согласились использоватьkey = b"test_png_encrypt"
,в то же время Мы согласны с тем, что файлы, которые необходимо зашифровать на рисунке выше,file = "top_secret.png"
。
в то же время,Для более интуитивной проверки эффекта шифрования и уменьшения излишней детализации деталей шифрования изображений.,У нас здесь соглашение:Для изображений PNG с обычным текстом зашифруйте их по строкам, используйте несколько строк данных в изображении для имитации больших объемов данных и используйте зашифрованный зашифрованный текст каждой строки для формирования изображения зашифрованного текста для имитации различных режимов шифрования. Характеристики массива данных. сгенерированный зашифрованный текст.
На этом этапе мы шифруем файл в режиме AES-ECB:
test_ecb = png_aes_encryption("ecb")
test_ecb.key_value = key
test_ecb.load(file)
test_ecb.encrypt()
test_ecb.save_cipher_png("cipher_ecb.png")
На этом этапе мы получаем изображение зашифрованного текста со следующим эффектом:
Как описать это чувство?
Он выглядит зашифрованным, но мало что добавляется.
Есть даже чувство одиночества!
AES-ECB не требует использования IV.Полагайтесь исключительно на сам ключ для блочного шифрования.,Под заданным ключом любой блок открытого текста всегда зашифровывается в один и тот же блок зашифрованного текста.。
Это также причина, по которой необходимо ввести IV.
IV усложняет процесс шифрования, позволяя лучше скрыть характеристики открытого текста.
Нет сомнений в том, что после введения IV в режиме CBC можно, по крайней мере, сделать вывод, что он не будет сохранять исходную текстовую информацию в полной мере.
Однако в реальных проектах по-прежнему часто можно увидеть, как пользователи применяют один и тот же ключ и IV к огромным объемам зашифрованных данных, чтобы избежать проблем.
Возьмем это изображение в качестве примера. Если предположить, что все изображение представляет собой огромный объем текстовых данных, то после использования одного и того же ключа и IV для шифрования каждой строки данных мы можем получить следующий эффект:
test_cbc_fixed_iv = png_aes_encryption("cbc")
test_cbc_fixed_iv.key_value = key
test_cbc_fixed_iv.load(file)
test_cbc_fixed_iv.iv_value = fixed_iv
test_cbc_fixed_iv.encrypt(fixed_iv = True)
test_cbc_fixed_iv.save_cipher_png("cipher_cbc_fixed_iv.png")
Кажется, эффект намного лучше, а текстовое содержимое на исходной картинке вообще больше не видно.
Однако со статистической точки зрения, когда мы сравниваем пиксели, мы все равно можем обнаружить, что изображение зашифрованного текста все еще сохраняет значительную статистическую информацию исходного изображения.
Более того, сравнивая границы шума двух изображений невооруженным глазом, мы можем обнаружить, что границы шума изображения зашифрованного текста почти идеально гладкие и почти идеально перекрываются с текстовой частью исходного текста.
Это показывает, что когда большой файл (или файл, разделенный на несколько блоков) шифруется с использованием одного и того же IV в режиме CBC, характеристики конфиденциальной информации в исходном файле могут быть сохранены.
Если быть более точным,для CBC шаблон, повторное использование IV Результат: зашифрованный результат открытого текста с тем же префиксом является зашифрованным текстом с тем же префиксом.
Предположим, что длина двух фрагментов открытого текста составляет 128 байт и 160 байт соответственно, а их первые 33 байта одинаковы. Тогда первые 32 байта их зашифрованного текста одинаковы.
И если мы оставим ключ неизменным, каждая строка данных будет использовать другой IV (каждый раз генерируется случайный IV):
test_cbc = png_aes_encryption("cbc")
test_cbc.key_value = key
test_cbc.load(file)
test_cbc.encrypt()
test_cbc.save_cipher_png("cipher_cbc.png")
Тогда вы сможете получить следующий эффект:
При использовании случайного IV для каждой строки распределение шума всей картинки выглядит гораздо более случайным, то есть с точки зрения статистических характеристик оно сохраняет меньше оригинальных особенностей, которые можно увидеть невооруженным глазом, что свидетельствует о его наличии. безопасность также лучше.
Сначала нам нужно просмотреть:GCM может обеспечить шифрование и проверку целостности сообщений. Это потоковое, а не блочное шифрование.
Метод потокового шифрования на самом деле более чувствителен к повторяющимся IV.
test_gcm_fixed_iv = png_aes_encryption("gcm")
test_gcm_fixed_iv.key_value = key
test_gcm_fixed_iv.load(file)
test_gcm_fixed_iv.iv_value = fixed_iv
test_gcm_fixed_iv.encrypt(fixed_iv = True)
test_gcm_fixed_iv.save_cipher_png("cipher_gcm_fixed_iv.png")
При шифровании строк с повторяющимися IV мы получаем картину следующего эффекта:
Удивлен или нет?
Удивительно ли это?
Если IV фиксирован, зашифрованный текст в режиме GCM выглядит таким же, как и в режиме ECB. Характеристическая информация исходного текста почти полностью сохраняется. Исходную текстовую информацию можно увидеть непосредственно через изображение зашифрованного текста.
Фактически, для OFB/CTR/GCM такой шаблон, как блочный шифр передачи, повторное использование NONCE понизит его до ECB модель.
ECB требуется как минимум один блок (16 байт для AES) одного и того же открытого текста, чтобы получить тот же зашифрованный текст, тогда как OFB/CTR/GCM требует только 1 бит. Если значения в одной и той же позиции в двух частях открытого текста одинаковы, то их значения в соответствующих позициях в зашифрованном тексте одинаковы в одно и то же время, если значения в одной и той же позиции в открытом тексте одинаковы; противоположны, значения на соответствующих позициях в зашифрованном тексте также противоположны. Ведь 1 бит имеет всего два значения: 0 и 1.
А что, если мы воспользуемся случайным IV?
test_gcm = png_aes_encryption("gcm")
test_gcm.key_value = key
test_gcm.load(file)
test_gcm.encrypt()
test_gcm.save_cipher_png("cipher_gcm.png")
Правильно, после использования случайного IV вы можете заметно почувствовать, что конфиденциальность зашифрованного текста стала лучше.
IV, который является вектором инициализации, не обязательно должен храниться в секрете в самом алгоритме шифрования, его можно сделать общедоступным.
Ключ, то есть ключ, в алгоритме шифрования должен храниться в секрете и его нельзя разглашать.
У IV есть одно основное требование как для режима CBC, так и для режима GCM: уникальность.
Когда IV требует только уникальности, мы также можем назвать его NONCE.
Здесь нужно подчеркнуть: уникальность и случайность — разные вещи.
В режиме GCM, поскольку оператор AES внутренне вычисляет ghash и рандомизирует введенный вами IV (NONCE) для режима GCM, даже если IV (NONCE) не является случайным, то есть даже если вы IV (NONCE) вводили при шифровании в режиме GCM каждый раз добавляется к предыдущему IV, что также разрешено. Поэтому в режиме GCM мы обычно не будем использовать термин IV, а напрямую называть его NONCE. Это также может быть отражено в некоторых часто используемых библиотеках:
Библиотека Apple: https://developer.apple.com/documentation/cryptokit/aes/gcm.
Известная библиотека C++: https://doc.libsodium.org/secret-key_cryptography/aead/aes-256-gcm.
Использование библиотеки Go: https://gist.github.com/kkirsche/e28da6754c39d5e7ea10.
Для режима CBC помимо уникальности требуется еще и рандомность IV. Что такое рандом?
Просто скажи это,яПо текущему IV невозможно угадать следующий IV. Если на основе текущего IV я могу угадать следующий IV, прибавив единицу к значению последнего байта предыдущего IV, то он теряет свою случайность.
Вообще говоря,в инженерных приложениях,я们Сохранение IV уникальным и случайным — лучший вариант.
Некоторые фрагменты кода, посвященные шифрованию и перезаписи изображений PNG, предназначены только для справки:
def cut_list_with_step(lst: List[Any], step: int = 1):
return [lst[i:i + step] for i in range(0, len(lst), step)]
class png_aes_encryption(object):
def __init__(self, mode: str = "cbc"):
self.__aes_operator = aes_encryption.aes_encryption(mode)
self.__width: int = 0
self.__height: int = 0
self.__rows: List[bytes] = list()
self.__info: Dict[str:Any] = dict()
self.__cipher_rows: List[bytes] = list()
@property
def key_value(self) -> bytes:
return self.__aes_operator.key_value
@key_value.setter
def key_value(self, key: bytes):
self.__aes_operator.key_value = key
@property
def iv_value(self):
return self.__aes_operator.iv_value
@iv_value.setter
def iv_value(self, iv: bytes):
self.__aes_operator.iv_value = iv
def load(self, filename: str):
self.__width, self.__height, self.__rows, self.__info = png.Reader(filename = filename).read()
print("file={filename}, file_info={info}".format(filename = filename, info = self.__info))
def __generate_row_cipher(self, row_data: bytes) -> Tuple[bytes, int]:
return self.__aes_operator.encrypt(row_data)
def encrypt(self, block_size_in_bytes: int = 0, fixed_iv: bool = False):
"""
block_size_in_bytes По умолчанию 0, Указывает шифрование по строкам матрицы
В противном случае каждая строка будет разделена в соответствии с размером блока_в_байтах, а затем каждый разделенный блок будет зашифрован.
"""
start = time.perf_counter()
self.__cipher_rows = list()
for i in self.__rows:
if not fixed_iv:
self.iv_value = bytes([random.randint(0, 255) for x in range(random.randint(8, 16))])
if block_size_in_bytes <= 0:
row_cipher, row_cipher_len = self.__aes_operator.encrypt(bytes(i))
self.__cipher_rows.append(row_cipher)
else:
if block_size_in_bytes % 4 != 0:
raise ValueError("block_size_in_bytes has to be a multiple of 4 ")
cut_row_cipher = bytes()
cut_row_list = cut_list_with_step(bytes(i), block_size_in_bytes)
for ci in cut_row_list:
tmp_cipher, tmp_cipher_len = self.__aes_operator.encrypt(bytes(ci))
cut_row_cipher += tmp_cipher
self.__cipher_rows.append(cut_row_cipher)
elapsed = round((time.perf_counter() - start), 3) * 1000
print("mode={}, file_size={}, encryption time cost={}ms".format(self.__aes_operator.current_mode,
self.__info["size"],
elapsed))
def save_cipher_png(self, filename: str):
f = open(filename, 'wb')
w = png.Writer(width = int(len(self.__cipher_rows[0]) / 4), height = len(self.__cipher_rows),
greyscale = self.__info['greyscale'],
alpha = self.__info['alpha'], bitdepth = self.__info['bitdepth'])
print("try save png:filename={}, width={}, height={}".format(filename,
int(len(self.__cipher_rows[0]) / 4),
len(self.__cipher_rows)))
w.write(f, self.__cipher_rows)
f.close()