George Huang
George Huang

Reputation: 2523

Verify JWT token signed with RS256 and get the "dynamic" public key from Azure

I am using this great method, found in this article https://codingstill.com/2016/01/verify-jwt-token-signed-with-rs256-using-the-public-key/#comment-3232, to validate my Azure idToken, which is signed with RS256.

I noticed that Azure AD changes the public key in a period of time. By reading this article, https://nicksnettravels.builttoroam.com/post-2017-01-24-verifying-azure-active-directory-jwt-tokens-aspx. Therefore, I used HttpClient (c#) to obtain the public key from the URL below, every time when I need to validate the token.

https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}

I did get the result of the public key, which is a string array in x5c key. Below is my code:

public static string GetPublicKeyAsync(string kid)
        {
            using (var client = new HttpClient())
            {
                HttpResponseMessage response = client.GetAsync("https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}").Result;

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = response.Content;

                    // by calling .Result you are synchronously reading the result
                    string responseBody = responseContent.ReadAsStringAsync().Result;

                    JObject json = JObject.Parse(responseBody);
                    string c = json["keys"].ToString();

                    List<ADPublic> allPublicKeys = JsonConvert.DeserializeObject<List<ADPublic>>(json["keys"].ToString());

                    foreach (ADPublic key in allPublicKeys)
                    {
                        if (key.kid == kid)
                        {
                            string certificateString = key.x5c[0];
                            var certificate = new X509Certificate2(Convert.FromBase64String(certificateString));

                            string pkey = Convert.ToBase64String(certificate.PublicKey.EncodedKeyValue.RawData);

                            var x509SecurityKey = new X509SecurityKey(certificate)
                            {
                                KeyId = key.kid
                            };
                            return pkey;
                        }
                    }

                }
                return null;
            }
            
    
        }

When I pass my public key over to the validation method. I always get an error at the line (PublicKeyFactory.CreateKey(keyBytes) method ):

Please refer to the verify method I mentioned in the beginning of this question for the code below:

// call my get public key function...

 string key = GetPublicKeyAsync(headerData["kid"].ToString());

var keyBytes = Convert.FromBase64String(key); // your key here
 
        **AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);**

Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger Parameter name: obj

I think I am almost there, but missing the last part, could you please help? Thank you very much!

Upvotes: 1

Views: 1578

Answers (1)

unknown
unknown

Reputation: 7473

I got the same error when using certificate.PublicKey.EncodedKeyValue.RawData to get the public key. I referred to this issue and finally succeeded.

The code shows getting AsymmetricKeyParameter from x5c string. Decode(...) function is referred to in the article.

string token = "<the token that you want to verify>";
string certificateString = "<the first x5c of the first key from https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}>";

byte[] buffer = Convert.FromBase64String(certificateString);
X509CertificateParser parser = new X509CertificateParser();
var _certificate = parser.ReadCertificate(buffer);
AsymmetricKeyParameter publicKey = _certificate.GetPublicKey();

string result = Decode(token, publicKey);
Console.WriteLine("result: " + result);

enter image description here

Upvotes: 0

Related Questions