我的 HMACSHA256 签名验证几乎可以但不完全是

My HMACSHA256 signature validation almost works but not quite

我写了一个方法,将 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);
    }
}

区别只是这里使用的编码不同造成的。您使用了Base64编码,但原始签名是Base64Url encoded. Base64Url encoding is the standard encoding for JWT

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

这应该可以解决问题:

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