класс 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.binread('document')
sha256 = OpenSSL::Digest.new('SHA256')
digest = sha256.digest(data)
Хеширование нескольких фрагментов данных одновременно
data1 = File.binread('file1')
data2 = File.binread('file2')
data3 = File.binread('file3')
sha256 = OpenSSL::Digest.new('SHA256')
sha256 << data1
sha256 << data2
sha256 << data3
digest = sha256.digest
Использование экземпляра Digest
data1 = File.binread('file1')
sha256 = OpenSSL::Digest.new('SHA256')
digest1 = sha256.digest(data1)
data2 = File.binread('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, которое является либо полным (ln), либо коротким (sn) именем поддерживаемого алгоритма хеширования.
Если data (строка) задано, оно используется в качестве начального входного значения для экземпляра 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_new_cstr(EVP_MD_name(EVP_MD_CTX_get0_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_get0_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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.