简体   繁体   English

c#和java - hmacsha256哈希之间的区别

[英]c# and java - difference between hmacsha256 hash

I have the following code in Java: 我在Java中有以下代码:

byte[] secretKey = secretAccessKey.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] bytes = data.getBytes("UTF-8");
byte[] rawHmac = mac.doFinal(bytes);
String result = javax.xml.bind.DatatypeConverter.printBase64Binary(rawHmac);

and the following code in C#: 以及C#中的以下代码:

UTF8Encoding enc = new UTF8Encoding();
byte[] secretKey = enc.GetBytes(secretAccessKey);
HMACSHA256 hmac = new HMACSHA256(secretKey);
hmac.Initialize();
byte[] bytes = enc.GetBytes(data);
byte[] rawHmac = hmac.ComputeHash(bytes);
string result = Convert.ToBase64String(rawHmac);

The byte arrays "secretKey" and "bytes" are equivalent but the byte array "rawHmac" is different, and the string "result" is different. 字节数组“secretKey”和“bytes”是等效的,但字节数组“rawHmac”是不同的,字符串“result”是不同的。 Can anyone see why? 谁能明白为什么?

Don't do this: 不要这样做:

byte[] bytes = data.getBytes();

That will use the platform default encoding to convert a string to a byte array. 这将使用平台默认编码将字符串转换为字节数组。 That can vary between platform, whereas you want something repeatable. 这可以在平台之间变化,而你想要可重复的东西。 I would suggest UTF-8: 我建议使用UTF-8:

byte[] bytes = data.getBytes("UTF-8");

(Do the same for the key, of course.) (当然,为密钥做同样的事。)

You should then use the same encoding in your C# - not ASCII, unless you really want to not handle non-ASCII characters. 然后,您应该在C#中使用相同的编码 - 而不是 ASCII,除非您真的不想处理非ASCII字符。

byte[] bytes = Encoding.UTF8.GetBytes(data);

It's also not clear how you're comparing the results afterwards - don't forget that byte is signed in Java, but unsigned in C#. 还不清楚你之后如何比较结果 - 不要忘记该byte是用Java签名的,但是在C#中是无符号的。 It's probably simplest to convert the hash to hex or base64 for comparison purposes. 为了进行比较,将哈希转换为十六进制或base64可能是最简单的。

EDIT: I strongly suspect the last part was the problem - comparing the results. 编辑:我强烈怀疑最后一部分是问题 - 比较结果。

Here are two short but complete programs (using the iharder.net base64 converter in Java) which produce the same base64 output: 这里有两个简短但完整的程序(使用Java中的iharder.net base64转换器),它产生相同的base64输出:

Java: Java的:

import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Test {
    public static void main (String[] args) throws Exception {
        String secretAccessKey = "mykey";
        String data = "my data";
        byte[] secretKey = secretAccessKey.getBytes();
        SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        byte[] bytes = data.getBytes();
        byte[] rawHmac = mac.doFinal(bytes);
        System.out.println(Base64.encodeBytes(rawHmac));
    }
}

C#: C#:

using System;
using System.Security.Cryptography;
using System.Text;

class Test
{
    static void Main()
    {
        String secretAccessKey = "mykey";
        String data = "my data";
        byte[] secretKey = Encoding.UTF8.GetBytes(secretAccessKey);
        HMACSHA256 hmac = new HMACSHA256(secretKey);
        hmac.Initialize();
        byte[] bytes = Encoding.UTF8.GetBytes(data);
        byte[] rawHmac = hmac.ComputeHash(bytes);
        Console.WriteLine(Convert.ToBase64String(rawHmac));
    }
}

Output from both: 两者的输出:

ivEyFpkagEoghGnTw/LmfhDOsiNbcnEON50mFGzW9/w=

This was a non-question, as demonstrated, the hashes are always the same. 这是一个非问题,如所示,哈希总是相同的。

The problem in my case was unrelated, the fact that Java uppercases percent encoding on UrlEncoder but .NET doesn't. 我的案例中的问题是无关紧要的,Java在UrlEncoder上编码百分比编码但.NET没有。

Goes to show how important it is to test in isolation! 去展示隔离测试的重要性!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM