2

我需要在 Perl 中hash_hmac('ripemd160', $data, $key)产生与 PHP相同的结果

设法将其追溯到两个 perl 模块,只是不能让它们一起工作......

摘要::HMAC 和地穴::RIPEMD160

use Crypt::RIPEMD160;

use Digest::HMAC;
$hmac = Digest::HMAC->new('bar', 'Crypt::RIPEMD160');

$hmac->add('foo');
$digest = $hmac->digest;

有人知道我在做什么错吗?

如果我使用上面的代码,我会收到以下错误:Can't call method "add" on an undefined value at /usr/lib64/perl5/vendor_perl/5.12.4/Digest/HMAC.pm 第 28 行。

由于我无法在上面的代码中传递哈希函数引用,在查看了 hmac 函数中的 HMAC 模块后,我想我可以直接在我的代码中编写它:

my $data = 'bar';
my $key = 'foo';
$block_size = 160;
$block_size ||= 64;
$key = Crypt::RIPEMD160->hash($key) if length($key) > $block_size;
my $k_ipad = $key ^ (chr(0x36) x $block_size);
my $k_opad = $key ^ (chr(0x5c) x $block_size);
my $digest =  Crypt::RIPEMD160->hash($k_opad, Crypt::RIPEMD160->hash($k_ipad, $data));

这确实会产生哈希,但仍然是错误的

PHP 生成的哈希:isceebbf5cd5e34c888b493cf7f7c39a7b181b65a3

perl 哈希: hash21a2fa2bf39fd99d4c9cdf147added69c32d45f9e

老实说,我不在乎它是如何完成的以及使用什么模块,只要我得到与 php 函数产生的相同的哈希值......此时我很想编写一个我从 perl 调用的 php 脚本来获取那个哈希值。 .. :( 因为我的想法已经用完了...

4

3 回答 3

3

唯一的Digest::HMAC包括Digest::HMAC_MD5Digest::HMAC_SHA1。但是,我查看了Digest::HMAC_MD5. 整个过程大约有 20 行代码。它基本上创建了两种方法

sub hmac_md5 {
    hmac($_[0], $_[1], \&md5, 64);
}

sub hmac_md5_hex {
    unpack("H*", &hmac_md5);
}

这几乎就是整个程序。

如果你忘记了包的面向对象风格,而使用了函数风格,看起来这可能对你有用:

hmac($data, $key, \&ripemd160, 160);

或者也许只是:

hmac($data, $key \&ripemd160);

事实上,这已记录在 CPAN Digest::HMAC页面本身。

于 2013-04-18T21:51:33.613 回答
2

我在这个讨论中可能有点晚了,但是在谈论Crypt::Digest::RIPEMD160(我是这个模块的作者 :) 时,您可以轻松地使用来自同一系列模块的Crypt::Mac::HMAC创建 HMAC。

它很简单:

use Crypt::Mac::HMAC 'hmac';
$hmac_raw = hmac('RIPEMD160', $key, $data);
于 2013-09-09T11:58:17.913 回答
1

您的代码不起作用的原因是,虽然Crypt::RIPEMD160提供的接口看起来类似于标准Digest接口,但它并不完全兼容:特别reset()是 Crypt::RIPEMD160 的方法显然不返回引用到它被调用的对象,而Digest::HMAC中的代码恰好依赖于该细节。

通过稍微调整任一模块来修复这种不兼容性将是一件微不足道的事情,或者将缺少的返回值添加到 Crypt::RIPEMD5 或使 Digest::HMAC 减少对不必要的方法链接的依赖。后者就像换行一样简单:

$self->{hasher}->reset->add($self->{k_opad}, $inner_digest);

在 Digest::HMAC 到:

$self->{hasher}->reset;
$self->{hasher}->add($self->{k_opad}, $inner_digest);

(当然,我不建议您自己执行此操作,尽管您可以将问题报告给这些模块的维护者。)

但是,就目前的这两个模块而言,它是行不通的。我推荐的解决方案是使用非 OO 接口,正如 David W. 建议的那样,或者尝试更新的Crypt::Digest::RIPEMD160模块,该模块正确实现了 Digest 接口并且应该与 Digest:: 一起玩得更好: HMAC。


编辑: 实际上,David W. 的建议不会像给定的那样起作用,因为 Crypt::RIPEMD160 不会导出非 OOripemd160()函数。但是,您可以轻松地创建一个:

use Crypt::RIPEMD160;
sub ripemd160 {
    return Crypt::RIPEMD160->hash( join "", @_ );
}

然后像这样使用它:

use Digest::HMAC qw( hmac );
sub hmac_ripemd160 {
    return hmac( @_[0, 1], \&ripemd160, 64 );
}

(是的,64 字节是 HMAC-RIPEMD160 的正确块大小,因为 RIPEMD160 的输入块长度是 16 个 32 位字,等于 512 位或 64 字节。实际上,使用错误的输入块大小不太可能会导致任何问题,当然除了互操作性之外,但是 HMAC 结构的安全证明假设,为简单起见,密钥被填充为恰好是一个输入块长。因此,为了确保 HMAC- 的所有实现RIPEMD160 对相同的密钥和消息产生相同的输出,最好遵守此规则。)


编辑 2:好的,我尝试针对来自 RFC 2286 的 HMAC-RIPEMD160 测试向量测试我在上面发布的代码,但无法获得匹配的结果。我终于意识到两件事:

  1. Digest::HMAC 导出的非 OOhmac()函数假定传递给它的自定义哈希函数将接受多个参数并将它们连接起来。我上面的包装器的原始实现ripemd160()没有(但我修复了它,现在它可以了)。这可以说是 Digest::HMAC 中的一个错误,或者至少在其文档中。

  2. Crypt::RIPEMD160 模块附带子模块Crypt::RIPEMD160::MAC,它已经实现了 HMAC-RIPEMD160,尽管出于某种不正当的原因,文档实际上并未使用名称 HMAC。 但是,如果您查看代码,或者只是将输出与官方测试向量进行比较,那确实正是它的作用。

于 2013-04-19T01:31:09.217 回答