Comment configurer MIcrosoft JWT avec la clé symétrique?

j'essaie de configurer mon ASP.NET application pour accepter un JSON Web Token (JWT) qui est signé avec une clé symétrique. Le STS n'est pas capable d'utiliser des certificats pour cela, donc nous utilisons leur support de clé symétrique.

de mon côté, j'utilise Microsoft JWT Developer Preview. Malheureusement, je n'ai vu aucun exemple d'utilisation d'une clé symétrique. Après avoir creusé un peu autour avec divers outils, j'ai trouvé le NamedKeyIssuerTokenResolver et j'ai découvert que j' configurer pour utiliser une clé symétrique. Par exemple:

<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>

je ne suis pas entièrement sûr de ce que je suis censé utiliser pour le name il n'. Devrait-il S'agir de L'URI du public, peut-être de l'émetteur? En tout cas, je sais que si je n'inclue pas un name, j'obtiens une exception lorsque mon programme démarre, car le securityKey l'élément nécessite cet attribut.

quoi qu'il en soit, cela ne résout toujours pas le problème. Après m'être authentifié contre les STS, j'obtiens le exception suivante:

[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

est-ce que je manque une autre étape de configuration? Est-ce que je mets la mauvaise chose dans le name l'attribut? Ou est-ce un bug connu dans la Preview du développeur JWT?

17
demandé sur Jim Mischel 2013-02-06 22:00:52

5 réponses

mise à Jour 2014/02/13:

http://leastprivilege.com/2013/07/16/identityserver-using-ws-federation-with-jwt-tokens-and-symmetric-signatures/.

notez que la réponse originale ci-dessous était pour la version Beta, Microsoft.IdentityModel.Jeton.JWT. La mise à niveau vers la version la version, le Système.IdentityModel.Jeton.Jwt, il fallait juste un peu plus de travail. Voir ci-dessous.

Le principal problème s'avère être que la méthode JWTSecurityTokenHandler.ValidateToken(token) ne remplit pas entièrement le TokenValidationParameters qu'il passe à JWTSecurityTokenHandler.ValidateToken(token, validationParameters). En particulier, il ne peuplera pas le SigningToken membre ou le ValidIssuers (ou ValidIssuer).

fait intéressant, la configuration que j'ai montrée dans ma question originale est en fait chargée par le résolveur de jeton, et est disponible à l'exécution, comme vous pouvez le voir dans le le code ci-dessous.

Je ne sais pas comment spécifier la chaîne valide de l'émetteur dans le fichier de configuration, cependant. Je soupçonne fortement qu'il y ait un endroit pour mettre cette information, mais je n'ai pas encore trouvé où elle devrait être.

La solution à mon problème est de créer un jeton de sécurité gestionnaire qui en dérive JWTSecurityTokenHandler. Primordial ValidateToken(token, validationParameters) me donne l'occasion de définir les paramètres dont j'ai besoin, et puis appeler la classe de base ValidateToken méthode.

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

dans ma toile.config, j'ai juste eu à changer le jeton de sécurité gestionnaire de:

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

rien de tel que de passer trois ou quatre jours à faire des recherches sur un problème qui est résolu avec quelques douzaines de lignes de code . . .

ajout pour la nouvelle version

en juin 2013, Microsoft a officiellement publié leur JWT. Ils ont changé le namespace en System.IdentityModel.Jeton.Jwt. Après la mise à niveau, la solution ci-dessus a cessé de fonctionner. De obtenir ce travail, j'ai dû ajouter ce qui suit à mon CustomJwtSecurityTokenHandler. En plus le code existant.

public override ClaimsPrincipal ValidateToken(JwtSecurityToken jwt)
{
    var vparms = new TokenValidationParameters
        {
            AllowedAudiences = Configuration.AudienceRestriction.AllowedAudienceUris.Select(s => s.ToString())
        };
    return ValidateToken(jwt, vparms);
}
18
répondu Jim Mischel 2014-02-13 15:15:39

voici un exemple d'utilisation de cette bibliothèque avec .Net 4.5 qui émet et valide un JWT signé avec la clé symétrique basée sur HMAC SHA256 (tout en code et sans 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;
12
répondu Kastorskij 2013-02-12 14:30:52

Jim,

Merci d'avoir essayé la preview, désolé que vous ayez eu des problèmes qui n'étaient pas évidents : - (.

NamedKeyIssuerTokenResolver est né de deux idées:

  1. la nécessité d'associer une clé pour la vérification de la signature d'un secret partagé;
  2. plusieurs clés valides pourraient être utilisées en même temps.

Il a été conçu pour fonctionner avec le NamedKeySecurityToken qui a un nom et un certain nombre de touches. NKITR peut retourner un NKST qui simplifie la vérification d'une signature lorsque plusieurs clés sont en jeu.

un but pour le NKITR était de fournir un mappage entre le JWT iss demande (dans l'en-tête) et une clé. Quand il est temps de vérifier la signature, le JWTHandler vérifie:

  1. TokenValidationParamerter.SigningToken, si trouvé l'utiliser;
  2. SecurityKeyIdentifier obtenu à partir de JWT.Header.SigningKeyIdentifier (actuellement seul x5t est pris en charge) est envoyé au INR;
  3. NamedKeyIdentifierClause est créé à partir de la Jwt.Issuer et envoyé au courant INR.

Depuis SecurityToken peut contenir plusieurs clés, chaque commande est utilisée pour vérifier la signature, premier succès s'arrête et le JWT.SigningToken contient SecurityToken qui a validé la signature.

Jim et Willy,

Désolé pour la confusion avec l' ValidateToken(SecurityToken) méthode de surcharge. Les paramètres sont déplacés à partir de ConfigurationValidationParameters, mais pas les propriétés comme ValidIssuer qui ont un seul élément, mais

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

Brent

5
répondu Brent Schmaltz 2013-06-27 17:48:34

AFAIK, le JWtSecurityTokenHandler n'est pas encore prêt à être utilisé à partir d'un fichier de configuration. L'exemple donné par Vittorio Bertocci est aussi un "exemple de code". En cela, il appelle explicitement le ValidateToken surchargé avec le paramètre tokenValidationParameters supplémentaire qui contient toutes les choses nécessaires pour faire la validation (comme la clé symétrique).

Malheureusement, cette surcharge n'est pas appelée par le pipeline WIF normal (il appelle le ValidateToken avec juste le jeton comme un paramètre) J'ai résolu de sous-classer le gestionnaire de jwtsecurity, de surcharger LoadCustomConfiguration pour charger manuellement les choses nécessaires pour créer un objet tokenValidationParemeter (j'ai dû créer quelques objets de configuration pour cela). Puis j'ai fait un override de validateToken pour explicitement appeler la surcharge avec le paramètre supplémentaire (que je pouvais créer à la volée avec les paramètres que j'ai lus à partir de la config). Tous très encombrants à faire mais le seul moyen d'exploiter le pouvoir de la tokenValidationparameters. (mais j'ai peut-être tort, bien sûr)

  <issuerTokenResolver type="Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver, Microsoft.IdentityModel.Tokens.JWT">
    <securityKey symmetricKey="01234567890123456789012345678901" name="MyIssuer"/>
  </issuerTokenResolver>
  <securityTokenHandlers>
4
répondu Willy Van den Driessche 2013-02-09 14:51:29
3
répondu leastprivilege 2013-07-16 07:34:12