Add x5c and verify public key against x5c when extracting a public key from a JSON Web Key
robotdan opened this issue · 6 comments
Issue
Inbound thread from Nightwatch Cybersecurity Research
Nightwatch Cybersecurity Research <research@....>
It looks like the "x5c" field is not being used, while the RFC
dictates that "x5c" needs to verify the raw key materials:
https://tools.ietf.org/html/rfc7517#section-4.7
Nightwatch Cybersecurity Research <research@....>
Hi,If the JWKS endpoint is compromised or the JWKS data is modified while
in transit by an MITM attacker (when the URL is non-HTTPS). Such an
attacker can choose to modify the public key information only and
leave the certificate alone, so an application verifying the
certificate will miss the fact that the public key doesn't match.There is some discussion on the IETF list ot this affect:
https://mailarchive.ietf.org/arch/msg/jose/f_jo8sfQN6TqzkpmzgzzMb3_kEw/Thanks
Solution
Add the x5c to the JSON Web Key when parsing a certificate, and when extracting a public key from a JSON Web key, attempt to verify the public key against the x5c if possible.
Solution notes
From what I can gather, a consumer of the JWKs may just use the certificate directly if available in the x5c parameter and not trust or parse the public key components.
However, if we also verify the response has not been modified by verifying the key components such as e
, and n
for RSA keys, or the x
and y
coordinates for an EC key, we should be able to detect when the public key represented by the JSON Web key does not match the x5c
parameter.
With all that said, it seems to me if you are concerned about the thread of MITM when consuming JWKS, you should only accept JWKS from a TLS endpoint which has HSTS enabled. Or, only accept a JWK if it is published with the x5c and ignore the public key components in the JWK response.
If you were to be accepting a JSON Web Key from an endpoint that had been compromised by a MITM, it would seem to me the easier way to mess with someone is to remove the x5c
parameter AND modify the public key components so it could not be verified. I do not know why you'd modify the public key components and NOT remove the x5c
.
In any case, it doesn't hurt to perform some additional validation against the JSON Web Key to ensure the public key components match up with the certificate if found in the x5c
parameter if we think it may help security some fashion.
Regarding the x5c material itself - it seems at the minimum, it would be nice to provide a feature to validate the certificate against the CAs known by the JVM
I can take a look at adding that capability.
Release 3.6.0 w/ validation for x5c. I'll keep this open to still look at adding certificate verification options.
Hello @robotdan
Came across this discussion while searching on the internet for the issue I am getting while trying to verify my Id Token.
I think you can help me with the issue so posting it here, I am sorry if this is not the correct place to ask about issues, I tried posting it on forums and stackoverflow but no help so far.
I am using fusionauth-jwt library to verify ID token signed by RSA SHA 256 key.
In my code below ,first trying to get public key using JWKS json and then trying to create a Verifier instance so that I can verify my id token.
List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromJWKS("http://localhost:9011/.well-known/jwks.json");
Map<String, Verifier> publicKeyVerifiers = new HashMap<String,Verifier>();
for (JSONWebKey key : keys) {
String publicKey = key.x5c.get(0); //getting x5c element
Verifier verifier = RSAVerifier.newVerifier(publicKey); // Creating RSA verifier instance where getting issue
String kid = key.kid;
publicKeyVerifiers.put(kid, verifier);
}
JWT jwtDecoded = JWT.getDecoder().decode(idToken, publicKeyVerifiers);
Getting issue at the time of creating verifier instance because the x5c element contains Base64Encoded value and not the .pem format value which begins with "-----BEGIN"
Class "io.fusionauth.pem.PEMDecoder.java" expects "-----BEGIN" which is not present in key.x5c and hence throwing exception "throw new PEMDecoderException(new InvalidParameterException("Unexpected PEM Format"));
Screenshot of PEMDecoder.java
Please could you suggest how to fix it.
Thanks.
@GokulMahajan20 - it looks like we are expect the key to be PEM encoded. The x5c
is not PEM encoded, it is just a base64
encoded version of the key. (mostly the same as PEM except no headers, and is not MIME encoded)
Could you just pass in the byte array instead?
String publicKey = key.x5c.get(0);
byte[] decoded = Base64.getDecoder().decode(publicKey);
Verifier verifier = RSAVerifier.newVerifier(decoded); // Creating RSA verifier instance where getting issue
Would that work for you?