класс OpenSSL::Digest
OpenSSL::Digest позволяет вычислять криптографически защищённые хэш-значения произвольных данных, то есть Digest реализует безопасную одностороннюю функцию.
Односторонние функции обладают полезными свойствами. Например, при наличии двух различных входных данных вероятность того, что оба дадут одинаковый результат, крайне низка. В сочетании с тем, что каждый алгоритм хэширования имеет выходные данные фиксированной длины всего в несколько байт, хэши часто используются для создания уникальных идентификаторов произвольных данных. Распространённый пример — создание уникального идентификатора для двоичных документов, хранящихся в базе данных.
Ещё одной полезной характеристикой односторонних функций (и, следовательно, их названием) является то, что зная хэш, невозможно определить исходные данные, которые его породили, то есть единственный способ определить исходный вход — «перебор» всех возможных комбинаций входов.
Эти характеристики также делают односторонние функции идеальными компаньонами для алгоритмов подписи с открытым ключом: вместо подписи всего документа сначала вычисляется хэш документа с помощью значительно более быстрого алгоритма хэширования, и подписываются только несколько байт его результата с использованием более медленного алгоритма подписи с открытым ключом. Для проверки целостности подписанного документа достаточно пересчитать хэш и проверить, что он равен хэшу в подписи.
Чтобы получить список всех поддерживаемых на вашей системе алгоритмов хэширования, выполните эту команду в своей терминальной оболочке:
openssl list -digest-algorithms
Среди поддерживаемых алгоритмов хэширования OpenSSL 1.1.1 есть:
-
SHA224, SHA256, SHA384, SHA512, SHA512-224 и SHA512-256
-
SHA3-224, SHA3-256, SHA3-384 и SHA3-512
-
BLAKE2s256 и BLAKE2b512
Каждый из этих алгоритмов можно инициализировать, используя имя:
digest = OpenSSL::Digest.new('SHA256')
«Взлом» алгоритма хэширования означает нарушение его свойств односторонней функции, то есть создание коллизии или нахождение способа получить исходные данные с помощью более эффективных методов, чем перебор и т. п. Большинство поддерживаемых алгоритмов хэширования в этом смысле можно считать взломанными, даже такие популярные алгоритмы, как MD5 и SHA1. Если безопасность для вас — первостепенная задача, вам следует, вероятно, использовать SHA224, SHA256, SHA384 или SHA512.
Хэширование файла
data = File.read('document')
sha256 = OpenSSL::Digest.new('SHA256')
digest = sha256.digest(data)
Хэширование нескольких фрагментов данных одновременно
data1 = File.read('file1')
data2 = File.read('file2')
data3 = File.read('file3')
sha256 = OpenSSL::Digest.new('SHA256')
sha256 << data1
sha256 << data2
sha256 << data3
digest = sha256.digest
Использование экземпляра Digest
data1 = File.read('file1')
sha256 = OpenSSL::Digest.new('SHA256')
digest1 = sha256.digest(data1)
data2 = File.read('file2')
sha256.reset
digest2 = sha256.digest(data2)
Публичные методы класса
# File ext/openssl/lib/openssl/digest.rb, line 29 def self.digest(name, data) super(data, name) end
Возвращает значение хэша, вычисленное с использованием name Digest. name — это либо полное, либо краткое имя поддерживаемого алгоритма хэширования.
Примеры
OpenSSL::Digest.digest("SHA256", "abc")
что эквивалентно:
OpenSSL::Digest.digest('SHA256', "abc")
static VALUE
ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
{
EVP_MD_CTX *ctx;
const EVP_MD *md;
VALUE type, data;
rb_scan_args(argc, argv, "11", &type, &data);
md = ossl_evp_get_digestbyname(type);
if (!NIL_P(data)) StringValue(data);
TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx);
if (!ctx) {
RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new();
if (!ctx)
ossl_raise(eDigestError, "EVP_MD_CTX_new");
}
if (!EVP_DigestInit_ex(ctx, md, NULL))
ossl_raise(eDigestError, "Digest initialization failed");
if (!NIL_P(data)) return ossl_digest_update(self, data);
return self;
} Создаёт экземпляр Digest на основе string, которое является полным или сокращённым именем поддерживаемого алгоритма хэширования.
Если data (a String) задано, оно используется в качестве начального входного значения для экземпляра Digest, т. е.
digest = OpenSSL::Digest.new('sha256', 'digestdata')
эквивалентно
digest = OpenSSL::Digest.new('sha256')
digest.update('digestdata')
Публичные методы экземпляра
Не каждый алгоритм хэширования может быть вычислен за один проход. Если хэш нужно вычислить из нескольких последующих источников, каждый из них можно передать в экземпляр Digest по отдельности.
Пример
digest = OpenSSL::Digest.new('SHA256')
digest.update('First input')
digest << 'Second input' # equivalent to digest.update('Second input')
result = digest.digest
static VALUE
ossl_digest_block_length(VALUE self)
{
EVP_MD_CTX *ctx;
GetDigest(self, ctx);
return INT2NUM(EVP_MD_CTX_block_size(ctx));
} Возвращает длину блока алгоритма хэширования, т. е. длину одного блока в байтах. Большинство современных алгоритмов разбивают входное сообщение на последовательность блоков фиксированной длины, которые обрабатываются последовательно.
Пример
digest = OpenSSL::Digest.new('SHA1')
puts digest.block_length # => 64
static VALUE
ossl_digest_size(VALUE self)
{
EVP_MD_CTX *ctx;
GetDigest(self, ctx);
return INT2NUM(EVP_MD_CTX_size(ctx));
} Возвращает размер выходных данных хэша, т. е. длину итогового значения хэша в байтах.
Пример
digest = OpenSSL::Digest.new('SHA1')
puts digest.digest_length # => 20
static VALUE
ossl_digest_copy(VALUE self, VALUE other)
{
EVP_MD_CTX *ctx1, *ctx2;
rb_check_frozen(self);
if (self == other) return self;
TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1);
if (!ctx1) {
RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new();
if (!ctx1)
ossl_raise(eDigestError, "EVP_MD_CTX_new");
}
GetDigest(other, ctx2);
if (!EVP_MD_CTX_copy(ctx1, ctx2)) {
ossl_raise(eDigestError, NULL);
}
return self;
} static VALUE
ossl_digest_name(VALUE self)
{
EVP_MD_CTX *ctx;
GetDigest(self, ctx);
return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx)));
} Возвращает сокращённое имя (sn) этого алгоритма Digest.
Пример
digest = OpenSSL::Digest.new('SHA512')
puts digest.name # => SHA512
static VALUE
ossl_digest_reset(VALUE self)
{
EVP_MD_CTX *ctx;
GetDigest(self, ctx);
if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL) != 1) {
ossl_raise(eDigestError, "Digest initialization failed.");
}
return self;
} Сбрасывает состояние Digest так, что все выполненные Digest#update операции игнорируются, и Digest возвращается в начальное состояние.
VALUE
ossl_digest_update(VALUE self, VALUE data)
{
EVP_MD_CTX *ctx;
StringValue(data);
GetDigest(self, ctx);
if (!EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)))
ossl_raise(eDigestError, "EVP_DigestUpdate");
return self;
} Не каждый алгоритм хэширования может быть вычислен за один проход. Если хэш нужно вычислить из нескольких последующих источников, каждый из них можно передать в экземпляр Digest по отдельности.
Пример
digest = OpenSSL::Digest.new('SHA256')
digest.update('First input')
digest << 'Second input' # equivalent to digest.update('Second input')
result = digest.digest
Приватные методы экземпляра
static VALUE
ossl_digest_finish(int argc, VALUE *argv, VALUE self)
{
EVP_MD_CTX *ctx;
VALUE str;
int out_len;
GetDigest(self, ctx);
rb_scan_args(argc, argv, "01", &str);
out_len = EVP_MD_CTX_size(ctx);
if (NIL_P(str)) {
str = rb_str_new(NULL, out_len);
} else {
StringValue(str);
rb_str_resize(str, out_len);
}
if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL))
ossl_raise(eDigestError, "EVP_DigestFinal_ex");
return str;
}
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.