JwtAuthForWebAPI 中的 SignatureVerificationFailedException
SignatureVerificationFailedException in JwtAuthForWebAPI
我已经连接了 JwtAuthForWebAPI nuget 项目,但我无法验证生成的令牌。我最终收到 500 错误。我在令牌生成和配置 JwtAuthenticationMessageHandler 时都使用完全相同的键值。
这是生成令牌的代码:
var tokenHandler = new JwtSecurityTokenHandler();
var symmetricKey = JsonWebTokenSecretKey.GetBytes();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
new[]{
new Claim(JwtClaimKeys.Audience, SessionManager.Current.ApplicationId.ToString()),
new Claim(JwtClaimKeys.Subject, userLoginRequest.ApplicationInstanceId.ToString())
}),
TokenIssuerName = "My Company",
Lifetime = new Lifetime(now, now.AddMinutes(tokenLifetimeInMinutes)),
SigningCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(symmetricKey),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256")
};
tokenDescriptor.Subject.AddClaims(GetRoles(userLoginRequest));
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
这是注册身份验证处理程序的代码:
var keyBuilder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
Issuer = "My Company",
AllowedAudience = ApplicationId.ToString(),
SigningToken = keyBuilder.CreateFromKey(JsonWebTokenSecretKey),
PrincipalTransformer = new MyUserPrincipleTransformer()
};
config.MessageHandlers.Add(jwtHandler);
这是我得到的错误:
{"Message":"An error has occurred.","ExceptionMessage":"IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey\r\n'.\nExceptions caught:\n ''.\ntoken: '{\"typ\":\"JWT\",\"alg\":\"HS256\"}.{\"aud\":\"1\",\"sub\":\"3\",\"role\":[\"User\",\"Admin\"],\"iss\":\"My Company\",\"exp\":1429547369,\"nbf\":1429543769}'","ExceptionType":"System.IdentityModel.SignatureVerificationFailedException",
"StackTrace":"
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)\r\n
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)\r\n
at JwtAuthForWebAPI.JwtSecurityTokenHandlerAdapter.ValidateToken(IJwtSecurityToken securityToken, TokenValidationParameters validationParameters)\r\n
at JwtAuthForWebAPI.JwtAuthenticationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n
at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()"}
这是一个示例 JSON 令牌:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwic3ViIjoiMyIsInJvbGUiOlsiVXNlciIsIkFkbWluIl0sImlzcyI6Ik15IENvbXBhbnkiLCJleHAiOjE0Mjk1NTE4MjgsIm5iZiI6MTQyOTU0ODIyOH0.9wA_RBir9u7Cn_-Fy2T-Q_IDUfz6B928IEbIgXD9Bug
有趣的是,我能够使用 http://jwt.io 使用我的密钥验证令牌。我怀疑这可能与 JwtAuthForWebAPI 库有关,它查看的内容不同于 System.Identity JWT 库生成的内容?
我是 Jamie(JwtAuthForWebAPI 包的作者)。服务器配置代码 - 具体来说,SecurityTokenBuilder.CreateFromKey(string)
- 假设给定的字符串是 base64 编码的 。要么是这样,要么需要假设或参数来指示使用哪种编码来转换为字节数组。我选择假设该字符串是 base64 编码的。我确信有一种更清晰的方法可以将字符串密钥转换为 SecurityToken,但这就是今天代码的方式。
在 JwtAuthForWebAPI.SampleClient 项目中的 SmokeTests.cs 中,您可以看到我使用了 Convert.FromBase64String()
方法,而不是使用 [=15] 中的 GetBytes()
方法=] class:
public const string SymmetricKey = "YQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6ADAAMQAyADMANAA1AA==";
// ...
var key = Convert.FromBase64String(SymmetricKey);
var credentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
请继续使用您当前的令牌生成代码,但在服务器上...
请尝试在服务器配置代码中指定 JsonWebTokenSecretKey 的 base64 编码版本。您可以使用像 https://www.base64encode.org/ 这样的网站对其进行编码,或者尝试这样的代码:
var base64key = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonWebTokenSecretKey));
var keyBuilder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
Issuer = "My Company",
AllowedAudience = ApplicationId.ToString(),
SigningToken = keyBuilder.CreateFromKey(base64key),
PrincipalTransformer = new MyUserPrincipleTransformer()
};
让我知道这是否有效。
此外,我将更新库以捕获 SignatureVerificationFailedException 异常和 return 401,而不是让内部服务器错误发生。您仍然需要将密钥指定为 base64 字符串,但至少此类配置问题不会导致 500 错误。
再一次,请告诉我这是否有效。
这只是我基于@Jamie 回答的代码示例
protected string GetUsername(string token)
{
string secret = "keyyyyy!@3";
var key = Convert.FromBase64String(secret);
var IssuerSigningKey = new SymmetricSecurityKey(key);
IdentityModelEventSource.ShowPII = true;
var SigningCredentials = new SigningCredentials(
IssuerSigningKey,
SecurityAlgorithms.HmacSha256Signature);
var handler = new JwtSecurityTokenHandler();
var tokenSecure = handler.ReadToken(token) as SecurityToken;
var validations = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = IssuerSigningKey,
ValidateIssuer = false,
ValidateAudience = false
};
var claims = handler.ValidateToken(token, validations, out tokenSecure);
return claims.Identity.Name;
}
我已经连接了 JwtAuthForWebAPI nuget 项目,但我无法验证生成的令牌。我最终收到 500 错误。我在令牌生成和配置 JwtAuthenticationMessageHandler 时都使用完全相同的键值。
这是生成令牌的代码:
var tokenHandler = new JwtSecurityTokenHandler();
var symmetricKey = JsonWebTokenSecretKey.GetBytes();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
new[]{
new Claim(JwtClaimKeys.Audience, SessionManager.Current.ApplicationId.ToString()),
new Claim(JwtClaimKeys.Subject, userLoginRequest.ApplicationInstanceId.ToString())
}),
TokenIssuerName = "My Company",
Lifetime = new Lifetime(now, now.AddMinutes(tokenLifetimeInMinutes)),
SigningCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(symmetricKey),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256")
};
tokenDescriptor.Subject.AddClaims(GetRoles(userLoginRequest));
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
这是注册身份验证处理程序的代码:
var keyBuilder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
Issuer = "My Company",
AllowedAudience = ApplicationId.ToString(),
SigningToken = keyBuilder.CreateFromKey(JsonWebTokenSecretKey),
PrincipalTransformer = new MyUserPrincipleTransformer()
};
config.MessageHandlers.Add(jwtHandler);
这是我得到的错误:
{"Message":"An error has occurred.","ExceptionMessage":"IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.InMemorySymmetricSecurityKey\r\n'.\nExceptions caught:\n ''.\ntoken: '{\"typ\":\"JWT\",\"alg\":\"HS256\"}.{\"aud\":\"1\",\"sub\":\"3\",\"role\":[\"User\",\"Admin\"],\"iss\":\"My Company\",\"exp\":1429547369,\"nbf\":1429543769}'","ExceptionType":"System.IdentityModel.SignatureVerificationFailedException",
"StackTrace":"
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)\r\n
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)\r\n
at JwtAuthForWebAPI.JwtSecurityTokenHandlerAdapter.ValidateToken(IJwtSecurityToken securityToken, TokenValidationParameters validationParameters)\r\n
at JwtAuthForWebAPI.JwtAuthenticationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n
at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()"}
这是一个示例 JSON 令牌:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwic3ViIjoiMyIsInJvbGUiOlsiVXNlciIsIkFkbWluIl0sImlzcyI6Ik15IENvbXBhbnkiLCJleHAiOjE0Mjk1NTE4MjgsIm5iZiI6MTQyOTU0ODIyOH0.9wA_RBir9u7Cn_-Fy2T-Q_IDUfz6B928IEbIgXD9Bug
有趣的是,我能够使用 http://jwt.io 使用我的密钥验证令牌。我怀疑这可能与 JwtAuthForWebAPI 库有关,它查看的内容不同于 System.Identity JWT 库生成的内容?
我是 Jamie(JwtAuthForWebAPI 包的作者)。服务器配置代码 - 具体来说,SecurityTokenBuilder.CreateFromKey(string)
- 假设给定的字符串是 base64 编码的 。要么是这样,要么需要假设或参数来指示使用哪种编码来转换为字节数组。我选择假设该字符串是 base64 编码的。我确信有一种更清晰的方法可以将字符串密钥转换为 SecurityToken,但这就是今天代码的方式。
在 JwtAuthForWebAPI.SampleClient 项目中的 SmokeTests.cs 中,您可以看到我使用了 Convert.FromBase64String()
方法,而不是使用 [=15] 中的 GetBytes()
方法=] class:
public const string SymmetricKey = "YQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6ADAAMQAyADMANAA1AA==";
// ...
var key = Convert.FromBase64String(SymmetricKey);
var credentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(key),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
请继续使用您当前的令牌生成代码,但在服务器上...
请尝试在服务器配置代码中指定 JsonWebTokenSecretKey 的 base64 编码版本。您可以使用像 https://www.base64encode.org/ 这样的网站对其进行编码,或者尝试这样的代码:
var base64key = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonWebTokenSecretKey));
var keyBuilder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
Issuer = "My Company",
AllowedAudience = ApplicationId.ToString(),
SigningToken = keyBuilder.CreateFromKey(base64key),
PrincipalTransformer = new MyUserPrincipleTransformer()
};
让我知道这是否有效。
此外,我将更新库以捕获 SignatureVerificationFailedException 异常和 return 401,而不是让内部服务器错误发生。您仍然需要将密钥指定为 base64 字符串,但至少此类配置问题不会导致 500 错误。
再一次,请告诉我这是否有效。
这只是我基于@Jamie 回答的代码示例
protected string GetUsername(string token)
{
string secret = "keyyyyy!@3";
var key = Convert.FromBase64String(secret);
var IssuerSigningKey = new SymmetricSecurityKey(key);
IdentityModelEventSource.ShowPII = true;
var SigningCredentials = new SigningCredentials(
IssuerSigningKey,
SecurityAlgorithms.HmacSha256Signature);
var handler = new JwtSecurityTokenHandler();
var tokenSecure = handler.ReadToken(token) as SecurityToken;
var validations = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = IssuerSigningKey,
ValidateIssuer = false,
ValidateAudience = false
};
var claims = handler.ValidateToken(token, validations, out tokenSecure);
return claims.Identity.Name;
}