класс OpenSSL::Cipher
Предоставляет симметричные алгоритмы для шифрования и дешифрования. Доступные алгоритмы зависят от конкретной версии OpenSSL, установленной на вашей системе.
Перечисление всех поддерживаемых алгоритмов
Список поддерживаемых алгоритмов можно получить, выполнив
puts OpenSSL::Cipher.ciphers
Создание экземпляра Cipher
Существует несколько способов создания экземпляра Cipher. Как правило, алгоритм Cipher определяется его именем, длиной ключа в битах и используемым режимом шифрования. Наиболее универсальный способ создания экземпляра Cipher — следующий
cipher = OpenSSL::Cipher.new('<name>-<key length>-<mode>')
То есть, строка, содержащая дефисно-связанное имя компонента, длину ключа и режим. Можно использовать как строчные, так и заглавные буквы, например:
cipher = OpenSSL::Cipher.new('aes-128-cbc')
Выбор режима шифрования или дешифрования
Шифрование и дешифрование часто являются очень похожими операциями для симметричных алгоритмов, это отражается в отсутствии необходимости выбирать разные классы для каждой операции; обе операции могут выполняться с использованием одного и того же класса. Однако после получения экземпляра Cipher, мы должны указать экземпляру, что мы хотим сделать, поэтому нам нужно вызвать либо
cipher.encrypt
либо
cipher.decrypt
на экземпляре Cipher. Это должен быть первый вызов после создания экземпляра, иначе ранее установленные настройки могут быть потеряны в процессе.
Выбор ключа
Для симметричного шифрования требуется ключ, одинаковый для шифрования и дешифрования, и после первоначального его установления он должен храниться как конфиденциальная информация. Есть много способов создать небезопасный ключ, наиболее известный из которых — использовать пароль в качестве ключа без дальнейшей обработки пароля. Простой и безопасный способ создания ключа для конкретного Cipher — это
cipher = OpenSSL::Cipher.new('aes-256-cfb')
cipher.encrypt
key = cipher.random_key # also sets the generated key on the Cipher
Если вам абсолютно необходимо использовать пароли в качестве ключей шифрования, вы должны использовать функцию вывода ключа на основе пароля PBKDF2 (Password-Based Key Derivation Function 2), генерируя ключ с помощью функциональности, предоставляемой OpenSSL::PKCS5.pbkdf2_hmac_sha1 или OpenSSL::PKCS5.pbkdf2_hmac.
Хотя существует Cipher#pkcs5_keyivgen, его использование устарело, и его следует использовать только в устаревших приложениях, потому что он не использует новые алгоритмы PKCS#5 v2.
Выбор IV
Режимы шифрования CBC, CFB, OFB и CTR требуют «вектора инициализации», или кратко, IV. Режим ECB — единственный режим, который не требует IV, но в нем почти нет оправданного использования из-за того, что он недостаточно скрывает шаблоны открытого текста. Поэтому
Вы никогда не должны использовать режим ECB, если вы абсолютно уверены, что вам это действительно необходимо
Из-за этого в любом случае вы получите режим, который явно требует IV. Хотя IV можно рассматривать как общедоступную информацию, т. е. ее можно передать открыто после генерации, она должна оставаться непредсказуемой, чтобы предотвратить определенные виды атак. Поэтому, желательно
Всегда создавайте безопасный случайный IV для каждого шифрования вашего Cipher
Новый случайный IV должен создаваться для каждого шифрования данных. Представьте IV как одноразовый идентификатор — он общедоступен, но случайный и непредсказуемый. Безопасный случайный IV можно создать следующим образом
cipher = ... cipher.encrypt key = cipher.random_key iv = cipher.random_iv # also sets the generated IV on the Cipher
Хотя ключ, как правило, также является случайным значением, его использование в качестве IV — плохая идея. Существуют сложные способы, с помощью которых злоумышленник может использовать такой IV. Как общее правило, прямое или косвенное раскрытие ключа следует избегать во всех случаях, и исключения допускаются только по уважительным причинам.
Вызов Cipher#final
Режимы ECB (который не следует использовать) и CBC — это режимы, основанные на блоках. Это означает, что в отличие от других потоковых режимов, они работают с блоками данных фиксированного размера, поэтому они требуют шага «окончания» для создания или правильного дешифрования последнего блока данных, надлежащим образом обрабатывая какую-либо форму дополнения. Поэтому крайне важно добавить результат вызова OpenSSL::Cipher#final в буфер шифрования/дешифрования, иначе вы столкнетесь с ошибками дешифрования или усеченными данными.
Хотя это не так уж необходимо для шифров в потоковом режиме, все же рекомендуется применять ту же схему добавления результата вызова Cipher#final — это также позволит вам легче переключаться между режимами в будущем.
Шифрование и дешифрование некоторых данных
data = "Very, very confidential data"
cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
encrypted = cipher.update(data) + cipher.final
...
decipher = OpenSSL::Cipher.new('aes-128-cbc')
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(encrypted) + decipher.final
puts data == plain #=> true
Проверка подлинности и шифрование данных (AEAD)
Если используемая версия OpenSSL ее поддерживает, режим аутентифицированного шифрования (например, GCM или CCM) всегда следует предпочитать любому неаутентифицированному режиму. В настоящее время OpenSSL поддерживает режим AE только в сочетании с ассоциированными Data (AEAD), где дополнительная ассоциированная информация включается в процесс шифрования для вычисления метки в конце шифрования. Эта метка также будет использоваться в процессе дешифрования и, проверив ее корректность, устанавливается подлинность данного зашифрованного текста.
Это превосходит неаутентифицированные режимы тем, что позволяет обнаружить, если кто-то фактически изменил зашифрованный текст после его шифрования. Это предотвращает злонамеренные изменения зашифрованного текста, которые в противном случае могли бы быть использованы для изменения зашифрованного текста в пользу потенциальных злоумышленников.
Ассоциированные данные используются, когда существует дополнительная информация, например, заголовки или метаданные, которые также должны быть проверены на подлинность, но необязательно шифроваться. Если для шифрования и последующего дешифрования не требуются ассоциированные данные, библиотека OpenSSL все равно требует установки значения — «» можно использовать в случае отсутствия таковых.
Пример использования GCM (режим Галуа/счетчика). У вас есть 16-байтовый ключ, 12-байтовый (96-битный) nonce и ассоциированные данные auth_data. Убедитесь, что вы не используете пару ключ и nonce повторно. Использование nonce повторно нарушает гарантии безопасности режима GCM.
cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
cipher.key = key
cipher.iv = nonce
cipher.auth_data = auth_data
encrypted = cipher.update(data) + cipher.final
tag = cipher.auth_tag # produces 16 bytes tag by default
Теперь вы — получатель. Вам известен ключ, и вы получили nonce, auth_data, зашифрованный текст и метку через небезопасную сеть. Обратите внимание, что GCM принимает метку произвольной длины от 1 до 16 байт. Вам может дополнительно потребоваться проверить, что полученная метка имеет правильную длину, или вы допускаете возможность злоумышленников подделать действительную метку в один байт для сфальсифицированного зашифрованного текста с вероятностью 1/256.
raise "tag is truncated!" unless tag.bytesize == 16
decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
decipher.key = key
decipher.iv = nonce
decipher.auth_tag = tag
decipher.auth_data = auth_data
decrypted = decipher.update(encrypted) + decipher.final
puts data == decrypted #=> true
Публичные методы класса
static VALUE
ossl_s_ciphers(VALUE self)
{
VALUE ary;
ary = rb_ary_new();
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
add_cipher_name_to_ary,
(void*)ary);
return ary;
} Возвращает имена всех доступных шифров в массиве.
static VALUE
ossl_cipher_initialize(VALUE self, VALUE str)
{
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
char *name;
name = StringValueCStr(str);
GetCipherInit(self, ctx);
if (ctx) {
ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
}
AllocCipher(self, ctx);
if (!(cipher = EVP_get_cipherbyname(name))) {
ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
}
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
return self;
} Строка должна содержать допустимое имя шифра, например, «aes-256-cbc».
Список имен шифров доступен, вызвав OpenSSL::Cipher.ciphers.
Публичные методы экземпляра
static VALUE
ossl_cipher_set_auth_data(VALUE self, VALUE data)
{
EVP_CIPHER_CTX *ctx;
unsigned char *in;
long in_len, out_len;
StringValue(data);
in = (unsigned char *) RSTRING_PTR(data);
in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "AEAD not supported by this cipher");
if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len))
ossl_raise(eCipherError, "couldn't set additional authenticated data");
return data;
} Устанавливает дополнительные аутентифицированные данные шифра. Это поле должно быть установлено при использовании режимов шифрования AEAD, таких как GCM или CCM. Если не следует использовать связанные данные, этот метод должен все равно вызываться со значением “”. Содержимое этого поля должно представлять собой нечувствительные данные, которые будут добавлены к шифртексту для генерации тега аутентификации, который подтверждает содержимое шифртекста.
AAD должен быть установлен до шифрования или дешифрования. В режиме шифрования он должен быть установлен после вызова Cipher#encrypt и установки Cipher#key= и Cipher#iv=. При дешифровании аутентифицированные данные должны быть установлены после ключа, IV и особенно после того, как был установлен тег аутентификации. То есть установите его только после вызова Cipher#decrypt, Cipher#key=, Cipher#iv= и Cipher#auth_tag=.
static VALUE
ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
{
VALUE vtag_len, ret;
EVP_CIPHER_CTX *ctx;
int tag_len = 16;
rb_scan_args(argc, argv, "01", &vtag_len);
if (NIL_P(vtag_len))
vtag_len = rb_attr_get(self, id_auth_tag_len);
if (!NIL_P(vtag_len))
tag_len = NUM2INT(vtag_len);
GetCipher(self, ctx);
if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "authentication tag not supported by this cipher");
ret = rb_str_new(NULL, tag_len);
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret)))
ossl_raise(eCipherError, "retrieving the authentication tag failed");
return ret;
} Получает тег аутентификации, сгенерированный режимами аутентифицированного шифрования Cipher (например, GCM). Этот тег может храниться вместе с шифртекстом, а затем устанавливаться в шифре дешифрования для аутентификации содержимого шифртекста на предмет изменений. Если задан необязательный целочисленный параметр tag_len, возвращаемый тег будет иметь длину tag_len байтов. Если параметр опущен, будет использоваться длина по умолчанию 16 байтов или длина, ранее установленная с помощью auth_tag_len=. Для максимальной безопасности следует выбирать максимально возможную длину.
Тег можно получить только после вызова Cipher#final.
static VALUE
ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
{
EVP_CIPHER_CTX *ctx;
unsigned char *tag;
int tag_len;
StringValue(vtag);
tag = (unsigned char *) RSTRING_PTR(vtag);
tag_len = RSTRING_LENINT(vtag);
GetCipher(self, ctx);
if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "authentication tag not supported by this cipher");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag))
ossl_raise(eCipherError, "unable to set AEAD tag");
return vtag;
} Устанавливает тег аутентификации для проверки целостности шифртекста. Это можно сделать только тогда, когда шифр поддерживает AE. Тег должен быть установлен после вызова Cipher#decrypt, Cipher#key= и Cipher#iv=, но перед вызовом Cipher#final. После выполнения всех операций дешифрования тег проверяется автоматически при вызове Cipher#final.
Для режима OCB длина тега должна быть указана с помощью auth_tag_len= заранее.
static VALUE
ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
{
int tag_len = NUM2INT(vlen);
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "AEAD not supported by this cipher");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL))
ossl_raise(eCipherError, "unable to set authentication tag length");
/* for #auth_tag */
rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len));
return vlen;
} Устанавливает длину тега аутентификации, который будет сгенерирован или задан для шифров AEAD, которые требуют его в качестве входного параметра. Обратите внимание, что не все шифры AEAD поддерживают этот метод.
В режиме OCB длина должна быть указана как при шифровании, так и при дешифровании, и должна быть указана до указания IV.
static VALUE
ossl_cipher_is_authenticated(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
} Указывает, использует ли этот экземпляр Cipher режим аутентифицированного шифрования.
static VALUE
ossl_cipher_block_size(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
} Возвращает размер в байтах блоков, над которыми работает этот Cipher.
static VALUE
ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
{
int in_len, out_len;
EVP_CIPHER_CTX *ctx;
in_len = NUM2INT(data_len);
GetCipher(self, ctx);
if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
ossl_raise(eCipherError, NULL);
return data_len;
} Устанавливает длину сообщения открытого/шифртекста, которое будет обработано в режиме CCM. Убедитесь, что вы вызываете этот метод после того, как key= и iv= были установлены, и перед auth_data=.
Вызывайте этот метод только после вызова Cipher#encrypt или Cipher#decrypt.
static VALUE
ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self)
{
return ossl_cipher_init(argc, argv, self, 0);
} Инициализирует Cipher для дешифрования.
Убедитесь, что вы вызываете Cipher#encrypt или Cipher#decrypt перед использованием любого из следующих методов:
Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 0).
static VALUE
ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self)
{
return ossl_cipher_init(argc, argv, self, 1);
} Инициализирует Cipher для шифрования.
Убедитесь, что вы вызываете Cipher#encrypt или Cipher#decrypt перед использованием любого из следующих методов:
Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1).
static VALUE
ossl_cipher_final(VALUE self)
{
EVP_CIPHER_CTX *ctx;
int out_len;
VALUE str;
GetCipher(self, ctx);
str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx));
if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len))
ossl_raise(eCipherError, NULL);
assert(out_len <= RSTRING_LEN(str));
rb_str_set_len(str, out_len);
return str;
} Возвращает оставшиеся данные, хранящиеся в объекте шифра. Дальнейшие вызовы Cipher#update или Cipher#final вернут мусор. Этот вызов всегда должен выполняться как последний вызов операции шифрования или дешифрования после того, как весь открытый текст или шифртекст был передан в экземпляр Cipher.
Если использовался аутентифицированный шифр, генерируется CipherError, если тег не может быть успешно аутентифицирован. Вызывайте этот метод только после установки тега аутентификации и передачи всего содержимого шифртекста в шифр.
static VALUE
ossl_cipher_copy(VALUE self, VALUE other)
{
EVP_CIPHER_CTX *ctx1, *ctx2;
rb_check_frozen(self);
if (self == other) return self;
GetCipherInit(self, ctx1);
if (!ctx1) {
AllocCipher(self, ctx1);
}
GetCipher(other, ctx2);
if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1)
ossl_raise(eCipherError, NULL);
return self;
} static VALUE
ossl_cipher_set_iv(VALUE self, VALUE iv)
{
EVP_CIPHER_CTX *ctx;
int iv_len = 0;
StringValue(iv);
GetCipher(self, ctx);
if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
if (!iv_len)
iv_len = EVP_CIPHER_CTX_iv_length(ctx);
if (RSTRING_LEN(iv) != iv_len)
ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len);
if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1)
ossl_raise(eCipherError, NULL);
return iv;
} Устанавливает IV шифра. Обратите внимание, что поскольку вы никогда не должны использовать режим ECB, IV всегда явно требуется и должен быть установлен до шифрования. Сам IV может быть безопасно передан публично, но он должен быть непредсказуемым для предотвращения определенных типов атак. Вы можете использовать Cipher#random_iv для создания безопасного случайного IV.
Вызывайте этот метод только после вызова Cipher#encrypt или Cipher#decrypt.
static VALUE
ossl_cipher_iv_length(VALUE self)
{
EVP_CIPHER_CTX *ctx;
int len = 0;
GetCipher(self, ctx);
if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)
len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
if (!len)
len = EVP_CIPHER_CTX_iv_length(ctx);
return INT2NUM(len);
} Возвращает ожидаемую длину в байтах для IV для этого Cipher.
static VALUE
ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
{
int len = NUM2INT(iv_length);
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER))
ossl_raise(eCipherError, "cipher does not support AEAD");
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL))
ossl_raise(eCipherError, "unable to set IV length");
/*
* EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save
* the length somewhere. Luckily currently we aren't using app_data.
*/
EVP_CIPHER_CTX_set_app_data(ctx, (void *)(VALUE)len);
return iv_length;
} Устанавливает длину IV/nonce для Cipher. Обычно шифры блочного типа не позволяют изменять длину IV, но некоторые используют IV для «nonce». Возможно, это потребуется для межпрограммной совместимости.
static VALUE
ossl_cipher_set_key(VALUE self, VALUE key)
{
EVP_CIPHER_CTX *ctx;
int key_len;
StringValue(key);
GetCipher(self, ctx);
key_len = EVP_CIPHER_CTX_key_length(ctx);
if (RSTRING_LEN(key) != key_len)
ossl_raise(rb_eArgError, "key must be %d bytes", key_len);
if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
rb_ivar_set(self, id_key_set, Qtrue);
return key;
} Устанавливает ключ шифра. Для генерации ключа следует использовать случайную строку байтов или, если ключ должен быть получен из пароля, использовать функциональность PBKDF2, предоставляемую OpenSSL::PKCS5. Для генерации ключа на основе случайных чисел можно использовать Cipher#random_key.
Вызывать этот метод следует только после вызова Cipher#encrypt или Cipher#decrypt.
static VALUE
ossl_cipher_key_length(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return INT2NUM(EVP_CIPHER_CTX_key_length(ctx));
} Возвращает длину ключа в байтах для Cipher.
static VALUE
ossl_cipher_set_key_length(VALUE self, VALUE key_length)
{
int len = NUM2INT(key_length);
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
if (EVP_CIPHER_CTX_set_key_length(ctx, len) != 1)
ossl_raise(eCipherError, NULL);
return key_length;
} Устанавливает длину ключа шифра. Если шифр имеет фиксированную длину, попытка установить длину ключа, отличную от фиксированного значения, приведёт к ошибке.
В обычных ситуациях вызывать этот метод не нужно (и, вероятно, не следует).
Для получения дополнительной информации см. EVP_CIPHER_CTX_set_key_length.
static VALUE
ossl_cipher_name(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx)));
} Возвращает имя шифра, которое может незначительно отличаться от исходного имени.
static VALUE
ossl_cipher_set_padding(VALUE self, VALUE padding)
{
EVP_CIPHER_CTX *ctx;
int pad = NUM2INT(padding);
GetCipher(self, ctx);
if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1)
ossl_raise(eCipherError, NULL);
return padding;
} Включает или отключает дополнение. По умолчанию операции шифрования дополняются стандартным блочным дополнением, а дополнение проверяется и удаляется при дешифровании. Если параметр pad равен нулю, дополнение не выполняется, при этом общий объём зашифрованных или расшифрованных данных должен быть кратным размеру блока, в противном случае возникнет ошибка.
Для получения дополнительной информации см. EVP_CIPHER_CTX_set_padding.
static VALUE
ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
{
EVP_CIPHER_CTX *ctx;
const EVP_MD *digest;
VALUE vpass, vsalt, viter, vdigest;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
int iter;
rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest);
StringValue(vpass);
if(!NIL_P(vsalt)){
StringValue(vsalt);
if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
ossl_raise(eCipherError, "salt must be an 8-octet string");
salt = (unsigned char *)RSTRING_PTR(vsalt);
}
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
if (iter <= 0)
rb_raise(rb_eArgError, "iterations must be a positive integer");
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest);
GetCipher(self, ctx);
EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
(unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1)
ossl_raise(eCipherError, NULL);
OPENSSL_cleanse(key, sizeof key);
OPENSSL_cleanse(iv, sizeof iv);
rb_ivar_set(self, id_key_set, Qtrue);
return Qnil;
} Генерирует и устанавливает ключ/IV на основе пароля.
ПРЕДУПРЕЖДЕНИЕ: Этот метод совместим с PKCS5 v1.5 только при использовании RC2, RC4-40 или DES с MD5 или SHA1. Использование чего-либо ещё (например, AES) сгенерирует ключ/iv с помощью метода, специфичного для OpenSSL. Этот метод устарел и больше не должен использоваться. Используйте метод генерации ключа PKCS5 v2 из OpenSSL::PKCS5.
Параметры
-
salt должен быть строкой из 8 байтов, если предоставлен.
-
iterations — целое число, по умолчанию 2048.
-
digest — объект
Digest, по умолчанию «MD5»
Рекомендуется использовать как минимум 1000 итераций.
# File ext/openssl/lib/openssl/cipher.rb, line 55 def random_iv str = OpenSSL::Random.random_bytes(self.iv_len) self.iv = str end
Генерирует случайный IV с помощью OpenSSL::Random.random_bytes и устанавливает его в шифр, а также возвращает его.
Вы должны вызвать encrypt или decrypt перед вызовом этого метода.
# File ext/openssl/lib/openssl/cipher.rb, line 43 def random_key str = OpenSSL::Random.random_bytes(self.key_len) self.key = str end
Генерирует случайный ключ с помощью OpenSSL::Random.random_bytes и устанавливает его в шифр, а также возвращает его.
Вы должны вызвать encrypt или decrypt перед вызовом этого метода.
static VALUE
ossl_cipher_reset(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
return self;
} Полностью сбрасывает внутреннее состояние Cipher. Это позволяет использовать один и тот же экземпляр Cipher несколько раз для задач шифрования или дешифрования.
Внутренне вызывает EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1).
static VALUE
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
{
EVP_CIPHER_CTX *ctx;
unsigned char *in;
long in_len, out_len;
VALUE data, str;
rb_scan_args(argc, argv, "11", &data, &str);
if (!RTEST(rb_attr_get(self, id_key_set)))
ossl_raise(eCipherError, "key not set");
StringValue(data);
in = (unsigned char *)RSTRING_PTR(data);
in_len = RSTRING_LEN(data);
GetCipher(self, ctx);
out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
if (out_len <= 0) {
ossl_raise(rb_eRangeError,
"data too big to make output buffer: %ld bytes", in_len);
}
if (NIL_P(str)) {
str = rb_str_new(0, out_len);
} else {
StringValue(str);
rb_str_resize(str, out_len);
}
if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
ossl_raise(eCipherError, NULL);
assert(out_len < RSTRING_LEN(str));
rb_str_set_len(str, out_len);
return str;
} Шифрует данные в потоковом режиме. Для шифрования передавайте последовательные блоки данных в метод update. Возвращает зашифрованный фрагмент данных. После завершения работы необходимо добавить результат вызова Cipher#final к результату.
Если параметр buffer задан, результат шифрования/расшифрования будет записан в него. buffer будет автоматически изменён в размере.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.