从以下问题中我们可以看到:
Java vs. Golang for HOTP (rfc-4226)
,当在TOTP / HOTP / HmacSHA256用例中使用密钥时,Java不能很好地发挥作用。我的分析是,以下原因引起了麻烦:
String.getBytes
(当然)将为字符值> 127的字符提供负字节值; javax.crypto.Mac
和javax.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
可以解决此问题。
答案 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签署了原语(boolean
和char
不支持),似乎使很多人望而却步。但是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");