класс 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. Каждый поддерживаемый алгоритм контрольной суммы имеет связанный с ним идентификатор объекта, которые, в свою очередь, имеют присвоенные им короткие/полные имена. Например, идентификатор объекта для 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)
Методы класса
# File ext/openssl/lib/openssl/digest.rb, line 34 def self.digest(name, data) super(data, name) end
Возвращает значение хеша, вычисленное с помощью name Digest. 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 (a String) задан, он используется в качестве начального ввода для экземпляра Digest, т.е.
digest = OpenSSL::Digest.new('sha256', 'digestdata')
эквивалентно
digest = OpenSSL::Digest.new('sha256')
digest.update('digestdata')
Открытые методы экземпляров
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 в том смысле, что любые 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
Закрытые методы экземпляров
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.