azure-activedirectory-identitymodel-extensions-for-dotnet: JWT Signature validation fails in .NET Core app targetting .NET Framework 4.7.1

From @fabiodaniele on February 13, 2018 15:55

Hi, I was having an issue trying to authenticate users to a .NET Core WebAPI using a JWT bearer token generated by a WSO2 Identity Server.

The project targets .NET Framework 4.7.1 and references Microsoft.AspNetCore.Authentication.JwtBearerToken.

At first, I thought it was an issue related to my WSO2 IS configuration.

Then, I found this article: https://www.jerriepelser.com/blog/manually-validating-rs256-jwt-dotnet/ and tried to execute the same code found there in a new .NET Core Console app: it worked!

So, I thought: “it maybe an issue of my WebAPI project”.

I then made a few tries with two different brand new .NET Core WebAPI projects, one targetting .NET Core 2.0 and the other one targetting .NET Framework 4.7.1, using the same startup code in both.

Here is the code:

      // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            IConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{Configuration["OpenId:Authority"]}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
            OpenIdConnectConfiguration openIdConfig = configurationManager.GetConfigurationAsync(CancellationToken.None).Result;

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.IncludeErrorDetails = true;
                    options.TokenValidationParameters.ValidateIssuer = true;
                    options.TokenValidationParameters.ValidateAudience = true;
                    options.TokenValidationParameters.ValidateIssuerSigningKey = true;
                    options.TokenValidationParameters.ValidIssuer = Configuration["OpenId:Issuer"];
                    options.TokenValidationParameters.ValidAudiences = new[] { Configuration["OpenId:Audience"] };
                    options.TokenValidationParameters.IssuerSigningKeys = openIdConfig.SigningKeys;
                });

            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthentication();

            app.UseMvc();
        }

I decorated the default ValuesController with the Authorize attribute and tried to invoke it via Postman, with a new JWT token obtained from the WSO2 IS.

The results are different:

  • the app targetting .NET Core 2.0 simply works, giving me the expected JSON result from the Action invoked
  • the one targetting .NET Framework 4.7.1 replies with Bearer error=“invalid_token”, error_description=“The signature is invalid”

So the question is: is this the expected behavior or is it a bug?

Thanks in advance.

Copied from original issue: aspnet/Security#1649

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 1
  • Comments: 48 (20 by maintainers)

Most upvoted comments

@brentschmaltz I’m being faced with similar issue that’s been reported in here. I have tried all the suggestions but I’m getting no where.

Am I missing something?

I have added a snapshot of my code

IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey

I’m using MSAL interceptor in Angular 8 code to send access token to the server “C# .NET core 3.1”.

//***************************************************************************************************** public static JwtSecurityToken Validate(string token) {

        string stsDiscoveryEndpoint = "https://login.microsoftonline.com/eddf9e42-9a7f-4639-8042-***********/.well-known/openid-configuration?appid=7391aebb-1a35-4ad7-8166-xxxxxxxxxx"; // https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
        
        ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
        OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;


        TokenValidationParameters validationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidIssuer = config.Issuer,
            IssuerSigningKeys = config.SigningKeys,
            ValidateIssuerSigningKey = false,
            ValidateIssuer = true,
            ValidateActor = false,
            ValidateLifetime = true
        };

        IdentityModelEventSource.ShowPII = true;
        JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();

        SecurityToken jwt;
        var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
        return jwt as JwtSecurityToken;
    }

/* IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: ‘HlC0R12skxNZ1WQwmjOF_6t_tDE’, InternalId: ‘91ea3222-c9ea-4aa4-a515-83f2b195f989’. , KeyId: HlC0R12skxNZ1WQwmjOF_6t_tDE '. kid: ‘HlC0R12skxNZ1WQwmjOF_6t_tDE’. Exceptions caught: ‘’. token: ‘{“alg”:“RS256”,“typ”:“JWT”,“nonce”:“19yszC4xPiqhnbI587HT_08QFjftE7J3qE8F9xw_sqY”,“x5t”:“HlC0R12skxNZ1WQwmjOF_6t_tDE”,“kid”:“HlC0R12skxNZ1WQwmjOF_6t_tDE”}.{“aud”:“00000003-0000-0000-c000-000000000000”,“iss”:“https://sts.windows.net/eddf9e42-9a7f-4639-8042-110281cf41a2/",“iat”:1581413726,“nbf”:1581413726,“exp”:1581417626,“acct”:0,“acr”:“1”,“aio”:“42NgYLgWut8sY6dad5urN6/nxuP8P7Z6ZXr1Wwl355xibZu8nxMA”,“amr”:[“pwd”],“app_displayname”:“SoraxWeb”,“appid”:“7391aebb-1a35-4ad7-8166-xxxxxxxx”,“appidacr”:“0”,“family_name”:“kwofie”,“given_name”:“Ernest”,“ipaddr”:“212.161.81.38”,“name”:"Exxxx kxxxx”,“oid”:“9f9efce3-37e5-4018-8035-7ffc6323e827”,“onprem_sid”:“S-1-5-21-3789744388-407605227-1228871550-7770”,“platf”:“3”,“puid”:“100320003319DDA9”,“scp”:“Mail.Send openid profile User.Read User.ReadWrite email”,“sub”:“LhkC5zL_zJm-MZBjJDYCz5Asjm7Nwg9tHUehKVSH40A”,“tid”:“eddf9e42-9a7f-4639-8042-*************”,“unique_name”:“Exxxx.kxxxx@xxxx.com”,“upn”:“Exxxx.kxxxx@xxxxx.com”,“uti”:“3RsEnJpEokmZb-Y3MwJzAA”,“ver”:“1.0”,“xms_st”:{“sub”:“zSJz0JM6iB6SRwxi1ZZm_1F__aYqHhEDtjb5qkege_o”},“xms_tcdt”:1443089411}’. */

Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddCors(options => options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); })); services.AddControllers(); }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseCors(MyAllowSpecificOrigins);
        app.UseHttpsRedirection();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

I am running into the same error [IDX10511: Signature validation failed.] when trying to validate a JWT AccessToken obtained from https://login.microsoftonline.com/common. If I obtain the AccessToken via ADAL.NET (Microsoft.IdenityModel.Clients.ActiveDirectory v3.9.30421) then it validates successfully. However, if I obtain the AccessToken via MSAL.NET (Microsoft.Identity.Client v2.6.2) then it fails with the IDX10511 exception.

In both cases, I am using System.IdentityModel.Tokens.Jwt v5.3.0 to validate. Also, in both cases, I am validating using signing keys obtained from https://login.microsoftonline.com/common/.well-known/openid-configuration (aka https://login.microsoftonline.com/common/discovery/keys) using Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.

Any ideas why the ADAL.NET-obtained token validates successfully but the MSAL.NET-obtained token fails signature validation?

[update] This post helped me resolve my issue. As explained in that post by @brentschmaltz, the AccessToken obtained via MSAL.NET has an audience of https://graph.windows.net. Graph access tokens contain a nonce and require special processing during validation. As @brentschmaltz noted, we should not really be trying to validate Graph access tokens anyway. In my use case, I do not need to access Graph; so instead of using the AccessToken (which will not validate), I switch to using the IdToken. The IdToken has an audience of my registered application’s clientId (not https://graph.windows.net) and it does validate as expected.

@brentschmaltz knowning the cause is a good starting point 😃

I don’t know if this explains why there are no problems with the same code in a Consoleapp targetting .NET Core 2.1 as opposed to a Consoleapp targetting .NET 4.7.2 (or lower), but I’m hoping this get’s fixed somehow.

In my ASP.NET Core web application targetting .NET 4.7.2 (and System.IdentityModel.Tokens.Jwt v5.2.0), I have to us a dirty workaround to get passed signature validation :

SignatureValidator = delegate (string token, TokenValidationParameters parameters)
{
	return new JwtSecurityToken(token);
}

but I’d like to get rid of it asap.

Can you give some advice for a better and more secure workaround?

@brentschmaltz & @Tratcher : I ended up here because I got the same problem as initially reported by @fabiodaniele and @allevyMS : ASP.NET Core web application targetting .NET 4.7.2 with the Microsoft.AspNetCore.Authentication.JwtBearer package that references System.IdentityModel.Tokens.Jwt v5.2.0.

I am implementing the OpenID Connect Authorization Code flow, but I’m stuck with the following error when validating the JWT signature:

IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.JsonWebKey , KeyId: <my kid>
'.
Exceptions caught:
 ''.
token: <decoded JWT>

I’m 100% sure the JWT is valid and I’m using the correct JsonWebKey. If I manually validate the signature using the PEM in jwt.io it shows “signature verified”.

Consoleapp targetting .NET Core 2.1 and using System.IdentityModel.Tokens.Jwt v5.3.0 to validate my JWT (JwtSecurityTokenHandler) using the same RSA key succeeds without any exception. Consoleapp targetting .NET 4.7.2 (or lower) and using System.IdentityModel.Tokens.Jwt v5.3.0 (or lower) to validate my JWT (JwtSecurityTokenHandler) using the same RSA key gives me the same IDX10503 exception.

I’m not using Dispose() or a using block as mentioned here: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/SecurityTokenInvalidSignatureException

Is there any way to get around this issue? It’s becoming a showstopper for the project I’m working on.

Thanks in advance!

@EkwofiePrax did you ever find a solution?

@RubenDelange at the top of this thread, you are setting the keys directly onto TokenValidationParameters. You can do the following:

var N = "ALq7LDOgZRirsfc308DI_hSwSRZsYpPhiD69WyA65s-wRpG276x5SoWeyMOZJMi4qz2CG6K51_mHov0ZFmdN3sXARQPOgvepwQ1hY2OPRVmWzitdAP3b_UDuJR_rBIsSXVjnaDOdF_y9vefmVWanDh3Aef5Dk_0TzPexMod_WbEgcnibIgg4aZMSUeFsAViYkYSfgXrF16nZm2A3QJTbSuFTGpr9VPVLdD7mRvBgNXhm117OTg5OIBzRUnLrKlcqIg41uPdFHxNCJI3ukfyw9hvoHb1qCdAO3I9L66ZXdXTjY6uhoqgP_OGo6ze3BpQtIcECInNU7qkNd8CJ_MMRqgE";
var modulus = Base64UrlEncoder.DecodeBytes(N);
if (modulus.Length == 257 && modulus[0] == 0)
{
    var newModulus = new byte[256];
    Array.Copy(modulus, 1, newModulus, 0, 256);
    N = Base64UrlEncoder.Encode(newModulus);
}

var jsonWebKey = new JsonWebKey
{
	KeyId = "xneUh4tPkUPsVH/okK+VMrXirVA=",
    Kid = "xneUh4tPkUPsVH/okK+VMrXirVA=",
    N = N,
    Alg = "RS256",
    E = "AQAB",
    Kty = "RSA",
    Use = "sig"
};

You could also inform the token issuer, they shouldn’t have a 0 byte prefix. They may run into issues with other stacks.

Of course you will need to run through the keys in: openIdConfig.SigningKeys

@brentschmaltz : code I’m using for both console apps:

static void Main(string[] args)
{
	var publicJwk = new JsonWebKey
	{
		KeyId = "xneUh4tPkUPsVH/okK+VMrXirVA=",
		Kid = "xneUh4tPkUPsVH/okK+VMrXirVA=",
		N = "ALq7LDOgZRirsfc308DI_hSwSRZsYpPhiD69WyA65s-wRpG276x5SoWeyMOZJMi4qz2CG6K51_mHov0ZFmdN3sXARQPOgvepwQ1hY2OPRVmWzitdAP3b_UDuJR_rBIsSXVjnaDOdF_y9vefmVWanDh3Aef5Dk_0TzPexMod_WbEgcnibIgg4aZMSUeFsAViYkYSfgXrF16nZm2A3QJTbSuFTGpr9VPVLdD7mRvBgNXhm117OTg5OIBzRUnLrKlcqIg41uPdFHxNCJI3ukfyw9hvoHb1qCdAO3I9L66ZXdXTjY6uhoqgP_OGo6ze3BpQtIcECInNU7qkNd8CJ_MMRqgE",
		Alg = "RS256",
		E = "AQAB",
		Kty = "RSA",
		Use = "sig"
	};

	// JWT to test
	const string testToken = "<JWT>";

	try
	{
		var user = ValidateAndDecode(testToken, publicJwk);

		Console.WriteLine($"Token is validated. User Id {user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value}");
	}
	catch (Exception e)
	{
		Console.WriteLine($"Error occurred while validating token: {e.Message}");
	}

	Console.WriteLine();
	Console.WriteLine("Press ENTER to continue...");
	Console.ReadLine();
}

private static ClaimsPrincipal ValidateAndDecode(string jwt, SecurityKey signingKey)
{
	var validationParameters = new TokenValidationParameters
	{
		IssuerSigningKey = signingKey,
		// We use an expired token for testing purposes => ignore expiration
		RequireExpirationTime = false,
		ValidateLifetime = false,
		//validating audience and issuer not relevant for this test
		ValidateAudience = false,
		ValidateIssuer = false
	};

	try
	{
		var claimsPrincipal = new JwtSecurityTokenHandler().ValidateToken(jwt, validationParameters, out var rawValidatedToken);

		//return (JwtSecurityToken)rawValidatedToken;
		return claimsPrincipal;
	}
	catch (SecurityTokenValidationException stvex)
	{
		// The token failed validation!
		throw new Exception($"Token failed validation: {stvex.Message}");
	}
	catch (ArgumentException argex)
	{
		// The token was not well-formed or was invalid for some other reason.
		throw new Exception($"Token was invalid: {argex.Message}");
	}
}

@cvocvo currently we only release on nuget.org. Yea, 5.2.5 should have a 4.6.1 or 4.6.0 target.

@Tratcher @manish13oct @cvocvo with this commit, this should now work. 5.2.3 will have this fix.

In 5.2.4 we plan on introducing a 4.6.0 or 4.6.1 target, that will solve the issue for applications that can use that target. As we plan on supporting 4.5.1 and 4.5.2 for a while, we need this fix as it has nice SHA2 support. We plan on extending this adapter to use CNG for certs and ecdsa.