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:
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.