如何在C#中生成HMAC-SHA1?

57

我正在尝试使用C#利用REST API。API创建者已经在PHP、Ruby和Java中提供了示例库。我遇到了一个问题,需要生成HMAC的一部分。

以下是他们提供的示例库中如何完成此操作的方法。

PHP

hash_hmac('sha1', $signatureString, $secretKey, false);

Ruby

digest = OpenSSL::Digest::Digest.new('sha1')
return OpenSSL::HMAC.hexdigest(digest, secretKey, signatureString)

Java

SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA1_ALGORITHM);

Mac mac = null;
mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);

byte[] bytes = mac.doFinal(signatureString.getBytes());

String form = "";
for (int i = 0; i < bytes.length; i++)
{
    String str = Integer.toHexString(((int)bytes[i]) & 0xff);
    if (str.length() == 1)
    {
        str = "0" + str;
    }

    form = form + str;
}
return form;

这是我在 C# 中的尝试。它不起作用。更新:下面的 C# 示例完美运行。我发现真正的问题是由于我的signatureString中换行字符的跨平台差异导致的。

var enc = Encoding.ASCII;
HMACSHA1 hmac = new HMACSHA1(enc.GetBytes(secretKey));
hmac.Initialize();

byte[] buffer = enc.GetBytes(signatureString);
return BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();

1
结果我的 C# 代码运行正常。真正的问题是由于 signatureString 中换行符的跨平台差异导致的。我将标记此问题以进行删除。 - jessegavin
10
不要删除问题,将您的代码作为答案发布并接受它。对于寻求执行相同操作的其他人来说,这个问题非常有用。 - Serhat Ozgel
我在文本“API响应…”上添加了进一步的更新,以避免潜在的误解。 - Ciro Corvino
3个回答

42

Vimvq1987的答案的一个扩展:

return hashValue.ToString();不能产生你想要/需要的输出。你需要将数组hashValue中的字节转换为它们的十六进制字符串表示。
可以简单地使用return BitConverter.toString(hashValue);(打印大写字母A-F),或者如果你喜欢更复杂一些:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        public static string Encode(string input, byte[] key)
        {
            HMACSHA1 myhmacsha1 = new HMACSHA1(key);
            byte[] byteArray = Encoding.ASCII.GetBytes(input);
            MemoryStream stream = new MemoryStream(byteArray);
            return myhmacsha1.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}",e), s => s );
        }


        static void Main(string[] args)
        {
            byte[] key = Encoding.ASCII.GetBytes("abcdefghijklmnopqrstuvwxyz");
            string input = "";
            foreach (string s in new string[] { "Marry", " had", " a", " little", " lamb" })
            {
                input += s;
                System.Console.WriteLine( Encode(input, key) );
            }
            return;
        }
    }
}

打印出

3545e064fb59bc4bfc02b6e1c3d4925c898aa504
3249f4c8468d4d67f465937da05b809eaff22fdb
87baaadf5d096677f944015e53d283834eb1e943
6325376820c29a09e3ab30db000033aa71d6927d
54579b0146e2476595381d837ee38863be358213

我得到了完全相同的结果

<?php
$secretKey = 'abcdefghijklmnopqrstuvwxyz';

$signatureString = '';
foreach( array('Marry',' had',' a',' little',' lamb') as $s ) {
    $signatureString .= $s;
    echo hash_hmac('sha1', $signatureString, $secretKey, false), "\n";
}

编辑:Dmitriy Nemykin 建议进行以下编辑

public static string Encode(string input, byte[] key)
{
    byte[] byteArray = Encoding.ASCII.GetBytes(input);
    using(var myhmacsha1 = new HMACSHA1(key))
    {
        var hashArray = myhmacsha1.ComputeHash(byteArray);
        return hashArray.Aggregate("", (s, e) => s + String.Format("{0:x2}",e), s => s );
    }
}

这被拒绝了。但正如James在对此答案的评论中指出的那样,using语句至少是一个好点。


myhmacsha1.ComputeHash(stream).Aggregate 出现错误:System.array 未定义 Aggregate。我有什么遗漏吗? - Roee Gavirel
根据您使用的 .net 版本,请参阅 http://msdn.microsoft.com/en-us/library/bb548651%28v=VS.90%29.aspx。 - VolkerK
嗯,那就是这样了。谢谢。 - Roee Gavirel
3
这里是否需要使用 MemoryStream?你的代码能否简化为 myhmacsha1.ComputeHash(byteArray)?并且,你应该考虑使用 using 块来包装你的 HMACSHA1(如果需要还有 MemoryStream)。 - James
VolkerK = 英雄地位 - Joe Schmucker

21

这个网站提供了跨语言的一些很好的示例:http://jokecamp.wordpress.com/2012/10/21/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/

撰写时的C#实现为:

private string CreateToken(string message, string secret)
{
 secret = secret ?? "";
 var encoding = new System.Text.ASCIIEncoding();
 byte[] keyByte = encoding.GetBytes(secret);
 byte[] messageBytes = encoding.GetBytes(message);
 using (var hmacsha256 = new HMACSHA256(keyByte))
 {
 byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
 return Convert.ToBase64String(hashmessage);
 }
}

6
尝试这个链接:http://msdn.microsoft.com/en-us/library/system.security.cryptography.hmacsha1.aspx。以下是简单的代码:
public string Encode(string input, byte [] key)
{
        HMACSHA1 myhmacsha1 = new HMACSHA1(key);
        byte[] byteArray = Encoding.ASCII.GetBytes( input );
        MemoryStream stream = new MemoryStream( byteArray ); 
        byte[] hashValue = myhmacsha1.ComputeHash(stream);
        return hashValue.ToString();
}

2
谢谢您的回答。然而,您的代码返回字符串“System.Byte[]”。因此,我修改了最后一行为“return BitConverter.ToString(hashValue).Replace("-","").ToLower()”,但我仍然无法让这个C#代码生成与其他语言相同的哈希值。 - jessegavin
7
非常晚了,但是 string.Join("", Array.ConvertAll(hashValue, b => b.ToString("x2"))) 应该能够起作用。 - Jason Larke

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