c # и java - разница между хешем hmacsha256

У меня есть следующий код на 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);

и следующий код на С#:

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);

Массивы байтов «secretKey» и «bytes» эквивалентны, но массив байтов «rawHmac» отличается, и строка «result» отличается. Кто-нибудь может понять, почему?


person Greg Tarr    schedule 05.11.2012    source источник
comment
Нужно было также сказать, что data и secretKey являются эквивалентными строками.   -  person Greg Tarr    schedule 05.11.2012


Ответы (2)


Не делайте этого:

byte[] bytes = data.getBytes();

Это будет использовать кодировку платформы по умолчанию для преобразования строки в массив байтов. Это может варьироваться в зависимости от платформы, в то время как вам нужно что-то повторяющееся. Я бы предложил UTF-8:

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

(Конечно, сделайте то же самое для ключа.)

Затем вы должны использовать ту же кодировку в своем C# - не ASCII, если вы действительно не хотите обрабатывать символы, отличные от ASCII.

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

Также неясно, как вы потом сравниваете результаты - не забывайте, что byte подписано в Java, но не подписано в C#. Вероятно, проще всего преобразовать хеш в шестнадцатеричный или base64 для целей сравнения.

РЕДАКТИРОВАТЬ: я сильно подозреваю, что последняя часть была проблемой - сравнение результатов.

Вот две короткие, но законченные программы (использующие iharder.net конвертер base64 в Java), которые выдают одинаковый вывод base64:

Джава:

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#:

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));
    }
}

Выход из обоих:

ivEyFpkagEoghGnTw/LmfhDOsiNbcnEON50mFGzW9/w=
person Jon Skeet    schedule 05.11.2012
comment
Спасибо, но, как я уже сказал, байты[] содержат одни и те же числа на обоих языках. Так что, хотя ваши комментарии полезны, они не решают проблему. - person Greg Tarr; 05.11.2012
comment
@GregT: Ну, они хотя бы исправляют проблему. См. также мой последний абзац - вы не дали нам никаких указаний на то, как вы их сравниваете. Если бы вы могли отредактировать свой вопрос, предоставив две короткие, но полные программы, демонстрирующие проблему, это облегчило бы жизнь. - person Jon Skeet; 05.11.2012
comment
Спасибо, сделал это сейчас. Я не знал о разнице между байтами. - person Greg Tarr; 05.11.2012
comment
@GregT: я отредактировал свой ответ, чтобы показать короткие, но полные программы, которые действительно дают один и тот же ответ. Можете ли вы отредактировать свой вопрос таким образом, чтобы отображались различные ответы? (Код, который вы дали, еще не завершен.) - person Jon Skeet; 05.11.2012
comment
Можно ли получить Base64 в java с помощью стандартного API? Я не могу редактировать код Java, из которого взят этот пример. - person Greg Tarr; 05.11.2012
comment
@GregT: я получаю тот же результат, используя javax.xml.bind.DatatypeConverter. Вы пробовали мой пример кода? Это действительно звучит так, как будто это, вероятно, проблема сравнения... - person Jon Skeet; 05.11.2012
comment
Да, это работает с моими данными и моим ключом. Это должно быть что-то в данных/ключах, которые я передаю - person Greg Tarr; 05.11.2012
comment
@GregT: Что ж, если что-то не в формате ASCII, это, безусловно, будет соответствовать моим предыдущим пунктам. Также проверьте длину каждой строки - если у вас есть непечатаемые символы в конце одной, но не в другой, это повлияет на ситуацию. - person Jon Skeet; 05.11.2012
comment
Это все ASCII, и я внес предложенные вами изменения. Есть несколько новых символов строки \n - person Greg Tarr; 05.11.2012
comment
@GregT: Ну, на этом этапе вы должны иметь возможность взять две автономные программы, которые я создал, и предоставить пару ключ/данные, которая показывает разницу. - person Jon Skeet; 05.11.2012

Это был не вопрос, как показано, хэши всегда одинаковы.

Проблема в моем случае была не связана с тем фактом, что кодировка процентов в верхнем регистре Java в UrlEncoder, а .NET - нет.

Это показывает, насколько важно проводить испытания в изоляции!

person Greg Tarr    schedule 06.11.2012