我正在尝试将我的ASP.NET应用程序配置为接受使用对称密钥签名的JSON Web令牌(JWT)。 STS无法使用证书,所以我们使用它们的对称密钥支持。
在我结束时,我正在使用Microsoft's JWT Developer Preview。不幸的是,我没有看到任何关于如何使用对称密钥的示例。在使用各种工具进行了一些挖掘之后,我找到了NamedKeyIssuerTokenResolver
并发现我可以将其配置为使用对称密钥。例如:
<securityTokenHandlers>
<add type="Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler,Microsoft.IdentityModel.Tokens.JWT" />
<securityTokenHandlerConfiguration>
<certificateValidation certificateValidationMode="PeerTrust" />
<issuerTokenResolver
type="Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver,
Microsoft.IdentityModel.Tokens.JWT">
<securityKey
symmetricKey="+zqf97FD/xyzzyplugh42ploverFeeFieFoeFooxqjE="
name="https://localhost/TestRelyingParty" />
</issuerTokenResolver>
</securityTokenHandlerConfiguration>
</securityTokenHandlers>
我不完全确定我应该在那里name
使用什么。应该是观众Uri,也许是发行人Uri?无论如何,我知道如果我不包含name
,我的程序启动时会出现异常,因为securityKey
元素需要该属性。
无论如何,这仍然无法解决问题。在我对STS进行身份验证后,我得到以下异常:
[SecurityTokenValidationException: JWT10310: Unable to validate signature. validationParameters.SigningTokenResolver type: 'Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver', was unable to resolve key to a token.
The SecurityKeyIdentifier is:
'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = Microsoft.IdentityModel.Tokens.JWT.NamedKeyIdentifierClause
)
'. validationParameters.SigningToken was null.]
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateSignature(JWTSecurityToken jwt, TokenValidationParameters validationParameters) +2111
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateToken(JWTSecurityToken jwt, TokenValidationParameters validationParameters) +138
Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler.ValidateToken(SecurityToken token) +599
System.IdentityModel.Tokens.SecurityTokenHandlerCollection.ValidateToken(SecurityToken token) +135
System.IdentityModel.Services.TokenReceiver.AuthenticateToken(SecurityToken token, Boolean ensureBearerToken, String endpointUri) +117
System.IdentityModel.Services.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequestBase request) +698
System.IdentityModel.Services.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args) +123924
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
我错过了其他一些配置步骤吗?我在name
属性中输入了错误的内容吗?或者这是JWT开发者预览版中的一个已知错误?
答案 0 :(得分:18)
正如@leastprivilege指出的那样,使用JWT的RTM版本可以轻松实现这一目标。我强烈建议您忽略这一点并使用他在http://leastprivilege.com/2013/07/16/identityserver-using-ws-federation-with-jwt-tokens-and-symmetric-signatures/提供的示例。
请注意,下面的原始答案是针对Beta版本的Microsoft.IdentityModel.Tokens.JWT。升级到发布版本System.IdentityModel.Tokens.Jwt,只需要更多的工作。见下文。
主要问题是,JWTSecurityTokenHandler.ValidateToken(token)
方法未完全填充传递给TokenValidationParameters
的{{1}}。特别是,它不会填充JWTSecurityTokenHandler.ValidateToken(token, validationParameters)
成员或SigningToken
(或ValidIssuers
)。
有趣的是,我在原始问题中显示的配置实际上是由令牌解析器加载的,并且在运行时可用,如下面的代码所示。
我不知道如何在配置文件中指定有效的颁发者字符串。我强烈怀疑有一个地方可以提供这些信息,但我还没弄清楚它属于哪里。
我的问题的解决方案是创建一个派生自ValidIssuer
的自定义安全令牌处理程序。覆盖JWTSecurityTokenHandler
使我有机会设置我需要的参数,然后调用基类的ValidateToken(token, validationParameters)
方法。
ValidateToken
在我的Web.config中,我只需要更改安全令牌处理程序:
public class CustomJwtSecurityTokenHandler: JWTSecurityTokenHandler
{
// Override ValidateSignature so that it gets the SigningToken from the configuration if it doesn't exist in
// the validationParameters object.
private const string KeyName = "https://localhost/TestRelyingParty";
private const string ValidIssuerString = "https://mySTSname/trust";
public override ClaimsPrincipal ValidateToken(JWTSecurityToken jwt, TokenValidationParameters validationParameters)
{
// set up valid issuers
if ((validationParameters.ValidIssuer == null) &&
(validationParameters.ValidIssuers == null || !validationParameters.ValidIssuers.Any()))
{
validationParameters.ValidIssuers = new List<string> {ValidIssuerString};
}
// and signing token.
if (validationParameters.SigningToken == null)
{
var resolver = (NamedKeyIssuerTokenResolver)this.Configuration.IssuerTokenResolver;
if (resolver.SecurityKeys != null)
{
List<SecurityKey> skeys;
if (resolver.SecurityKeys.TryGetValue(KeyName, out skeys))
{
var tok = new NamedKeySecurityToken(KeyName, skeys);
validationParameters.SigningToken = tok;
}
}
}
return base.ValidateToken(jwt, validationParameters);
}
}
没有什么比花费三到四天研究用几十行代码解决的问题了。 。
2013年6月,微软正式发布了他们的JWT。他们将名称空间更改为System.IdentityModel.Tokens.Jwt。升级到之后,上面的解决方案停止了工作。为了使其正常工作,我必须将以下内容添加到 <securityTokenHandlers>
<!--<add type="Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler,Microsoft.IdentityModel.Tokens.JWT" />-->
<!-- replaces the default JWTSecurityTokenHandler -->
<add type="TestRelyingParty.CustomJwtSecurityTokenHandler,TestRelyingParty" />
。这是现有代码的补充。
CustomJwtSecurityTokenHandler
答案 1 :(得分:12)
这是一个使用.Net 4.5的库的用法示例,它发布并验证使用基于对称密钥的HMAC SHA256签名的JWT(所有代码都没有WIF):
string jwtIssuer = "MyIssuer";
string jwtAudience = "MyAudience";
// Generate symmetric key for HMAC-SHA256 signature
RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider();
byte[] keyForHmacSha256 = new byte[64];
cryptoProvider.GetNonZeroBytes(keyForHmacSha256);
///////////////////////////////////////////////////////////////////
// Create signing credentials for the signed JWT.
// This object is used to cryptographically sign the JWT by the issuer.
SigningCredentials sc = new SigningCredentials(
new InMemorySymmetricSecurityKey(keyForHmacSha256),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
///////////////////////////////////////////////////////////////////
// Create token validation parameters for the signed JWT
// This object will be used to verify the cryptographic signature of the received JWT
TokenValidationParameters validationParams =
new TokenValidationParameters()
{
AllowedAudience = s_jwtAudience,
ValidIssuer = s_jwtIssuer,
ValidateExpiration = true,
ValidateNotBefore = true,
ValidateIssuer = true,
ValidateSignature = true,
SigningToken = new BinarySecretSecurityToken(keyForHmacSha256),
};
///////////////////////////////////////////////////////////////////
// Create JWT handler
// This object is used to write/sign/decode/validate JWTs
JWTSecurityTokenHandler jwtHandler = new JWTSecurityTokenHandler();
// Create a simple JWT claim set
IList<Claim> payloadClaims = new List<Claim>() { new Claim("clm1", "clm1 value"), };
// Create a JWT with signing credentials and lifetime of 12 hours
JWTSecurityToken jwt =
new JWTSecurityToken(jwtIssuer, jwtAudience, payloadClaims, sc, DateTime.UtcNow, DateTime.UtcNow.AddHours(12.0));
// Serialize the JWT
// This is how our JWT looks on the wire: <Base64UrlEncoded header>.<Base64UrlEncoded body>.<signature>
string jwtOnTheWire = jwtHandler.WriteToken(jwt);
// Validate the token signature (we provide the shared symmetric key in `validationParams`)
// This will throw if the signature does not validate
jwtHandler.ValidateToken(jwtOnTheWire, validationParams);
// Parse JWT from the Base64UrlEncoded wire form (<Base64UrlEncoded header>.<Base64UrlEncoded body>.<signature>)
JWTSecurityToken parsedJwt = jwtHandler.ReadToken(jwtOnTheWire) as JWTSecurityToken;
答案 2 :(得分:5)
吉姆,
感谢您尝试预览,抱歉您遇到了一些不明显的问题: - (。
NamedKeyIssuerTokenResolver
诞生于两个想法:
它旨在使用具有名称和许多键的NamedKeySecurityToken
。 NKITR
可以返回NKST
,这样可以在多个密钥播放时简化签名检查。
NKITR
的一个目标是在JWT iss
声明(标题中)和密钥之间提供映射。在检查签名时,JWTHandler
检查:
TokenValidationParamerter.SigningToken
,如果发现使用它; SecurityKeyIdentifier
获得的JWT.Header.SigningKeyIdentifier
(目前仅支持x5t)会发送到当前INR
; NamedKeyIdentifierClause
创建Jwt.Issuer
并发送到当前INR
。由于SecurityToken
可以包含多个密钥,因此每个密钥用于检查签名,首先成功停止,JWT.SigningToken
将包含验证签名的SecurityToken
。< / p>
吉姆和威利,
很抱歉与ValidateToken(SecurityToken)
重载方法混淆了。参数从Configuration
移至ValidationParameters
,但不包含ValidIssuer
等具有单个项目的属性,但
IssuerNameRegistry -> VP.IssuerNameRegistry
IssuerTokenResolver -> VP.SigningTokenResolver
AllowedAudienceUris -> VP.AllowedAudiences
CertificateValidator -> VP.CertificateValidator
SaveBootStrapContext -> VP.SaveBootStrapContext
布伦特
答案 3 :(得分:4)
AFAIK,JWtSecurityTokenHandler尚未准备好从配置文件中使用。 Vittorio Bertocci给出的例子也是一个“代码示例”。在那里,他使用附加的tokenValidationParameters参数显式调用重载的ValidateToken,该参数包含进行验证所需的所有内容(如对称密钥)。
不幸的是,普通的Wif管道不会调用该重载(它只调用带有令牌作为参数的ValidateToken)
我决定继承jwtsecurity令牌处理程序,重写LoadCustomConfiguration以手动加载创建tokenValidationParemeter对象所需的东西(我必须为此创建一些配置对象)。然后我做了一个validateToken的覆盖来明确地使用附加参数调用重载(我可以使用从配置中读取的参数动态创建)。所有这些都非常麻烦,但却是利用tokenValidationparameters强大功能的唯一方法。 (但我当然可能错了)
<issuerTokenResolver type="Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver, Microsoft.IdentityModel.Tokens.JWT">
<securityKey symmetricKey="01234567890123456789012345678901" name="MyIssuer"/>
</issuerTokenResolver>
<securityTokenHandlers>
答案 4 :(得分:3)