简体   繁体   中英

Creating jwks endpoint spring boot

I am building a two service app in Java Spring Boot where one service is an auth service that generates a jwt and the other one is a resource service, that decodes the jwt and returns list of posts based on the jwt subject.

My current auth service setup has a .jks key pair stored in the resources and a login endpoint that:

  • checks that the username exists in MongoDB and that the password matches the stored encoded one
  • if the check passes, returns a jwt with the username in subject

My resource server has a method that decodes the jwt at the preHandle() stage and looks like this:

@Value("${jwt.key}")
private String publicKey;

public Claims decodeJWT(String jwt) {

    KeyFactory kf;
    PublicKey key;

    publicKey = publicKey
            .replace("-----BEGIN PUBLIC KEY-----", "")
            .replace("-----END PUBLIC KEY-----", "")
            .replace("\n", "")
            .trim();

    try{
        kf = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pubKeySpecX509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
        key = kf.generatePublic(pubKeySpecX509EncodedKeySpec);
    } catch (Exception e) {
        throw new RuntimeException("Failed to generate Public Key", e);
    }

    //This line will throw an exception if it is not a signed JWS (as expected)
    return Jwts.parser()
            .requireIssuer("auth-service")
            .requireAudience("posts-service")
            .setSigningKey(key)
            .parseClaimsJws(jwt).getBody();
}

My only issue with this is that I need to manually export the public key from the auth service .jks file and store it in the resource service application.yml file.

My question therefore is: What would be the best way to create a jwks endpoint in the auth service, that returns the public key from the .jks file? and how to best add a call to it from the resource service?

Cheers Kris

Figured it out.

Creating JWKS in the auth service:

    public Map<String, List<Map<String, Object>>> getPublicKeys(){

    RSAPublicKey rsa;

    try{
        is = JwtBuilder.class.getResourceAsStream(("/" + signingKeyStore));
        keyStore = KeyStore.getInstance("JKS");
        keyStore.load(is, signingKeyStorePassword.toCharArray());
        rsa = (RSAPublicKey) keyStore.getCertificate(signingKeyId).getPublicKey();
    } catch (Exception e) {
        throw new RuntimeException("Failed to retrieve public key", e);
    }

    Map<String, Object> values = new HashMap<>();

    values.put("kty", rsa.getAlgorithm()); // getAlgorithm() returns kty not algorithm
    values.put("kid", signingKeyId);
    values.put("n", Base64.getUrlEncoder().encodeToString(rsa.getModulus().toByteArray()));
    values.put("e", Base64.getUrlEncoder().encodeToString(rsa.getPublicExponent().toByteArray()));
    values.put("alg", "RS256");
    values.put("use", "sig");

    List<Map<String, Object>> keyList = new ArrayList<>();

    keyList.add(values);

    Map<String, List<Map<String, Object>>> keys = new HashMap<>();

    keys.put("keys", keyList);

    return keys;
}

Decoding and validating JWT in the resource service:

public DecodedJWT decodeAndVerifyJWT(String token){

    DecodedJWT jwt;

    try{
        jwt = JWT.decode(token);
        JwkProvider provider = new UrlJwkProvider("http://localhost:8090");
        Jwk jwk = provider.get(jwt.getKeyId());
        Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
        algorithm.verify(jwt);
    } catch (InvalidPublicKeyException e) {
        throw new RuntimeException("Invalid Public key", e);
    } catch (JwkException e) {
        throw new RuntimeException("Couldn't verify JWT", e);
    }

    return jwt;
}

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