简体   繁体   中英

Verification of signed JWT

I am signing JWT with private key (authorization server) and I am using public key (resource server) to "verify" it... How can I know whether the JWT has not been compromised? Or how can I do that?

The code is from resource server

       JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.txt");
        String publicKey = null;
        try {
            publicKey = IOUtils.toString(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);

        return converter;
    }

Spring Security will do the verification of the token based on configurations in authorization server.

For a standalone verification, the code would be like:

  RsaVerifier verifier = new RsaVerifier(RSAPublicKey);
  Jwt tokenDecoded = JwtHelper.decodeAndVerify(token, verifier);
  Map<String, Object> claimsMap = (Map<String, Object>) new 
  ObjectMapper().readValue(tokenDecoded.getClaims(), Map.class);
  //Verify the claims then
  // 1 Verify if the token has not already expired
  // 2 Verify the issuance date ( should be before this date )
  // 3 Verify if the issuer of this token is contained in verified authorities.
  // 4 Verify if the token was issued for this client
  // 5 Verify if the token contained any expected claims...

But the above is implemented by Spring Security for Oauth2 authentication process, client application just needs to provide configurations.

The trigger is OAuth2AuthenticationProcessingFilter in the Spring security filter chain. This filter is added when resources are protected by Oauth2 security.

In your application, the authorization server configuration would look like ( only relevant indicative configuration extracts below)

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

...
 @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        RSAPemKeyPairLoader keyPairLoader = new RSAPemKeyPairLoader();
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(...);
        converter.setVerifierKey(...);
        return converter;
    }

 @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(...);
        defaultTokenServices.setAccessTokenValiditySeconds(...);
        defaultTokenServices.setRefreshTokenValiditySeconds(...);
        return defaultTokenServices;
    }
}

In your application, the Resource Server configuration would be like:

@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
  public void configure(HttpSecurity http) throws Exception {
   ...
  }
}

To trace in Spring implementation where the requested token is intercepted and verified look at the Spring OAUTH2 implementation - flow details below, where Authentication object, an instance of OAuth2Authentication would be attempted to be created for successful requests.

All below Code extracts are from spring-security-oauth2-2.0.8.RELEASE implementations.

public class OAuth2AuthenticationManager implements AuthenticationManager {

                ....
                public Authentication authenticate(Authentication authentication) throws AuthenticationException {

                        if (authentication == null) {
                            throw new InvalidTokenException("Invalid token (token not found)");
                        }
                        String token = (String) authentication.getPrincipal();
                        OAuth2Authentication auth = tokenServices.loadAuthentication(token);
                        ...
         }
}

The loadAuthentication would be basically verifying the access token and attempting to convert it into OAuth2Authentication

public class DefaultTokenServices implements AuthorizationServerTokenServices ...{

           public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException {

                   OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
                    ...
            }
}

JwtTokenStore would create OAuth2AccessToken and in the process decode and verify the String token.

    public class JwtTokenStore implements TokenStore {

        public OAuth2AccessToken readAccessToken(String tokenValue) {
                OAuth2AccessToken accessToken = convertAccessToken(tokenValue);
                if (jwtTokenEnhancer.isRefreshToken(accessToken)) {
                    throw new InvalidTokenException("Encoded token is a refresh token");
                }
                return accessToken;
            }

        private OAuth2AccessToken convertAccessToken(String tokenValue) {
                return jwtTokenEnhancer.extractAccessToken(tokenValue, jwtTokenEnhancer.decode(tokenValue));
            }

  }

JWTAccessTokenConverter does the decoding and extraction of token claims.

public class JwtAccessTokenConverter implements AccessTokenConverter {

   protected Map<String, Object> decode(String token) {
                    try {
                        Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
                        String content = jwt.getClaims();
                        Map<String, Object> map = objectMapper.parseMap(content);
                        if (map.containsKey(EXP) && map.get(EXP) instanceof Integer) {
                            Integer intValue = (Integer) map.get(EXP);
                            map.put(EXP, new Long(intValue));
                        }
                        return map;
                    }
                    catch (Exception e) {
                        throw new InvalidTokenException("Cannot convert access token to JSON", e);
                    }
}

JwtHelper would do the decoding and request verification.

public static Jwt decodeAndVerify(String token, SignatureVerifier verifier) {
     Jwt jwt = decode(token);
     jwt.verifySignature(verifier);
     return jwt;
}

JwttImpl invokes the verifier.

public void verifySignature(SignatureVerifier verifier) {
    verifier.verify(signingInput(), crypto);
}

For example, RSA Signature verifier would finally do the verification:

public class RsaVerifier implements SignatureVerifier {

            public void verify(byte[] content, byte[] sig) {
                    try {
                        Signature signature = Signature.getInstance(algorithm);
                        signature.initVerify(key);
                        signature.update(content);

                        if (!signature.verify(sig)) {
                            throw new InvalidSignatureException("RSA Signature did not match content");
                        }
                    }
                    catch (GeneralSecurityException e) {
                        throw new RuntimeException(e);
                    }
                }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM