Java中具有无符号字节密钥的TOTP / HOTP / HmacSHA256

时间:2018-08-20 15:19:22

标签: java cryptography hmac one-time-password totp

从以下问题中我们可以看到:

Java HmacSHA256 with key

Java vs. Golang for HOTP (rfc-4226)

,当在TOTP / HOTP / HmacSHA256用例中使用密钥时,Java不能很好地发挥作用。我的分析是,以下原因引起了麻烦:

  • String.getBytes(当然)将为字符值> 127的字符提供负字节值;
  • javax.crypto.Macjavax.crypto.spec.SecretKeySpec在内部和外部都使用byte[]来接受和转换密钥。

我们已经获得了多个Feitian C-200 Single Button OTP devices,并且它们带有十六进制字符串机密,该字符串机密由字节值> 127组成。

我们已经在Ruby中成功为这些令牌创建了PoC,该PoC可以完美运行。由于我们想将它们集成到Keycloak中,因此我们需要找到一个Java解决方案。

由于我们已经看到每种TOTP / HOTP / HmacSHA256的实现都使用javax.crypto库和byte[],因此我们担心我们必须重写所有使用的类,但是必须依次使用int支持这种情况。

问:还有其他方法吗?如何在Java的HmacSHA256计算中使用秘密(其字节的值大于127)而不必重写所有内容?


更新

我看错了方向。我的问题是,该键由一个字符串(Java中的UTF-16)表示,其中包含Unicode字符,这些字符被getBytes()分解为 2 个字节,然后传递给{{1 }}。

强行SecretKeySpec可以解决此问题。

3 个答案:

答案 0 :(得分:2)

签名与未签名是一个表达问题,仅与人类有关。计算机不知道或关心0xFF对您来说是-1还是255。因此,不,您不需要使用整数,使用byte[]可以正常工作。

这并不意味着您不能破坏事物,因为某些操作基于默认的带符号变量类型进行。例如:

byte b = (byte)255;    // b is -1 or 255, depending on how you interpret it
int i = b;      // i is -1 or 2³² instead of 255
int u = b & 0xFF; // u is 255

似乎只有Java签署了原语(booleanchar不支持),似乎使很多人望而却步。但是Java完全有能力执行加密操作,因此所有这些“不可能”的问题都是用户错误。编写对安全敏感的代码时,您不需要这些。

答案 1 :(得分:0)

不要害怕Java :)我已经测试了来自不同供应商的数十种令牌,并且Java一切都很好,您只需要选择正确的转换器即可。

常见的问题是从String获取字节作为getBytes()而不是使用适当的转换器。您从供应商处获得的文件代表十六进制格式的秘密密钥,因此只需使用Google“ java十六进制字符串到字节数组”并选择解决方案即可。

十六进制,Base32,Base64只是一种表示形式,您可以轻松地从一种转换为另一种。

答案 2 :(得分:0)

(几年后),我遇到了完全相同的问题:我们得到了 Feitian设备,并且不得不设置它们的服务器端代码。
没有可用的实现与它们一起工作(php或java)。

解决方案:飞天设备附带十六进制种子。首先,您必须将种子解码为原始二进制文件(例如,在PHP中使用 hex2bin())。该数据是TOTP / HOTP功能的正确输入。
Java的 hex2bin()版本有点棘手,其解决方案显然写在OP问题中。

(长话短说:必须使用StandardCharsets.ISO_8859_1解释hex2bin的结果,否则某些字符将被解释为2个字节的utf-16字符,这最终会导致不同的密码)

String hex = "1234567890ABCDEF"; // original seed from Feitian

Sring secretKey = new String(hex2bin(hex), StandardCharsets.ISO_8859_1);
Key key = new SecretKeySpec(secretKey.getBytes(StandardCharsets.ISO_8859_1), "RAW");
// or without String representation:
Key key = new SecretKeySpec(hex2bin(hex), "RAW");