класс 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
Если вам абсолютно необходимо использовать пароли в качестве ключей шифрования, вы должны использовать функцию вывода ключа из пароля (Password-Based Key Derivation Function 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 байт key, 12 байт (96 бит) nonce и связанные данные auth_data. Убедитесь, что вы не используете пару key и 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
Теперь вы получатель. Вам известен key и вы получили 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
Публичные методы класса
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_CTX_flags(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=. Для максимальной безопасности следует выбирать максимально возможную длину.
Метку можно получить только после вызова #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_CTX_flags(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_CTX_flags(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);
#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
#else
return Qfalse;
#endif
} Указывает, использует ли этот экземпляр 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_decrypt(int argc, VALUE *argv, VALUE self)
{
return ossl_cipher_init(argc, argv, self, 0);
} Инициализирует Cipher для дешифрования.
Убедитесь, что вы вызываете #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);
} Инициализирует Cipher для шифрования.
Убедитесь, что вы вызываете #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 вернут мусор. Этот вызов всегда должен выполняться как последний вызов операции шифрования или дешифрования после того, как весь открытый текст или шифротекст был передан в экземпляр Cipher.
Если использовался аутентифицированный шифр, возникает ошибка CipherError, если метку не удалось успешно аутентифицировать. Вызывайте этот метод только после установки метки аутентификации и передачи всего содержимого шифротекста в шифр.
static VALUE
ossl_cipher_set_iv(VALUE self, VALUE iv)
{
EVP_CIPHER_CTX *ctx;
int iv_len = 0;
StringValue(iv);
GetCipher(self, ctx);
#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
#endif
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 может быть безопасно передан по открытому каналу, но он должен быть непредсказуемым, чтобы предотвратить определенные виды атак. Вы можете использовать #random_iv для создания безопасного случайного IV.
Вызывайте этот метод только после вызова #encrypt или #decrypt.
static VALUE
ossl_cipher_iv_length(VALUE self)
{
EVP_CIPHER_CTX *ctx;
int len = 0;
GetCipher(self, ctx);
#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
#endif
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_CTX_flags(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. Для генерации ключа на основе безопасных случайных чисел можно использовать #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));
} Возвращает длину ключа в байтах для 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() : GetDigestPtr(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 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.