class OpenSSL::Digest
OpenSSL::Digest 允许你计算任意数据的消息摘要(有时也互换称为“哈希值),这些摘要是密码学安全的,即 Digest 实现了一个安全的一向函数。
一向函数提供了一些有用的属性。例如,给定两个不同的输入,它们产生相同输出的概率极低。结合每个消息摘要算法都有一个固定的、只有几个字节的输出的事实,摘要经常被用来为任意数据创建唯一标识符。一个常见的例子是在数据库中存储二进制文档时创建唯一 ID。
一向函数的另一个有用特性(因此得名)是,给定一个摘要,无法得知产生它的原始数据,也就是说,识别原始输入的唯一方法是通过“暴力破解”所有可能的输入组合。
这些特性也使得一向函数成为公钥签名算法的理想伴侣:与其签署整个文档,不如先用一个速度快得多的消息摘要算法生成文档的哈希值,然后只用较慢的公钥算法签名其输出的几个字节。为了验证签名文档的完整性,只需重新计算哈希值并验证它是否与签名中的哈希值相等即可。
你可以通过在终端中运行此命令来获取系统中支持的所有摘要算法的列表
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)
Public Class Methods
Source
# File ext/openssl/lib/openssl/digest.rb, line 25 def self.digest(name, data) super(data, name) end
Source
static VALUE
ossl_s_digests(VALUE self)
{
VALUE ary;
ary = rb_ary_new();
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
add_digest_name_to_ary,
(void*)ary);
return ary;
}
返回一个包含所有可用摘要名称的数组。
Source
static VALUE
ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
{
EVP_MD_CTX *ctx;
const EVP_MD *md;
VALUE type, data, md_holder;
rb_scan_args(argc, argv, "11", &type, &data);
md = ossl_evp_md_fetch(type, &md_holder);
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");
rb_ivar_set(self, id_md_holder, md_holder);
if (!NIL_P(data)) return ossl_digest_update(self, data);
return self;
}
创建一个基于 string 的 Digest 实例,string 是支持的摘要算法的 ln(长名称)或 sn(短名称)。可以通过调用 OpenSSL::Digest.digests 来获取支持的算法列表。
如果提供了 data(一个 String),它将用作 Digest 实例的初始输入,即
digest = OpenSSL::Digest.new('sha256', 'digestdata')
等同于
digest = OpenSSL::Digest.new('sha256') digest.update('digestdata')
Public Instance Methods
Source
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
Source
static VALUE
ossl_digest_size(VALUE self)
{
EVP_MD_CTX *ctx;
GetDigest(self, ctx);
return INT2NUM(EVP_MD_CTX_size(ctx));
}
Source
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)));
}
Source
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 被设置回其初始状态。
Source
static 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
私有实例方法
Source
static VALUE
ossl_digest_finish(VALUE self)
{
EVP_MD_CTX *ctx;
VALUE str;
GetDigest(self, ctx);
str = rb_str_new(NULL, EVP_MD_CTX_size(ctx));
if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL))
ossl_raise(eDigestError, "EVP_DigestFinal_ex");
return str;
}