【问题标题】:My HMACSHA256 signature validation almost works but not quite我的 HMACSHA256 签名验证几乎可以工作,但不完全
【发布时间】:2021-03-10 07:21:25
【问题描述】:

我编写了一个将 JWT 作为请求并检查签名是否有效的方法。

这是单元测试:

@Test
public void isValid() {
    final JwtValidator jwtValidator = JwtValidator.getInstance();
    final boolean valid = jwtValidator.isValid("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
    Assert.isTrue(valid);
}

这是代码:

@SneakyThrows
public boolean isValid(String extractedToken) {
    final String[] tokenParts = extractedToken.split(Pattern.quote("."));
    String header = tokenParts[0];
    String payload = tokenParts[1];
    String signature = tokenParts[2];

    final byte[] calcHmacSha256 = HMAC.calcHmacSha256("your-256-bit-secret".getBytes(), (header+"."+payload).getBytes());

    final String s = Base64.getEncoder().encodeToString(calcHmacSha256);

    System.out.println("'" + signature + "'.equals('"+s+"')");
    return signature.equals(s);
}

日志打印两个字符串,只有 2 个字符不同,所以我觉得我已经接近“但不完全”以使其工作:

'SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'.equals('SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV/adQssw5c=')

当然有硬编码值,因为实现不完整,但我现在使用https://jwt.io/ 中的示例值以便于使用。

谢谢!

编辑 1:

public class JwtValidatorTest {

    @Test
    public void isValid() {
        byte[] header64 = Base64.getEncoder().encode("{\"alg\":\"HS256\",\"typ\":\"JWT\"}".getBytes());
        byte[] payload64 = Base64.getEncoder().encode("{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022}".getBytes());

        final byte[] calcHmacSha256 = HMAC.calcHmacSha256("your-256-bit-secret".getBytes(), (header64+"."+payload64).getBytes());
        final String signature64 = Base64.getEncoder().encodeToString(calcHmacSha256);

        final String input = header64 + "." + payload64 + "." + signature64;

        final JwtValidator jwtValidator = JwtValidator.getInstance();

        final boolean valid = jwtValidator.isValid(input);
        
        Assert.isTrue(valid);
    }
}

【问题讨论】:

    标签: java jwt sha256 hmac


    【解决方案1】:

    差异只是这里使用的不同编码造成的。您使用了 Base64 编码,但原始签名是 Base64Url 编码的。 Base64Url 编码为standard encoding for JWT

    Base64Url 编码末尾没有填充 (=),字符 +/ 被替换为 -_

    这应该可以解决问题:

    final String s = Base64.getUrlEncoder().encodeToString(calcHmacSha256);
    

    【讨论】:

    • 我无法相信输入数据是“错误的”...我更改了测试并编辑了问题。测试通过了,但您介意检查它是否真的正确吗?
    • 也许你误解了我的回答。从 jwt.io 复制的原始令牌是正确的(使用 base64url 编码)所以你需要改变你的验证方法,并且 base64url 对计算的签名进行编码,如我的答案所示
    • 好吧,这个令牌用于服务器到服务器的通信(ADFS - OAuth2 客户端凭据流)。在这种情况下,令牌存储在标头中,而不是在 URL 中传递,因此我认为更改为 base64url 在我的情况下没有意义......或者是吗?
    • 令牌通常在标头中传输,但 base64 编码只是标准编码(请参阅我的答案中的链接)。如果您使用库来签名和验证令牌(我会推荐),那么您总是会得到 base64 url​​ 编码的令牌。所以只要接受它作为一个标准。
    猜你喜欢
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    • 2013-01-12
    • 2012-12-20
    • 1970-01-01
    相关资源
    最近更新 更多