Keycloak 21.0.0: Public Key Not Found for Signature Verification Using External JWK API

In Keycloak 21.0.0, an issue occurs during signature verification when consuming an external API that provides JSON Web Keys (JWKs). The system fails to find the expected public key, logging the following warning and error messages:

WARN [org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProvider] PublicKey wasn’t found in the storage. Requested kid: ‘sig-2024-10-07-DB’. Available kids: ‘’ WARN [org.keycloak.services] KC-SERVICES0097: Invalid request: java.lang.RuntimeException: Failed to verify signature on ‘request’ object

@Override
public void authenticate(AuthenticationFlowContext context) {
JWKSet euroInfoJwkSet = fetchJWKSet();
boolean isValid = verifySignature(decryptedJwt, euroInfoJwkSet);
if (!isValid) {
context.failure(GENERIC_AUTHENTICATION_ERROR);
return;
}
}

private JWKSet fetchJWKSet() throws Exception {
URL url = new URL(“https://request/jwk_request.cgi?client_id=5g4h4r8r”);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(“GET”);
conn.setRequestProperty(“Accept”, “application/json”);

if (conn.getResponseCode() != 200) {
    throw new RuntimeException("Failed to fetch JWKs: HTTP error code " + conn.getResponseCode());
}

BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String inputLine;

while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();

Gson gson = new Gson();
return JWKSet.parse(response.toString());

}

private boolean verifySignature(String jwt, JWKSet euroInfoJwkSet) throws Exception {
String jwtParts = jwt.split(“\.”);
if (jwtParts.length != 3) {
throw new IllegalArgumentException(“Invalid JWT format”);
}

byte[] signatureBytes = Base64.getUrlDecoder().decode(jwtParts[2]);

JWK jwk = euroInfoJwkSet.getKeyByKeyId(jwtParts[0]);

PublicKey publicKey = ((RSAKey) jwk).toPublicKey();

Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update((jwtParts[0] + "." + jwtParts[1]).getBytes());
return sig.verify(signatureBytes);

}

I resolved this issue by adjusting the client authentication flow in Keycloak. Specifically, I changed the client to confidential mode. Once this was set, a new tab appeared under the client’s configuration where I could input the JWK URL.

Keycloak then handles the entire authentication process when consuming the /auth path. It automatically retrieves the JWK from the provided URL, verifies the signature, decrypts the message, and stores the result in the session, resolving the public key verification issue.