验证JsonWebToken签名

时间:2018-07-11 07:55:34

标签: java jwt digital-signature

我正在接收JWT,并想验证其签名。 它未加密,基于base64编码,并使用HmacSha256签名。 它是用我知道的秘密签名的。

如果不使用https://jwt.io/上列出的第三方库(例如java-jwt,jpose4j等),我似乎找不到如何验证签名的任何示例。

可以这样做吗?

到目前为止我所拥有的:

private boolean validateSignature( String header, String data, String signature, String secretKey ) throws Exception {
    Base64 base64 = new Base64( true );
    SecretKeySpec secret = new SecretKeySpec( secretKey.getBytes(), "HmacSHA256" );
    Mac mac = Mac.getInstance( "HmacSHA256" );
    mac.init( secret );

    byte[] hmacDataBytes = mac.doFinal( data.getBytes( StandardCharsets.UTF_8.name()) );
    String hmacData = new String( hmacDataBytes );

    return hmacData.equals( signature ); // Compare signatures here...
}

基于@pedrofb和@jps答案,这是解决方案:

private boolean validToken( String authToken, String key ) throws Exception {
    String[] token = authToken.split( "\\." );
    String header = token[0];
    String payload = token[1];
    String originalSignature = token[2];

    SecretKeySpec secret = new SecretKeySpec( Base64.getDecoder().decode( key ), ALGORITM_HMACSHA256 );
    Mac mac = Mac.getInstance( ALGORITM_HMACSHA256 );
    mac.init( secret );

    StringBuilder headerAndPayload = new StringBuilder( header ).append( "." ).append( payload );

    byte[] signatureBytes = mac.doFinal( headerAndPayload.toString().getBytes( StandardCharsets.UTF_8.name() ) );
    String calculatedSignature = Base64.getUrlEncoder().withoutPadding().encodeToString( signatureBytes );

    return calculatedSignature.equals( originalSignature );
}

2 个答案:

答案 0 :(得分:3)

JWT由base64url编码的三部分用点分隔

header.payload.signature

签名是根据header.payload

计算的

假设您的方法接收到base64url中的元素,则需要在header + "." + data上计算HMAC,将结果编码为base64url,然后与签名字段进行比较

类似这样的东西:

private boolean validateSignature( String header, String data, String signature, String secretKey ) throws Exception {

    SecretKeySpec secret = new SecretKeySpec( secretKey.getBytes(), "HmacSHA256" );
    Mac mac = Mac.getInstance( "HmacSHA256" );
    mac.init( secret );

    String body = header + "." + data;
    byte[] hmacDataBytes = mac.doFinal( body.getBytes( StandardCharsets.UTF_8.name()) );
    String hmacData = Base64.getUrlEncoder().encodeToString( hmacDataBytes );

    return hmacData.equals( signature ); // Compare signatures here...
}

答案 1 :(得分:2)

要验证签名,您需要获取Base64Url编码的标头和有效负载,使用秘密计算HMACSha256哈希,Base64Url对结果进行编码并将其与原始签名进行比较。当然,您需要HMACSha256算法和Base64#Url编码的库,但不需要特定的JWT库。

用伪代码编写以显示原理:

hash = HmacSHA256(header + "." + payload , secret)

这里header是Base64Url编码的标头,payload是Base64Url编码的有效载荷

result = Base64UrlEncode(hash)

result现在可以与原始签名进行比较。

在另一本answer中,我使用node.js和在线工具描述了该过程。