Search code examples
c#jwtecdsajwk

C# Validate JWT token with values from an authorize end point (crv, x, y)


I have a web page being displayed on a vendors site in an iframe. They call my page and pass a JWT in the query string. I have to parse the jwt.

eyJraWQiOiIyMDIwLTA5LTAyVDE3OjM2OjE3LjU3MC5lYyIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzdWIiOiJ1cm46Y2VybmVyOmlkZW50aXR5LWZlZGVyYXRpb246cmVhbG06SFdPb0lsUlgyWWRGZjkyNGJBZTZSR0l5WmtuajZrTjctY2g6cHJpbmNpcGFsOnRhNDh6OWdkNTVkNndyNW0iLCJhdWQiOiJodHRwczpcL1wvdXJsMjU4dmowai5leGVjdXRlLWFwaS51cy1lYXN0LTIuYW1hem9uYXdzLmNvbSIsImlzcyI6Imh0dHBzOlwvXC9kZXYuYmF5Y2FyZS5wYXRpZW50cG9ydGFsLnVzLTEuaGVhbHRoZWludGVudC5jb20iLCJleHAiOjE1OTkxNTQ1MTYsImlhdCI6MTU5OTE1MzkxNiwic2lkIjoiZGUwNmJhNmUtYjQyYy00ZmY5LWI4MmQtYmM4NjY0ODJmODU4In0.6Ru5Lyd1Zq016uv84pP-GjSuz6koVNipa_cd939eF21-5N2_A0Nj3I6AkDhuHrE870WzyTiCmZfkIjMOFZkRCA

I am suppose to verify the signature by pulling values from the https://authorizion.x.com/jwk. I get values that look like this:

{
   "keys":[
      {
         "kty":"EC",
         "crv":"P-256",
         "kid":"2020-09-04T18:16:04.934.ec",
         "x":"82WEbXbnfGC1kmMfjJch6gFJRp7hEp08gzZQdBLLFIk",
         "y":"ytkPwl4IjLw8M94DzgTmdAbxjq0AmmYu9mMmxpU3eBI"
      },
      {
         "kty":"EC",
         "crv":"P-256",
         "kid":"2020-09-02T17:36:17.570.ec",
         "x":"uAfEPKELRuUVMtB0DCB5oyYWnfiV8-9zHYntvI0lsRE",
         "y":"32J6nVgeb9RLdWK21QNDHhWdOsZJbxvyEq2n0IOvLtQ"
      },
      {
         "kty":"EC",
         "crv":"P-256",
         "kid":"2020-08-31T17:36:17.359.ec",
         "x":"HsxFY2vihycZgYnkSTLDHJ0Cagr2nUcZTbf2yQKPS6A",
         "y":"4kLClPGM0TG_gCUlBKkYdXrlLFVasPxQ2UOvwSBKyt0"
      }
   ]
}

I pick the key based on the kid value in the header of the JWT. How do I validate the signature based on the crv, x and y values? It seems like I need either a public or private certificate but I don't have that.

What more can I add. It is using ECDSA p-256 encryption. There is a Json Web Token above and at jwt.io I get the following values,

Header

{
  "kid": "2020-09-02T17:36:17.570.ec",
  "typ": "JWT",
  "alg": "ES256"
}

Payload

{
  "sub": "urn:cerner:identity-federation:realm:HWOoIlRX2YdFf924bAe6RGIyZknj6kN7-ch:principal:ta48z9gd55d6wr5m",
  "aud": "https://url258vj0j.execute-api.us-east-2.amazonaws.com",
  "iss": "https://dev.baycare.patientportal.us-1.healtheintent.com",
  "exp": 1599154516,
  "iat": 1599153916,
  "sid": "de06ba6e-b42c-4ff9-b82d-bc866482f858"
}

Signature

6Ru5Lyd1Zq016uv84pP-GjSuz6koVNipa_cd939eF21-5N2_A0Nj3I6AkDhuHrE870WzyTiCmZfkIjMOFZkRCA

I included the Java Web Key Set matching the JWT.


Solution

  • Here's a very short solution based on Jose.

    Basically you need to read the JWKS from the given URL, choose the right JWK with the matching key ID (kid), create a ECJWKey based on the given, Base64 encoded, x and y parameters and then validate the JWT with that key:

    The following source code is only a short illustration of the process and the key is hard coded and not read from the JWKS endpoint.

    using Jose;
    using System;
    using System.Security.Cryptography;
    
    namespace JWKValiadation
    {
        public class ECJWKey
        {
            public string kty { get; set; }
            public string crv { get; set; }
            public string kid { get; set; }
            public string x { get; set; }
            public string y { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                ECJWKey ecjwkkey = new ECJWKey
                {
                    kty = "EC",
                    crv = "P-256",
                    kid = "2020-09-02T17:36:17.570.ec",
                    x = "uAfEPKELRuUVMtB0DCB5oyYWnfiV8-9zHYntvI0lsRE",
                    y = "32J6nVgeb9RLdWK21QNDHhWdOsZJbxvyEq2n0IOvLtQ"
                };
    
                string tokenEC = "eyJraWQiOiIyMDIwLTA5LTAyVDE3OjM2OjE3LjU3MC5lYyIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzdWIiOiJ1cm46Y2VybmVyOmlkZW50aXR5LWZlZGVyYXRpb246cmVhbG06SFdPb0lsUlgyWWRGZjkyNGJBZTZSR0l5WmtuajZrTjctY2g6cHJpbmNpcGFsOnRhNDh6OWdkNTVkNndyNW0iLCJhdWQiOiJodHRwczpcL1wvdXJsMjU4dmowai5leGVjdXRlLWFwaS51cy1lYXN0LTIuYW1hem9uYXdzLmNvbSIsImlzcyI6Imh0dHBzOlwvXC9kZXYuYmF5Y2FyZS5wYXRpZW50cG9ydGFsLnVzLTEuaGVhbHRoZWludGVudC5jb20iLCJleHAiOjE1OTkxNTQ1MTYsImlhdCI6MTU5OTE1MzkxNiwic2lkIjoiZGUwNmJhNmUtYjQyYy00ZmY5LWI4MmQtYmM4NjY0ODJmODU4In0.6Ru5Lyd1Zq016uv84pP-GjSuz6koVNipa_cd939eF21-5N2_A0Nj3I6AkDhuHrE870WzyTiCmZfkIjMOFZkRCA";
    
                // first read the header to get the kid
                var headers = Jose.JWT.Headers(tokenEC);
                if(headers.TryGetValue("kid", out var keyId))
                {
                    // in a real application you would need the kid 
                    // to select the right key from the JKWS
                    Console.WriteLine(keyId);
                }
    
                // create the key based on the parameters from the JWK
                ECDsa eckey = ECDsa.Create(new ECParameters
                {
                    Curve = ECCurve.NamedCurves.nistP256,
                    Q = new ECPoint
                    {
                        X = Base64Url.Decode(ecjwkkey.x),
                        Y = Base64Url.Decode(ecjwkkey.y)
                    }
                });
                
                // verify and decode the token
                string payload = Jose.JWT.Decode(tokenEC, eckey);
                Console.WriteLine(payload);
            }
        }
    }