Ява против Голанга для HOTP (rfc-4226)
Я пытаюсь реализовать HOTP (rfc-4226) на Голанге, и я изо всех сил пытаюсь создать действительный HOTP. Я могу сгенерировать его в Java, но по какой-то причине моя реализация в Golang отличается. Вот образцы:
public static String constructOTP(final Long counter, final String key)
throws NoSuchAlgorithmException, DecoderException, InvalidKeyException {
final Mac mac = Mac.getInstance("HmacSHA512");
final byte[] binaryKey = Hex.decodeHex(key.toCharArray());
mac.init(new SecretKeySpec(binaryKey, "HmacSHA512"));
final byte[] b = ByteBuffer.allocate(8).putLong(counter).array();
byte[] computedOtp = mac.doFinal(b);
return new String(Hex.encodeHex(computedOtp));
}
и в го:
func getOTP(counter uint64, key string) string {
str, err := hex.DecodeString(key)
if err != nil {
panic(err)
}
h := hmac.New(sha512.New, str)
bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, counter)
h.Write(bs)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
Я считаю, что проблема заключается в том, что строка Java: ByteBuffer.allocate(8).putLong(counter).array();
генерирует байтовый массив, отличный от строки Go: binary.BigEndian.PutUint64(bs, counter)
,
В Java генерируется следующий байтовый массив: 83 -116 -9 -98 115 -126 -3 -48
и в го: 83 140 247 158 115 130 253 207
,
Кто-нибудь знает разницу в двух линиях и как я могу портировать Java-линию, чтобы пойти?
1 ответ
byte
тип в Java подписан, у него есть диапазон -128..127
в то время как в го byte
псевдоним uint8
и имеет ряд 0..255
, Так что если вы хотите сравнить результаты, вы должны сдвинуть отрицательные значения Java на 256
(добавлять 256
).
Совет: для отображения Java byte
значение без знака, используйте: byteValue & 0xff
который преобразует его в int
используя 8 битов byte
как младшие 8 бит в int
, Или лучше: отображать оба результата в шестнадцатеричной форме, так что вам не нужно заботиться о подписи...
Добавляя 256 к вашим отрицательным значениям байтов Java, вывод почти идентичен выводам Go: последний байт выключен на 1:
javabytes := []int{83, -116, -9, -98, 115, -126, -3, -48}
for i, b := range javabytes {
if b < 0 {
javabytes[i] += 256
}
}
fmt.Println(javabytes)
Выход:
[83 140 247 158 115 130 253 208]
Итак, последний байт вашего массива Java 208
в то время как Го 207
, Я угадываю твои counter
увеличивается в другом месте в вашем коде, который вы не опубликовали.
Отличие состоит в том, что в Java вы возвращаете результат в шестнадцатеричном коде, а в Go вы возвращаете результат в кодировке Base64 (это 2 разных кодировки, дающие совершенно разные результаты). Как вы подтвердили, в Go возвращаются hex.EncodeToString(h.Sum(nil))
результаты совпадают.
Совет № 2: чтобы отображать байты Go в подписанном виде, просто преобразуйте их в int8
(который подписан) вот так:
gobytes := []byte{83, 140, 247, 158, 115, 130, 253, 207}
for _, b := range gobytes {
fmt.Print(int8(b), " ")
}
Это выводит:
83 -116 -9 -98 115 -126 -3 -49