класс 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>. Они представляют собой текстовое представление идентификаторов OBJECT ASN.1. Каждый поддерживаемый алгоритм контрольной суммы имеет связанный с ним идентификатор 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)
Открытые методы класса
# File ext/openssl/lib/openssl/digest.rb, line 36 def self.digest(name, data) super(data, name) end
Возвращает вычисленный data хеш с помощью 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 = GetDigestPtr(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 (строка) задан, он используется в качестве начального ввода для экземпляра 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_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
Закрытые методы экземпляра
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.