17

対称キーで署名された JSON Web トークン (JWT) を受け入れるように ASP.NET アプリを構成しようとしています。STS はこれに証明書を使用できないため、STS の対称キー サポートを使用しています。

私の側では、Microsoft の 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。視聴者ウリか、発行者ウリか。いずれにせよ、 を含めないと、要素がその属性を必要とする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 Developer Preview の既知のバグですか?

4

5 に答える 5

18

2014/02/13 更新:

@leastprivilege が以下で指摘しているように、これは JWT の RTM バージョンを使用すると非常に簡単になります。これを無視して、彼がhttp://leastprivilege.com/2013/07/16/identityserver-using-ws-federation-with-jwt-tokens-and-symmetric-signatures/で提供している例に従うことを強くお勧めします。

以下の元の回答は、ベータ版の Microsoft.IdentityModel.Tokens.JWT に対するものであることに注意してください。リリース バージョンの System.IdentityModel.Tokens.Jwt へのアップグレードには、もう少し作業が必要でした。下記参照。

主な問題は、メソッドがに渡す をJWTSecurityTokenHandler.ValidateToken(token)完全に設定していないことです。特に、メンバーまたは(または) は設定されません。TokenValidationParametersJWTSecurityTokenHandler.ValidateToken(token, validationParameters)SigningTokenValidIssuersValidIssuer

興味深いことに、最初の質問で示した構成は、実際にはトークン リゾルバーによって読み込まれ、実行時に利用できるようになっています (以下のコードを参照)。

ただし、構成ファイルで有効な発行者文字列を指定する方法がわかりません。その情報を入れる場所があるのではないかと強く疑っていますが、それがどこに属しているかはまだわかりません.

私の問題の解決策は、から派生したカスタム セキュリティ トークン ハンドラーを作成することJWTSecurityTokenHandlerです。オーバーライドValidateToken(token, validationParameters)すると、必要なパラメーターを設定してから、基本クラスのValidateTokenメソッドを呼び出すことができます。

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);
    }
}

私の Web.config では、セキュリティ トークン ハンドラーを次のように変更する必要がありました。

  <securityTokenHandlers>
    <!--<add type="Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler,Microsoft.IdentityModel.Tokens.JWT" />-->
    <!-- replaces the default JWTSecurityTokenHandler -->
    <add type="TestRelyingParty.CustomJwtSecurityTokenHandler,TestRelyingParty" />

数十行のコードで解決できる問題を調査するのに 3 ~ 4 日を費やすのに勝るものはありません。. .

新バージョン追加

2013 年 6 月、Microsoft は正式に JWT をリリースしました。名前空間を System.IdentityModel.Tokens.Jwt に変更しました。それにアップグレードした後、上記のソリューションは機能しなくなりました。それを機能させるには、以下を my.xml に追加する必要がありましたCustomJwtSecurityTokenHandler。それは既存のコードに追加されます。

public override ClaimsPrincipal ValidateToken(JwtSecurityToken jwt)
{
    var vparms = new TokenValidationParameters
        {
            AllowedAudiences = Configuration.AudienceRestriction.AllowedAudienceUris.Select(s => s.ToString())
        };
    return ValidateToken(jwt, vparms);
}
于 2013-02-12T17:08:10.043 に答える
13

以下は、対称鍵ベースの HMAC SHA256 で署名された JWT を発行および検証する .Net 4.5 を使用したこのライブラリの使用例です (すべてコード内で 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;
于 2013-02-12T14:30:52.290 に答える
5

ジム、

プレビューをお試しいただきありがとうございます。わかりにくい問題がありましたことをお詫び申し上げます :-(.

は、次のNamedKeyIssuerTokenResolver2 つのアイデアから生まれました。

  1. 共有秘密である署名をチェックするためのキーを関連付ける必要性。
  2. 複数の有効なキーが同時に使用されている可能性があります。

NamedKeySecurityToken名前とキーの数を持つ で動作するように設計されています。は、複数のキーが使用されている場合に署名のチェックを簡素化する をNKITR返すことができます。NKST

の 1 つの目標はNKITR、JWTissクレーム (ヘッダー内) とキーの間のマッピングを提供することでした。署名をチェックするとき、 は次をチェックしJWTHandlerます。

  1. TokenValidationParamerter.SigningToken、見つかった場合はそれを使用します。
  2. からSecurityKeyIdentifier取得したJWT.Header.SigningKeyIdentifier(現在は x5t のみがサポートされています) は、現在の に送信されINRます。
  3. からNamedKeyIdentifierClauseが作成されJwt.Issuer、現在の に送信されINRます。

SecurityTokenには複数のキーを含めることができるため、それぞれのキーを順番に使用して署名を確認します。最初の成功は停止し、署名を検証した にJWT.SigningTokenは が含まれます。SecurityToken

ジムとウィリー、

ValidateToken(SecurityToken)オーバーロード メソッドとの混乱について申し訳ありません。パラメータは からConfigurationに移動されますが、単一のアイテムを持つValidationParametersようなプロパティではありませんが、ValidIssuer

IssuerNameRegistry -> VP.IssuerNameRegistry
IssuerTokenResolver -> VP.SigningTokenResolver
AllowedAudienceUris -> VP.AllowedAudiences
CertificateValidator -> VP.CertificateValidator
SaveBootStrapContext -> VP.SaveBootStrapContext

ブレント

于 2013-03-08T07:51:25.003 に答える
4

私の知る限り、JWtSecurityTokenHandler はまだ構成ファイルから使用する準備ができていません。Vittorio Bertocci の例も「コード例」です。その中で、検証を行うために必要なすべてのもの (対称鍵など) を含む追加の tokenValidationParameters パラメーターを使用して、オーバーロードされた ValidateToken を明示的に呼び出します。
残念ながら、そのオーバーロードは通常の Wif パイプラインでは呼び出されません (パラメーターとしてトークンのみを使用して ValidateToken を呼び出します)。jwtsecurity トークン ハンドラーをサブクラス化することにしました。このためにいくつかの構成オブジェクトを作成します)。次に、validateToken のオーバーライドを行って、追加のパラメーター (構成から読み取ったパラメーターを使用してオンザフライで作成できます) を使用してオーバーロードを明示的に呼び出しました。すべてを行うのは非常に面倒ですが、tokenValidation パラメーターの力を活用する唯一の方法です。(もちろん間違っているかもしれませんが)

  <issuerTokenResolver type="Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver, Microsoft.IdentityModel.Tokens.JWT">
    <securityKey symmetricKey="01234567890123456789012345678901" name="MyIssuer"/>
  </issuerTokenResolver>
  <securityTokenHandlers>
于 2013-02-09T14:51:29.797 に答える
3

JWTハンドラーの RTM バージョンでの動作は次のとおりです。

于 2013-07-16T07:34:12.667 に答える