Что такое шифрование? - Руководство по шифрам, дайджестам, соли и IV

Что такое шифрование? - Руководство по шифрам, дайджестам, соли и IV

23 января 2023 г.

n Авторское право на изображение (c) Серджио Миятович, 2022 г.

Что такое шифрование?

n Шифрование – это метод преобразования данных в непригодную для использования форму, которую можно сделать полезной только с помощью расшифровки. Цель состоит в том, чтобы сделать данные доступными только для тех, кто может их расшифровать (то есть сделать их пригодными для использования). Как правило, данные необходимо шифровать, чтобы гарантировать невозможность их получения в случае несанкционированного доступа. Это последняя линия обороны после того, как злоумышленнику удалось взломать системы авторизации и контроля доступа. n n Это не означает, что все данные должны быть зашифрованы, поскольку часто систем авторизации и доступа может быть достаточно, и, кроме того, шифрование и дешифрование данных снижает производительность. Если и когда данные будут зашифрованы, это вопрос планирования приложений и оценки рисков, а иногда это также нормативные требования, такие как HIPAA или GDPR. n n Данные могут быть зашифрованы в состоянии покоя, например, на диске, или при передаче, например, между двумя сторонами, общающимися через Интернет. n n Здесь вы узнаете, как шифровать и расшифровывать данные с помощью пароля, также известного как симметричное шифрование. Этот пароль должен быть известен обеим сторонам, обменивающимся информацией.

Шифр, дайджест, соль, итерации, IV

n Чтобы правильно и безопасно использовать шифрование, необходимо объяснить несколько понятий. n n Шифр – это алгоритм, используемый для шифрования. Например, AES256 — это шифр. Идея шифра — это то, о чем думает большинство людей, когда речь заходит о шифровании. n n Дайджест — это, по сути, хеш-функция, которая используется для шифрования и удлинения пароля (то есть ключа шифрования) до того, как он будет использован шифром. Почему это делается? Во-первых, он создает хорошо рандомизированный хеш одинаковой длины ключа, который лучше подходит для шифрования. Он также очень подходит для «засолки», о которой мы поговорим позже. n n "Соль" — это метод борьбы с так называемыми "радужными" таблицами. Злоумышленник знает, что два хешированных значения будут выглядеть точно так же, как оригиналы. Однако, если вы добавите значение соли к хешированию, они этого не сделают. Он называется «солью», потому что его смешивают с тональностью, чтобы получить что-то другое. Теперь радужная таблица попытается сопоставить известные хешированные значения с предварительно вычисленными данными, чтобы угадать пароль. Обычно соль генерируется случайным образом для каждого ключа и хранится вместе с ним. Чтобы сопоставить известные хэши, злоумышленник должен предварительно вычислить радужные таблицы для большого количества случайных значений, что, как правило, невозможно. n n Вы часто будете слышать о «итерациях» в шифровании. Итерация — это одиночный цикл, в котором ключ и соль смешиваются таким образом, чтобы угадать ключ было сложнее. Это делается много раз, чтобы злоумышленнику было сложно угадать ключ с вычислительной точки зрения, отсюда и «итерации» (во множественном числе). Как правило, минимальное необходимое количество итераций равно 1000, но оно может отличаться от указанного. Если вы начинаете с действительно надежного пароля, обычно вам нужно меньше. n n IV (или "вектор инициализации") обычно представляет собой случайное значение, которое используется для шифрования каждого сообщения. Теперь соль используется для создания ключа на основе пароля. А IV используется, когда у вас уже есть ключ и вы сейчас шифруете сообщения. Цель IV — сделать так, чтобы одни и те же сообщения выглядели по-разному в зашифрованном виде. Иногда IV также имеет последовательный компонент, поэтому он состоит из случайной строки и последовательности, которая постоянно увеличивается. Это затрудняет атаки «повторения», когда злоумышленнику не нужно расшифровывать сообщение; скорее, зашифрованное сообщение было «обнюхано» (т. Е. Перехвачено между отправителем и получателем), а затем воспроизведено в надежде повторить уже выполненное действие. Хотя на самом деле в большинстве высокоуровневых протоколов уже есть последовательность, в которой каждое сообщение имеет возрастающий номер пакета, поэтому в большинстве случаев IV не нуждается в этом.

Предпосылки

n В этом примере используется-Velyфреймворк. n n Обратите внимание, что использование пользовательских шифров и дайджестов, а также явное использование векторов инициализации и кэширования ключей доступно с версии 15.2. Если вы используете более раннюю версию, вам следует: установите 15.2 или более позднюю версию, чтобы запустить эти примеры.

Пример шифрования

n Чтобы запустить приведенные здесь примеры, создайте приложение "enc" в отдельном каталоге (дополнительную информацию см. в vf). руководитель программы Vely): n n

mkdir enc_example
cd enc_example
sudo vf -i -u $(whoami) enc

n Для шифрования данных используйте оператор encrypt-data. Простейшей формой является шифрование строки с завершающим нулем. Создайте файл "encrypt.vely" и скопируйте его: n n

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password"
    p-out enc_str
    @
    // Decrypt
    decrypt-data enc_str password "my_password" to define dec_str
    p-out dec_str
    @
}

n Вы можете увидеть основное использование шифрования-данных и-расшифровки-данных. Вы предоставляете данные (оригинальные или зашифрованные), пароль и вперед. Данные шифруются, а затем расшифровываются, получая оригинал. n n В исходном коде строковая переменная "enc_str" (которая создается как "char *") будет содержать зашифрованную версию "Это содержит секретный код, который называется Сезам, откройся!" и "dec_str" будут расшифрованными данными, которые должны быть точно такими же. n n Чтобы запустить этот код из командной строки, сначала создайте приложение: n

vv -q

n Затем пусть Vely создаст код bash для его запуска — путь запроса «/encrypt», который в нашем случае обрабатывается функцией «void encrypt()», определенной в исходном файле «encrypt.vely». В Vely эти имена всегда совпадают, что упрощает написание, чтение и выполнение кода. Используйте параметр "-r" в vv, чтобы указать путь запроса и получить код, необходимый для запуска программы: n n

vv -r --req="/encrypt"

n Это даст что-то вроде: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

n Скопируйте и вставьте это в оболочку bash и выполните. Вы получите такой ответ: n

Content-type: text/html;charset=utf-8
Cache-Control: max-age=0, no-cache
Pragma: no-cache
Status: 200 OK

72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9
This contains a secret code, which is Open Sesame!

n Здесь у вас есть зашифрованные данные, а затем эти зашифрованные данные расшифровываются с использованием того же пароля. Неудивительно, что результат соответствует строке, которую вы зашифровали в первую очередь. n n Обратите внимание, что по умолчанию encrypt-data создает зашифрованное значение в удобочитаемой шестнадцатеричной форме, что означает, что оно состоит из шестнадцатеричных символов от «0» до «9» и от «a» до «f». Таким образом, вы можете хранить зашифрованные данные в обычной строке. Например, он может перейти в документ JSON или в столбец VARCHAR в базе данных или куда угодно еще. Однако вы также можете создавать двоичные зашифрованные данные. Подробнее об этом чуть позже. n n Приведенный выше результат является корректным HTTP-ответом с правильным заголовком. Чтобы пропустить заголовок, установите для переменной VV_SILENT_HEADER значение «да»: n

export VV_SILENT_HEADER=yes
/var/lib/vv/bld/enc/enc

n Если вы снова запустите программу "/var/lib/vv/bld/enc/enc", результат будет таким же, без каких-либо заголовков: n

72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9
This contains a secret code, which is Open Sesame!

Зашифровать данные в двоичный результат

n В предыдущем примере результирующие зашифрованные данные представлены в удобочитаемой шестнадцатеричной форме. Вы также можете создавать двоичные зашифрованные данные, которые не являются удобочитаемой строкой и к тому же короче. Для этого используйте «бинарное» предложение. Замените код в "encrypt.vely" на: n

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password" 
        binary output-length define outlen
    // Save the encrypted data to a file
    write-file "encrypted_data" from enc_str length outlen
    get-app directory to define app_dir
    @Encrypted data written to file <<p-out app_dir>>/encrypted_data
    // Decrypt data
    decrypt-data enc_str password "my_password" 
        input-length outlen binary to define dec_str
    p-out dec_str
    @
}

n Если вы хотите получить двоично зашифрованные данные, вы также должны получить их длину в байтах, иначе вы не будете знать, где они заканчиваются, так как они могут содержать нулевые символы. Используйте для этой цели предложение «output-length». В этом коде зашифрованные данные в переменной «enc_str» записываются в файл «encrypted_data», а записанная длина равна «outlen» байтам. Когда файл записывается без пути, он всегда записывается в домашний каталог приложения (см.-how_vely_works), поэтому вы должны использовать get-app, чтобы получить этот каталог. n n При расшифровке данных обратите внимание на использование условия input-length. Он говорит, сколько байтов имеют зашифрованные данные. Очевидно, вы можете получить это из переменной «outlen», где encrypt-data хранит длину зашифрованных данных. Когда шифрование и дешифрование разделены, т. е. выполняются в отдельных программах, вы должны убедиться, что эта длина доступна. n n Обратите также внимание на то, что когда данные зашифрованы как "двоичные" (что означает создание двоичного вывода), расшифровка должна использовать то же самое. n n Подайте заявку: n

vv -q

n Запустите его так же, как и раньше: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

n Результат: n

Encrypted data written to file /var/lib/vv/enc/app/encrypted_data
This contains a secret code, which is Open Sesame!

n Расшифрованные данные точно такие же, как и исходные. n n Вы можете увидеть фактические зашифрованные данные, записанные в файл, с помощью утилиты Linux «восьмеричный дамп» («od»): n

$ od -c /var/lib/vv/enc/app/encrypted_data
0000000   r 335 324   L 020 351   i   ; 346 254   w 312 253 306   N 005
0000020 370  t   )  n 020 235 367 317 305   t  0 224 214 270 210 315
0000040   # 307 351 216 025 274 362 033   % 253 023   7 335 306 320
0000060 224   #   ! 021 252     242 325   H 300 217   #  v   m   V 351
0000100

n Вот оно. Вы заметите, что данные являются двоичными и на самом деле содержат нулевые символы.

Шифровать двоичные данные

n Данные для шифрования в этих примерах представляют собой строку, т. е. разделенную нулем. Вы можете так же легко зашифровать двоичные данные, указав их длину в предложении «input-length», например, скопировав это в «encrypt.vely»: n

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This c000ontains a secret code, which is Open Sesame!";
    // Encrypt
    encrypt-data str to define enc_str password "my_password" 
        input-length 12
    p-out enc_str
    @
    // Decrypt
    decrypt-data enc_str password "my_password" to define dec_str 
        output-length define res_len
    // Output binary data; present null char as octal 000
    int i;
    for (i = 0; i < res_len; i++) {
        if (dec_str[i] == 0) {
            p-out "000"
        } else {
            pf-out "%c", dec_str[i]
        }
    }
    @
}

n Это зашифрует 12 байтов в ячейке памяти "enc_str" независимо от нулевых символов. В данном случае это «This c», за которым следует нулевой символ, за которым следует строка «ontain», но это могут быть любые двоичные данные, например содержимое файла JPG. n n На стороне расшифровки вы получите количество расшифрованных байтов в предложении "output-length". Наконец, показано, что расшифрованные данные точно соответствуют оригиналу, а нулевой символ представлен в типичном восьмеричном представлении. n n Подайте заявку: n

vv -q

n Запустите его так же, как и раньше: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

n Результат: n

6bea45c2f901c0913c87fccb9b347d0a
This c000ontai

n Зашифрованное значение короче, потому что и в этом случае данные короче, а результат точно соответствует оригиналу.

Использовать любой шифр или дайджест

n По умолчанию используется шифрование AES256 и SHA256 из стандартной библиотеки OpenSSL, которые широко используются в криптографии. Однако вы можете использовать любой доступный шифр и дайджест (т. е. хэш), которые поддерживаются OpenSSL (даже предоставленные вами пользовательские). n n Чтобы увидеть, какие алгоритмы доступны, сделайте это в командной строке: n

#get list of cipher providers
openssl list -cipher-algorithms

#get list of digest providers
openssl list -digest-algorithms

n Эти два будут предоставлять список алгоритмов шифрования и дайджеста (хеширования). Некоторые из них могут быть слабее, чем стандартные, выбранные Vely, а другие могут быть только для обратной совместимости со старыми системами. Третьи могут быть совершенно новыми, и у них не было достаточно времени для проверки в той степени, в которой вы хотите, чтобы они были. Поэтому будьте осторожны при выборе этих алгоритмов и обязательно знайте, почему вы меняете алгоритмы по умолчанию. Тем не менее, вот пример использования шифрования Camellia-256 (то есть «CAMELLIA-256-CFB1») с дайджестом «SHA3-512». Замените код в "encrypt.vely" на: n

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Encrypt data
    encrypt-data str to define enc_str password "my_password" 
        cipher "CAMELLIA-256-CFB1" digest "SHA3-512"
    p-out enc_str
    @
    // Decrypt data
    decrypt-data enc_str password "my_password"  to define dec_str 
        cipher "CAMELLIA-256-CFB1" digest "SHA3-512"
    p-out dec_str
    @
}

n Подайте заявку: n

vv -q

n Запустить: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc

n В этом случае результат: n

f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6
This contains a secret code, which is Open Sesame!

n Опять же, вы получаете исходные данные. Обратите внимание, что вы должны использовать один и тот же шифр и дайджест как для шифрования данных, так и для расшифровки данных! n n Конечно, вы можете создать двоичное зашифрованное значение, как и раньше, используя предложения «binary» и «output-length». n n Если у вас есть внешние системы, которые шифруют данные, и вы знаете, какие шифры и дайджесты они используют, вы можете сопоставить их и сделать свой код совместимым. Vely использует стандартную библиотеку OpenSSL, так что есть вероятность, что и другое программное обеспечение тоже.

Использование соли

n Чтобы добавить соль к шифрованию, используйте пункт "соль". Вы можете сгенерировать случайную соль с помощью инструкции random-string (или-random-crypto, если есть необходимость). Вот код для "encrypt.vely": n

#include "vely.h"

void encrypt() {
    out-header default
    char *str = "This contains a secret code, which is Open Sesame!";
    // Get salt
    random-string to define rs length 16
    // Encrypt data
    encrypt-data str to define enc_str password "my_password" salt rs
    @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>>
    // Decrypt data
    decrypt-data enc_str password "my_password" salt rs to define dec_str
    p-out dec_str
    @
}

n Подайте заявку: n

vv -q

n Запустить несколько раз: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc

n Результат: n

Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd
This contains a secret code, which is Open Sesame!
Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371
This contains a secret code, which is Open Sesame!
Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917
This contains a secret code, which is Open Sesame!

n Как видите, для каждого шифрования генерируется случайное солт-значение (в данном случае длиной 16 байт), и зашифрованное значение каждый раз разное, хотя шифруемые данные были одинаковыми! Это затрудняет взлом такого шифрования. n n Конечно, для расшифровки вы должны записать соль и использовать ее точно так же, как при шифровании. В этом коде переменная «rs» содержит соль. Если вы храните зашифрованные значения в базе данных, вы, скорее всего, храните соль рядом с ней.

Вектор инициализации

n На практике вы не будете использовать разные значения соли для каждого сообщения. Он каждый раз создает новый ключ, что может снизить производительность. И на самом деле в этом нет необходимости: использование соли состоит в том, чтобы сделать каждый ключ (даже одинаковый) намного сложнее угадать. После того, как вы это сделаете, вам может не понадобиться делать это снова или часто. n n Вместо этого вы должны использовать IV (вектор инициализации) для каждого сообщения. Обычно это случайная строка, из-за которой одни и те же сообщения выглядят по-разному, что увеличивает вычислительные затраты на взлом пароля. Вот новый код для "encrypt.vely": n

#include "vely.h"

void encrypt() {
    out-header default
    // Get salt
    random-string to define rs length 16
    // Encrypt data
    num i;
    for (i = 0; i < 10; i++) {
        random-string to define iv length 12
        encrypt-data "The same message" to define enc_str password "my_password" salt rs iterations 2000 init-vector iv cache
        @The encrypted string is <<p-out enc_str>>
        // Decrypt data
        decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to define dec_str cache
        p-out dec_str
        @
    }
}

n Подайте заявку: n

vv -q

n Запустить несколько раз: n

export REQUEST_METHOD=GET
export SCRIPT_NAME="/enc"
export PATH_INFO="/encrypt"
export QUERY_STRING=""
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc
/var/lib/vv/bld/enc/enc

n Результат может быть: n

The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2
The same message
The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be
The same message
The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7
The same message
The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056
The same message
The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd
The same message
The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58
The same message
The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40
The same message
The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5
The same message
The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458
The same message
The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00
The same message

n Вы можете видеть, что одно и то же сообщение в зашифрованном виде выглядит по-разному, хотя при расшифровке оно снова остается прежним. Разумеется, пароль, соль, количество итераций и инициализирующий вектор должны быть одинаковыми как для шифрования, так и для дешифрования. n n Обратите внимание на использование предложения "cache" в encrypt-data и decrypt-data. Он эффективно кэширует вычисленный ключ (заданный пароль, соль, алгоритмы шифрования/дайджеста и количество итераций), поэтому он не вычисляется каждый раз в цикле. С «кэшем» ключ вычисляется один раз, а затем для каждого сообщения используется другой IV (в предложении «init-vector»). n n Если вы хотите время от времени перестраивать ключ, используйте пункт «clear-cache», который предоставляет логическое значение. Если true, ключ вычисляется заново, в противном случае он остается в покое. Подробнее об этом см. в разделе encrypt-data.

Заключение

n Вы научились шифровать и расшифровывать данные, используя различные шифры, дайджесты, соли и значения IV в Vely. Вы также можете создать удобочитаемое зашифрованное значение и двоичный вывод, а также зашифровать как строки, так и двоичные значения (например, документы).

Что вы думаете о моем руководстве? Дайте мне знать в комментариях ниже.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE