класс 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 как nonce (число, используемое один раз) — он общедоступен, но случайный и непредсказуемый. Безопасный случайный 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 являются режимами блочного шифрования. Это означает, что в отличие от других режимов поточного шифрования, они работают с блоками данных фиксированного размера и поэтому требуют шага «финализации», чтобы получить или правильно расшифровать последний блок данных, соответствующим образом обработав форму padding. Поэтому крайне важно добавить результат вызова 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 только в сочетании с 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, encrypted и tag через небезопасную сеть. Обратите внимание, что 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);
if ((in_len = RSTRING_LEN(data)) == 0)
ossl_raise(rb_eArgError, "data must not be empty");
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.