fusionauth-jwt icon indicating copy to clipboard operation
fusionauth-jwt copied to clipboard

Add x5c and verify public key against x5c when extracting a public key from a JSON Web Key

Open robotdan opened this issue 3 years ago • 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.

robotdan avatar Oct 15 '20 14:10 robotdan

Commits:

https://github.com/FusionAuth/fusionauth-jwt/commit/fd761c642f28fb95e384ac2a6e4bcba23ba89d9c https://github.com/FusionAuth/fusionauth-jwt/commit/4465240de20951a59d8374a8f49460ba6343d731 https://github.com/FusionAuth/fusionauth-jwt/commit/ec26cb764127317a139c9a3638a0d4c19ad0a7b8

robotdan avatar Oct 15 '20 14:10 robotdan

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

nightwatchcyber avatar Oct 18 '20 01:10 nightwatchcyber

I can take a look at adding that capability.

robotdan avatar Oct 19 '20 16:10 robotdan

Release 3.6.0 w/ validation for x5c. I'll keep this open to still look at adding certificate verification options.

robotdan avatar Nov 06 '20 16:11 robotdan

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

image

Please could you suggest how to fix it. Thanks.

GokulMahajan20 avatar Feb 16 '22 12:02 GokulMahajan20

@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?

robotdan avatar May 27 '22 17:05 robotdan