javajwtnimbus-jose-jwt

How to implement a shared secret version of NimbusJwtDecoder and NimbusJwtEncoder


I’m trying to implement a solution with the encoder/decoder from the org.springframework.security.oauth2.jwt package with a shared secret. But my attempt fails when I try to encode a token with a JwtEncodingException. I have asked this question in another form, but here I include a simple ready to execute example, to verify the problem.

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.oauth2.jwt.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class EncoderDecoderTest {
  public static String secret = "j8IoV1jF67";

  public JwtEncoder jwtEncoder() throws JOSEException {
    SecretKey originalKey = new SecretKeySpec(secret.getBytes(), "AES");
    JWKSource<SecurityContext> immutableSecret = new ImmutableSecret<SecurityContext>(originalKey);
    return new NimbusJwtEncoder(immutableSecret);
  }
  public JwtDecoder jwtDecoder() {
    SecretKey originalKey = new SecretKeySpec(secret.getBytes(), "AES");
    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(originalKey).build();
    return jwtDecoder;
  }

  public void tester() throws JOSEException {
    JwtClaimsSet claims = JwtClaimsSet.builder()
            .issuer("self")  //Only this for simplicity
            .build();
    var encoder = jwtEncoder();
    //This line throws the exception
    String token = encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
    System.out.println(token);

    var decoder = jwtDecoder();
    Jwt jwt = decoder.decode(token);
    System.out.println(jwt.getIssuer());
  }

  public static void main(String[] args) throws JOSEException {
    new EncoderDecoderTest().tester();
  }
}

jps has come up with a an explanation to why this doesn't work (AES is not a valid signature algorithm), thanks :-) I guess my question could then be rephrased into: how do I implement a shared secret version of these two encoders/decoders, for example with HMAC using SHA-256?

org.springframework.security.oauth2.jwt.NimbusJwtDecoder
org.springframework.security.oauth2.jwt.NimbusJwtEncoder

I tried (a lot) but have not yet succeeded ;-( Any suggestions for how to fix this problem?


Solution

  • With the input from jps (AES is actually OK for encrypted tokens, but not signed) and Github Copilot I came up with a working solution using HMAC-SHA256:

    //Don't hardcode like this for real
      public static String secret = "s/4KMb61LOrMYYAn4rfaQYSgr+le5SMrsMzKw8G6bXc=";
    
      public JwtEncoder jwtEncoder() throws JOSEException {
        SecretKey key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        JWKSource<SecurityContext> immutableSecret = new ImmutableSecret<SecurityContext>(key);
        return new NimbusJwtEncoder(immutableSecret);
      }
    
      public JwtDecoder jwtDecoder() {
        SecretKey originalKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(originalKey).build();
        return jwtDecoder;
      }
    
      public void tester() throws JOSEException {
        //Create the TOKEN
        JwtClaimsSet claims = JwtClaimsSet.builder()
                .issuer("https://somewhere.com")  //Only this for simplicity
                .build();
        var encoder = jwtEncoder();
        JwsHeader jwsHeader = JwsHeader.with(() -> "HS256").build();
        String token = encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
        //Decode the TOKEN
        var decoder = jwtDecoder();
        Jwt jwt = decoder.decode(token);
        System.out.println(jwt.getIssuer());
      }