класс 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 определен класс, названный в честь шифра, например, для получения экземпляра AES можно также использовать
# these are equivalent
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
cipher = OpenSSL::Cipher::AES.new('128-CBC')
Наконец, из-за широкого распространения также определены дополнительные классы для различных размеров ключей AES
cipher = OpenSSL::Cipher::AES128.new(:CBC) cipher = OpenSSL::Cipher::AES192.new(:CBC) cipher = OpenSSL::Cipher::AES256.new(:CBC)
Выбор режима шифрования или дешифрования
Шифрование и дешифрование часто являются очень похожими операциями для симметричных алгоритмов, это отражается в отсутствии необходимости выбора разных классов для каждой операции; обе операции могут выполняться с использованием одного и того же класса. Тем не менее, после получения экземпляра Cipher, нам нужно указать экземпляру, что именно мы хотим с ним сделать, поэтому необходимо вызвать либо
cipher.encrypt
или
cipher.decrypt
на экземпляре Cipher. Это должно быть первое обращение после создания экземпляра, в противном случае настроенные ранее параметры могут быть потеряны в процессе.
Выбор ключа
Симметричное шифрование требует ключа, который одинаков для шифрования и дешифрования, и после первоначального установления ключа он должен храниться в конфиденциальности. Есть много способов создать небезопасные ключи, наиболее заметным из которых является просто взятие пароля в качестве ключа без дальнейшей обработки пароля. Простой и безопасный способ создания ключа для конкретного Cipher — это
cipher = OpenSSL::AES256.new(:CFB) cipher.encrypt key = cipher.random_key # also sets the generated key on the Cipher
Если вам абсолютно необходимо использовать пароли в качестве ключей шифрования, вы должны использовать функцию вывода ключа на основе пароля 2 (PBKDF2), сгенерировав ключ с помощью функциональности OpenSSL::PKCS5#pbkdf2_hmac_sha1 или OpenSSL::PKCS5#pbkdf2_hmac.
Хотя существует #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. Как общее правило, прямое или косвенное раскрытие ключа следует избегать в любом случае, и исключения следует делать только по уважительным причинам.
Вызов #final
Режимы ECB (который не следует использовать) и CBC являются блочными режимами. Это означает, что в отличие от других потоковых режимов, они работают с блоками данных фиксированного размера, и поэтому требуют шага «завершения», чтобы произвести или правильно расшифровать последний блок данных путем надлежащего обработки какой-либо формы заполнения. Поэтому крайне важно добавить результат #final в ваш буфер шифрования/дешифрования, иначе вы получите ошибки дешифрования или усеченные данные.
Хотя для потоковых шифров это не так уж и необходимо, все же рекомендуется применять ту же схему добавления результата #final и туда — это также позволит вам в будущем легче переключаться между режимами.
Шифрование и дешифрование данных
data = "Very, very confidential data" cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.encrypt key = cipher.random_key iv = cipher.random_iv encrypted = cipher.update(data) + cipher.final ... decipher = OpenSSL::Cipher::AES.new(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::AES.new(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::AES.new(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
Методы класса Public
static VALUE
ossl_s_ciphers(VALUE self)
{
VALUE ary;
ary = rb_ary_new();
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
(void(*)(const OBJ_NAME*,void*))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-128-CBC» или «3DES».
Список имен шифров доступен путем вызова ::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 должен быть установлен до шифрования или дешифрования. В режиме шифрования он должен быть установлен после вызова #encrypt и установки #key= и #iv=. При дешифровании аутентифицированные данные должны быть установлены после ключа, IV и, особенно, после установки тега аутентификации. То есть, установите их только после вызова #decrypt, #key=, #iv= и #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;
} Получает тег аутентификации, сгенерированный режимами аутентифицированного шифрования Шифр (например, GCM). Этот тег может храниться вместе с зашифрованным текстом, а затем устанавливаться в дешифрующем шифре для проверки целостности содержимого зашифрованного текста на предмет изменений. Если опциональный целочисленный параметр tag_len задан, возвращаемый тег будет иметь длину tag_len байтов. Если параметр опущен, будет использоваться значение по умолчанию 16 байтов или длина, ранее установленная параметром auth_tag_len=. Для максимальной безопасности следует выбирать наибольшую возможную длину.
Тег можно получить только после вызова #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. Тег должен быть установлен после вызова #decrypt, #key= и #iv=, но перед вызовом #final. После завершения всех операций дешифрования тег автоматически проверяется при вызове #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;
} Указывает, использует ли этот экземпляр Шифра режим аутентифицированного шифрования.
static VALUE
ossl_cipher_block_size(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
} Возвращает размер блоков в байтах, на которых этот Шифр работает.
static VALUE
ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self)
{
return ossl_cipher_init(argc, argv, self, 0);
} Инициализирует Шифр для дешифрования.
Убедитесь, что вы вызвали #encrypt или #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);
} Инициализирует Шифр для шифрования.
Убедитесь, что вы вызвали #encrypt или #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;
} Возвращает оставшиеся данные, содержащиеся в объекте шифра. Дальнейшие вызовы #update или #final вернут мусор. Этот вызов всегда должен выполняться последним в операции шифрования или дешифрования после того, как весь открытый текст или зашифрованный текст были предоставлены экземпляру Шифра.
Если использовался аутентифицированный шифр, возникает 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 может быть безопасно передан публично, но он должен быть непредсказуемым, чтобы предотвратить определенные виды атак. Для создания безопасного случайного IV можно использовать #random_iv.
Вызывайте этот метод только после вызова #encrypt или #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 в байтах для этого Шифра.
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 Шифра. Обычно блочные шифры не позволяют изменять длину 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. Для генерации безопасного случайного ключа можно использовать #random_key.
Вызывайте этот метод только после вызова #encrypt или #decrypt.
static VALUE
ossl_cipher_key_length(VALUE self)
{
EVP_CIPHER_CTX *ctx;
GetCipher(self, ctx);
return INT2NUM(EVP_CIPHER_CTX_key_length(ctx));
} Возвращает длину ключа в байтах Шифра.
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;
} Включает или выключает заполнение. По умолчанию операции шифрования заполняются стандартным блочным заполнением, а заполнение проверяется и удаляется при расшифровке. Если параметр padding равен нулю, заполнение не выполняется, а общее количество зашифрованных или расшифрованных данных должно быть кратно размеру блока, в противном случае произойдёт ошибка.
Дополнительную информацию см. в 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 вместо этого.
Параметры
-
соль должна быть строкой длиной 8 байт, если она указана.
-
итерации — целое число, по умолчанию 2048.
-
хэш — объект Digest, по умолчанию 'MD5'
Рекомендуется минимальное количество итераций 1000.
# File ext/openssl/lib/openssl/cipher.rb, line 54 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 42 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, чтобы зашифровать их. Возвращает зашифрованный фрагмент данных. Когда закончите, к результату необходимо добавить результат вызова #final.
Если buffer задан, результат шифрования/расшифровки будет записан в него. buffer будет автоматически изменён в размере.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.