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

TOTP / HOTP / HmacSHA256 with unsigned bytes key in Java

从下面的问题可以看出:

Java HmacSHA256 with key

在 TOTP / HOTP / HmacSHA256 用例中使用密钥时,

、Java 并不能很好地发挥作用。我的分析是以下问题造成的:

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

我们已经在 Ruby 中为这些代币成功创建了一个 PoC,它可以完美运行。由于我们要将这些整合到Keycloak中,所以我们需要找到一个Java的解决方案。

由于我们看到的 TOTP / HOTP / HmacSHA256 的每个实现都使用了 javax.crypto 库和 byte[],我们担心我们必须重写所有使用的 类 但使用 int 为了支持这个场景。

问:还有别的方法吗?我们如何在 Java 的 HmacSHA256 计算中使用秘密,其中字节的值 > 127 而不必重写所有内容?


更新

我看错方向了。我的问题是密钥表示为字符串(Java 中的 UTF-16),其中包含 Unicode 字符,这些字符在 getBytes() 之前被分解为 两个 字节被传递到 SecretKeySpec.

在此转换中强制 StandardCharsets.ISO_8859_1 解决了问题。

有符号与无符号是一个主要仅与人类相关的演示问题。计算机不知道也不关心 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 完全能够执行加密操作,因此所有这些 "impossible" 的问题都只是用户错误。在编写安全敏感代码时,这不是您想要的。

不要害怕 Java :) 我已经测试了来自不同供应商的几十个令牌,Java 一切都很好,你只需要选择正确的转换器。

从字符串中获取字节作为 getBytes() 而不是使用正确的转换器是常见问题。您从供应商处获得的文件以十六进制格式表示密钥,因此只需 google 'java hex string to byte array' 并选择适合您的解决方案。

Hex、Base32、Base64 只是一种表示形式,您可以轻松地将它们相互转换。

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

解决方法:飞天设备自带的种子是十六进制的。首先,您必须将种子解码为原始二进制文件(例如,在 PHP 中使用 hex2bin())。该数据是 TOTP/HOTP 函数的正确输入。
hex2bin()版本的java有点tricky,其解决方案在OP的问题中写得很清楚。

(长话短说:你必须用 StandardCharsets 解释 hex2bin 的结果。ISO_8859_1,否则某些字符将被解释为 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");