Как настроить MIcrosoft JWT с симметричным ключом?
Я пытаюсь настроить приложение ASP.NET, чтобы принять JNON Web Token (JWT), подписанный симметричным ключом. 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?
Ответы
Ответ 1
Обновление 2014/02/13:
Как видно из нижеприведенного определения @leastprivilege, это намного проще с RTM-версией JWT. Я настоятельно рекомендую вам проигнорировать это и перейти к примеру, который он предоставляет в 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)
не полностью заполняет TokenValidationParameters
, который он передает в JWTSecurityTokenHandler.ValidateToken(token, validationParameters)
. В частности, он не заполняет член SigningToken
или ValidIssuers
(или ValidIssuer
).
Интересно, что конфигурация, показанная мной в моем первоначальном вопросе, фактически загружается с помощью распознавателя токенов и доступна во время выполнения, как вы можете видеть в приведенном ниже коде.
Я не знаю, как указать правильную строку эмитента в файле конфигурации. Я сильно подозреваю, что есть место, где можно разместить эту информацию, но я еще не понял, где она принадлежит.
Решение моей проблемы заключается в создании специального обработчика маркеров безопасности, который происходит от 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" />
Ничего, как потратить три или четыре дня на исследование проблемы, которая решена с помощью пары дюжины строк кода.,.
Дополнение для новой версии
В июне 2013 года Microsoft официально выпустила свой JWT. Они изменили пространство имен на System.IdentityModel.Tokens.Jwt. После обновления до этого решение выше перестало работать. Чтобы заставить его работать, мне пришлось добавить следующее в CustomJwtSecurityTokenHandler
. Это в дополнение к существующему коду.
public override ClaimsPrincipal ValidateToken(JwtSecurityToken jwt)
{
var vparms = new TokenValidationParameters
{
AllowedAudiences = Configuration.AudienceRestriction.AllowedAudienceUris.Select(s => s.ToString())
};
return ValidateToken(jwt, vparms);
}
Ответ 2
Вот пример использования этой библиотеки с .Net 4.5, который выдает и проверяет JWT, подписанный с использованием HMAC SHA256 с симметричным ключом (все в коде и без 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;
Ответ 3
Джим
Спасибо, что попробовали предварительный просмотр, извините, что у вас были некоторые проблемы, которые не были очевидны: - (.
NamedKeyIssuerTokenResolver
родилась из двух идей:
- необходимо связать ключ для проверки подписи, являющейся общим секретом;
- несколько действительных ключей могут использоваться одновременно.
Он был разработан для работы с NamedKeySecurityToken
, который имеет имя и несколько ключей. NKITR
может возвращать NKST
, что упрощает проверку подписи при работе нескольких ключей.
Одна цель для NKITR
заключалась в предоставлении сопоставления между заявкой JWT iss
(в заголовке) и ключом. Когда нужно проверить подпись, JWTHandler
проверяет:
-
TokenValidationParamerter.SigningToken
, если он используется,
- A
SecurityKeyIdentifier
, полученный из JWT.Header.SigningKeyIdentifier
(поддерживается только x5t), отправляется в текущий INR
;
- A
NamedKeyIdentifierClause
создается из Jwt.Issuer
и отправляется в текущий INR
.
Так как a SecurityToken
может содержать несколько ключей, каждый в порядке используется для проверки сигнатуры, первых остановок успеха и JWT.SigningToken
будет содержать SecurityToken
, который подтвердил подпись.
Джим и Вилли,
Извините за путаницу с методом перегрузки ValidateToken(SecurityToken)
. Параметры перемещаются от Configuration
до ValidationParameters
, но не такие свойства, как ValidIssuer
, которые имеют один элемент, но
IssuerNameRegistry -> VP.IssuerNameRegistry
IssuerTokenResolver -> VP.SigningTokenResolver
AllowedAudienceUris -> VP.AllowedAudiences
CertificateValidator -> VP.CertificateValidator
SaveBootStrapContext -> VP.SaveBootStrapContext
Брент
Ответ 4
AFAIK, JWtSecurityTokenHandler еще не готов к использованию из файла конфигурации. Пример, приведенный Витторио Берточчи, также является "примером кода". В этом случае он явно вызывает перегруженный ValidateToken с дополнительным параметром tokenValidationParameters, который содержит все необходимое для проверки (например, симметричный ключ).
К сожалению, эта перегрузка не вызывается обычным конвейером Wif (он вызывает ValidateToken с помощью только токена в качестве параметра)
Я решил подклассифицировать обработчик маркера jwtsecurity, переопределить LoadCustomConfiguration, чтобы вручную загрузить материал, необходимый для создания объекта tokenValidationParemeter (мне пришлось создать для него некоторые объекты конфигурации). Затем я сделал переопределение validateToken, чтобы явно вызвать перегрузку с дополнительным параметром (который я мог бы создать "на лету" с параметрами, которые я читал из конфигурации). Все очень громоздко, но это единственный способ задействовать силу параметров tokenValidation. (но я, возможно, ошибаюсь, конечно)
<issuerTokenResolver type="Microsoft.IdentityModel.Tokens.JWT.NamedKeyIssuerTokenResolver, Microsoft.IdentityModel.Tokens.JWT">
<securityKey symmetricKey="01234567890123456789012345678901" name="MyIssuer"/>
</issuerTokenResolver>
<securityTokenHandlers>
Ответ 5
Вот как это работает с RTM-версией обработчика JWT:
http://leastprivilege.com/2013/07/16/identityserver-using-ws-federation-with-jwt-tokens-and-symmetric-signatures/