我的 HMACSHA256 签名验证几乎有效,但不完全有效

Cap*_*rco 1 java sha256 hmac jwt

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

这是单元测试:

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

这是代码:

@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);
}
Run Code Online (Sandbox Code Playgroud)

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

'SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'.equals('SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV/adQssw5c=')
Run Code Online (Sandbox Code Playgroud)

当然存在硬编码值,因为实现尚未完成,但我现在使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

jps*_*jps 5

差异只是由于这里使用的编码不同造成的。您使用了 Base64 编码,但原始签名是Base64Url编码的。根据 RFC7519,Base64Url 编码是JWT 的标准编码

每个部分都包含一个 base64url 编码的值

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

这段代码应该可以解决这个问题:

final String s = Base64.getUrlEncoder().withoutPadding().encodeToString(calcHmacSha256);
Run Code Online (Sandbox Code Playgroud)