【问题标题】:Java vs. Golang for HOTP (rfc-4226)用于 HOTP 的 Java 与 Golang (rfc-4226)
【发布时间】:2017-12-14 00:31:43
【问题描述】:

我正在尝试在 Golang 中实现 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));
}

在 Go 中:

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,在 Go 中:83 140 247 158 115 130 253 207

有人知道这两条线的区别以及如何移植 java 线吗?

【问题讨论】:

    标签: java go cryptography sha512


    【解决方案1】:

    Java 中的byte 类型是有符号的,其范围为-128..127,而在Go 中byteuint8 的别名,范围为0..255。因此,如果要比较结果,则必须将 Java 的负值移动256(添加256)。

    提示:要以无符号方式显示 Java byte 值,请使用:byteValue & 0xff,它将 int 转换为 int,使用 byte 的 8 位作为 @ 中的最低 8 位987654332@。或者更好:以十六进制形式显示两个结果,这样您就不必关心符号...

    将 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,而 Go 数组的最后一个字节是 207。我猜你的counter 会在你没有发布的代码中的其他地方增加一次。

    不同的是,在 Java 中返回的是十六进制编码的结果,而在 Go 中返回的是 Base64 编码的结果(它们是两种不同的编码,给出完全不同的结果)。正如您所确认的,在返回 hex.EncodeToString(h.Sum(nil)) 的 Go 中,结果匹配。

    提示 #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 
    

    【讨论】:

    • 哇——直到!逆运算是什么? (采用 Go 版本并创建 java 版本)。非常感谢!
    • @ZachKauffman:这是相同的数据,只是将其解释为有符号或无符号的区别。
    • @ZachKauffman 是的,要以签名方式显示 Go byte 值,只需将其转换为 int8。请参阅编辑后的答案。
    • 太棒了!我想我需要稍微复习一下我的基础知识(双关语)。看起来在 Go 版本中将我的 return 从 base64.StdEncoding.EncodeToString(h.Sum(nil)) 更改为 hex.EncodeToString(h.Sum(nil)) 解决了这个问题。
    • @ZachKauffman Base64 和十六进制编码是完全不同的东西(给出完全不同的结果)。所以基本上 long -> 字节数组转换适合您的情况,但在您的示例中,counter 值不同。
    猜你喜欢
    • 2011-05-17
    • 2015-08-04
    • 2019-01-26
    • 2011-10-25
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2018-08-24
    • 2017-05-16
    相关资源
    最近更新 更多