класс OpenSSL::Digest
OpenSSL::Digest позволяет вычислить хэш-значения (иногда их называют «хешами») произвольных данных, которые являются криптографически безопасными, т.е. Digest реализует безопасную одностороннюю функцию.
Односторонние функции обладают некоторыми полезными свойствами. Например, при двух разных входных данных вероятность того, что оба дадут один и тот же результат, очень низка. В сочетании с тем, что каждый алгоритм хэширования имеет выходные данные фиксированной длины всего в несколько байт, хэши часто используются для создания уникальных идентификаторов для произвольных данных. Типичный пример — создание уникального идентификатора для двоичных документов, хранящихся в базе данных.
Еще одной полезной характеристикой односторонних функций (и, следовательно, их названия) является то, что, зная хэш, нет никакой информации о первоначальных данных, которые его породили, т.е. единственный способ идентифицировать исходный вход — это «перебрать» все возможные комбинации входов.
Эти характеристики делают односторонние функции также идеальными компаньонами для алгоритмов подписи с открытым ключом: вместо подписи всего документа сначала вычисляется хэш документа с помощью значительно более быстрого алгоритма хэширования, и только несколько байт его результата должны быть подписаны с помощью более медленного алгоритма с открытым ключом. Для проверки целостности подписанного документа достаточно повторно вычислить хэш и проверить, что он равен хэшу в подписи.
К поддерживаемым алгоритмам хэширования относятся:
-
SHA, SHA1, SHA224, SHA256, SHA384 и SHA512
-
MD2, MD4, MDC2 и MD5
-
RIPEMD160
-
DSS, DSS1 (Псевдоалгоритмы для использования в подписях DSA. DSS эквивалентен SHA, а DSS1 эквивалентен SHA1)
Для каждого из этих алгоритмов существует подкласс Digest, который можно инициализировать так же просто, как, например,
digest = OpenSSL::Digest::SHA1.new
Сопоставление класса Digest и sn/ln
sn (короткое имя) и ln (полное имя) определены в <openssl/object.h> и <openssl/obj_mac.h>. Они представляют собой текстовые представления идентификаторов ASN.1 OBJECT. Каждый поддерживаемый алгоритм хэширования имеет связанный с ним идентификатор OBJECT, а у них, в свою очередь, есть короткие/полные имена. Например, идентификатором OBJECT для SHA-1 является 1.3.14.3.2.26, его sn — «SHA1», а ln — «sha1».
MD2
-
sn: MD2
-
ln: md2
MD4
-
sn: MD4
-
ln: md4
MD5
-
sn: MD5
-
ln: md5
SHA
-
sn: SHA
-
ln: SHA
SHA-1
-
sn: SHA1
-
ln: sha1
SHA-224
-
sn: SHA224
-
ln: sha224
SHA-256
-
sn: SHA256
-
ln: sha256
SHA-384
-
sn: SHA384
-
ln: sha384
SHA-512
-
sn: SHA512
-
ln: sha512
«Взлом» алгоритма хэширования означает нарушение его свойств односторонней функции, т.е. получение коллизии или нахождение способа получения исходных данных способами, более эффективными, чем перебор и т.д. Большинство поддерживаемых алгоритмов хэширования можно считать в этом смысле сломанными, даже очень популярные алгоритмы MD5 и SHA1. Если для вас безопасность имеет наивысшее значение, то вы, вероятно, должны полагаться на SHA224, SHA256, SHA384 или SHA512.
Хэширование файла
data = File.read('document')
sha256 = OpenSSL::Digest::SHA256.new
digest = sha256.digest(data)
Хэширование нескольких кусков данных одновременно
data1 = File.read('file1')
data2 = File.read('file2')
data3 = File.read('file3')
sha256 = OpenSSL::Digest::SHA256.new
sha256 << data1
sha256 << data2
sha256 << data3
digest = sha256.digest
Использование экземпляра Digest
data1 = File.read('file1')
sha256 = OpenSSL::Digest::SHA256.new
digest1 = sha256.digest(data1)
data2 = File.read('file2')
sha256.reset
digest2 = sha256.digest(data2)
Методы класса public
# File ext/openssl/lib/openssl/digest.rb, line 33 def self.digest(name, data) super(data, name) end
Возвращает значение хэша, вычисленного с помощью Digest name. name — это либо полное, либо короткое имя поддерживаемого алгоритма хэширования.
Примеры
OpenSSL::Digest.digest("SHA256", "abc")
что эквивалентно:
OpenSSL::Digest::SHA256.digest("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')
Методы экземпляра public
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::SHA1.new 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::SHA1.new 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::SHA512.new 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 в том смысле, что любые выполненные #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::SHA256.new
digest.update('First input')
digest << 'Second input' # equivalent to digest.update('Second input')
result = digest.digest
Методы экземпляра private
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.