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)

公共类方法

digest(name, data) 点击切换源代码

返回使用 name Digest 计算的哈希值。name 是受支持的摘要算法的长名称或短名称。

示例

OpenSSL::Digest.digest("SHA256", "abc")
调用超类方法
# File openssl/lib/openssl/digest.rb, line 25
def self.digest(name, data)
  super(data, name)
end
OpenSSL::Digest.digests → array[string...] 点击切换源代码

以数组形式返回所有可用摘要的名称。

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;
}
new(string [, data]) → Digest 点击切换源代码

基于 string 创建一个 Digest 实例,string 是受支持的摘要算法的 ln(长名称)或 sn(短名称)。可以通过调用 OpenSSL::Digest.digests 获取受支持算法的列表。

如果给定了 data(字符串),则将其用作 Digest 实例的初始输入,即

digest = OpenSSL::Digest.new('sha256', 'digestdata')

等同于

digest = OpenSSL::Digest.new('sha256')
digest.update('digestdata')
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;
}

公共实例方法

<<(p1)

并非每个消息摘要都可以一次性计算完成。如果消息摘要要从多个后续来源计算得出,那么每个来源都可以单独传递给 Digest 实例。

示例

digest = OpenSSL::Digest.new('SHA256')
digest.update('First input')
digest << 'Second input' # equivalent to digest.update('Second input')
result = digest.digest
别名为: update
block_length → integer 点击切换源代码

返回摘要算法的块长度,即单个块的字节长度。大多数现代算法将要摘要的消息划分为一系列连续处理的固定大小的块。

示例

digest = OpenSSL::Digest.new('SHA1')
puts digest.block_length # => 64
static VALUE
ossl_digest_block_length(VALUE self)
{
    EVP_MD_CTX *ctx;

    GetDigest(self, ctx);

    return INT2NUM(EVP_MD_CTX_block_size(ctx));
}
digest_length → integer 点击切换源代码

返回摘要的输出大小,即最终消息摘要结果的字节长度。

示例

digest = OpenSSL::Digest.new('SHA1')
puts digest.digest_length # => 20
static VALUE
ossl_digest_size(VALUE self)
{
    EVP_MD_CTX *ctx;

    GetDigest(self, ctx);

    return INT2NUM(EVP_MD_CTX_size(ctx));
}
initialize_copy(p1) 点击切换源代码
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;
}
name → string 点击切换源代码

返回此 Digest 算法的短名称,该名称可能与提供的原始名称略有不同。

示例

digest = OpenSSL::Digest.new('SHA512')
puts digest.name # => SHA512
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)));
}
reset → self 点击切换源代码

重置 Digest,即放弃已执行的任何 Digest#update,并将 Digest 设置回其初始状态。

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;
}
update(string) → aString 点击切换源代码

并非每个消息摘要都可以一次性计算完成。如果消息摘要要从多个后续来源计算得出,那么每个来源都可以单独传递给 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_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;
}
也别名为: <<

私有实例方法

finish → aString 点击切换源代码
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;
}