HMC SHA1哈希 - C#生成的哈希输出与Ruby不同

5

我试图快速让一个有缺陷的 .Net 客户端库与我正在使用的第三方服务配合工作。原始库(可以正常工作)是用 Ruby 编写的,但他们针对 DotNet 的等效库会产生与 Ruby 库不同的哈希输出。

Ruby 加密代码如下:

def self.encrypt_string(input_string)
  raise Recurly::ConfigurationError.new("Recurly gem not configured") unless Recurly.private_key.present?
  digest_key = ::Digest::SHA1.digest(Recurly.private_key)
  sha1_hash = ::OpenSSL::Digest::Digest.new("sha1")
  ::OpenSSL::HMAC.hexdigest(sha1_hash, digest_key, input_string.to_s)
end

(可能)等价的C#代码如下:

private static string ComputePrivateHash(string dataToProtect)
{
    if(String.IsNullOrEmpty(Configuration.RecurlySection.Current.PrivateKey))
        throw new RecurlyException("A Private Key must be configured to use the Recurly Transparent Post API.");

    byte[] salt_binary = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(dataToProtect));
    string salt_hex = BitConverter.ToString(salt_binary).Replace("-", "").ToLower();
    string salt = salt_hex.Substring(0, 20);

    HMACSHA1 hmac_sha1 = new HMACSHA1(Encoding.ASCII.GetBytes(Configuration.RecurlySection.Current.PrivateKey));
    hmac_sha1.Initialize();

    byte[] private_key_binary = Encoding.ASCII.GetBytes(salt);
    byte[] passkey_binary = hmac_sha1.ComputeHash(private_key_binary, 0, private_key_binary.Length);

    return BitConverter.ToString(passkey_binary).Replace("-", "").ToLower();
}

实际的哈希输出会因为相同的输入和私钥而有所不同。C#方法有什么问题会导致它产生错误的哈希输出?
编辑: 这是我编写代码的方式,但它仍然产生错误的输出:
private static string ComputePrivateHash(string dataToProtect)
{
    if(String.IsNullOrEmpty(Configuration.RecurlySection.Current.PrivateKey))
        throw new RecurlyException("A Private Key must be configured to use the Recurly Transparent Post API.");

    var privateKey = Configuration.RecurlySection.Current.PrivateKey;
    var hashedData = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(dataToProtect));
    var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(privateKey));
    var hash = hmac.ComputeHash(hashedData);
    return BitConverter.ToString(hash).Replace("-", "").ToLower();
}

正确答案

感谢下面Henning的回答,我确定正确的代码如下:

var privateKey = Configuration.RecurlySection.Current.PrivateKey;
var hashedKey = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(privateKey));
var hmac = new HMACSHA1(hashedKey);
var hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(dataToProtect));
return BitConverter.ToString(hash).Replace("-", "").ToLower();

也许 http://stackoverflow.com/questions/3393790/how-to-do-it-in-ruby-on-rails 可以帮忙? - Andrey M.
1
你可能会想,但是不是的 :( - Nathan Ridley
你自己有没有尝试过调试这个问题?至少可以期望你去调查一下 HMAC 原语的二进制输入是否相同。你的 C# 版本在十六进制编码和子字符串提取方面做了一些奇怪的事情,而这些在你的 Ruby 代码中似乎并不存在。所以你确定 Ruby 版本实际上是在你背后完成所有这些工作吗? - hmakholm left over Monica
嘿,是的,我已经尽力调试这个问题了。我已经以几种不同的方式重写了该方法,并对每个运行单元测试以检查输出,但是我的所有尝试都没有奏效。我同意子字符串代码看起来有些不合适,但是我对加密有点模糊,而且我没有编写过这段代码;它是库中存在的内容。 - Nathan Ridley
你能展示一下在样例输入和关键字上的输出吗? - CesarB
显示剩余4条评论
1个回答

4
如果我理解这段代码的话,似乎Ruby代码在将key传递给HMAC之前会单独对其进行哈希(这在密码学上并不必要,因为如果需要,HMAC本身会对长键进行哈希),然后将哈希后的key与原始消息一起传递给HMAC。
另一方面,你的C#代码使用原始key和消息的哈希值计算HMAC。(不可思议的是,你存储哈希消息的变量称为“salt”和“private_key_binary”,尽管内容既不是盐也不是密钥...)
我无法想象Ruby和C#库在处理HMAC时会有如此不同以至于这是正确的做法。

太好了,你说得对,我没有正确理解我所读的内容。感谢你的指导;我已经更新了我的问题,包括正确的答案。 - Nathan Ridley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接