Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression testing: Add JWE use cases #2815

Merged
merged 7 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,22 @@ internal ValidationResult<string> DecryptToken(
if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(jwtToken.Alg))
{
// on decryption we get the public key from the EPK value see: https://datatracker.ietf.org/doc/html/rfc7518#appendix-C
var ecdhKeyExchangeProvider = new EcdhKeyExchangeProvider(
key as ECDsaSecurityKey,
validationParameters.EphemeralDecryptionKey as ECDsaSecurityKey,
jwtToken.Alg,
jwtToken.Enc);
jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apu, out string apu);
jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apv, out string apv);
SecurityKey kdf = ecdhKeyExchangeProvider.GenerateKdf(apu, apv);
var kwp = key.CryptoProviderFactory.CreateKeyWrapProviderForUnwrap(kdf, ecdhKeyExchangeProvider.GetEncryptionAlgorithm());
var unwrappedKey = kwp.UnwrapKey(Base64UrlEncoder.DecodeBytes(jwtToken.EncryptedKey));
unwrappedKeys.Add(new SymmetricSecurityKey(unwrappedKey));
jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Epk, out string epk);
ECDsaSecurityKey? publicKey = new ECDsaSecurityKey(new JsonWebKey(epk), false);
if (publicKey is not null)
{
var ecdhKeyExchangeProvider = new EcdhKeyExchangeProvider(
key as ECDsaSecurityKey,
publicKey,
jwtToken.Alg,
jwtToken.Enc);
jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apu, out string apu);
jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apv, out string apv);
SecurityKey kdf = ecdhKeyExchangeProvider.GenerateKdf(apu, apv);
var kwp = key.CryptoProviderFactory.CreateKeyWrapProviderForUnwrap(kdf, ecdhKeyExchangeProvider.GetEncryptionAlgorithm());
var unwrappedKey = kwp.UnwrapKey(Base64UrlEncoder.DecodeBytes(jwtToken.EncryptedKey));
unwrappedKeys.Add(new SymmetricSecurityKey(unwrappedKey));
}
}
else
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ private Exception GetException(Type exceptionType, Exception innerException)
exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenException))
exception = new SecurityTokenException(MessageDetail.Message);
else if (exceptionType == typeof(SecurityTokenKeyWrapException))
exception = new SecurityTokenKeyWrapException(MessageDetail.Message);
}
else
{
Expand Down Expand Up @@ -182,6 +184,8 @@ private Exception GetException(Type exceptionType, Exception innerException)
exception = new SecurityTokenInvalidAlgorithmException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenException))
exception = new SecurityTokenException(MessageDetail.Message, actualException);
else if (exceptionType == typeof(SecurityTokenKeyWrapException))
exception = new SecurityTokenKeyWrapException(MessageDetail.Message, actualException);
}

return exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,6 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken,
/// </summary>
public string DebugId { get; set; }

/// <summary>
/// Gets the <see cref="SecurityKey"/> representing the ephemeral decryption key used for decryption by certain algorithms.
/// </summary>
public SecurityKey EphemeralDecryptionKey { get; set; }

/// <summary>
/// Gets or sets a boolean that controls if a '/' is significant at the end of the audience.
/// The default is <c>true</c>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

using System;
using System.Collections.Generic;
#if NET472_OR_GREATER || NET6_0_OR_GREATER
using Newtonsoft.Json.Linq;
#endif
using System.IdentityModel.Tokens.Jwt.Tests;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.TestUtils;
Expand Down Expand Up @@ -94,10 +97,29 @@ public static TheoryData<TokenDecryptingTheoryData> JsonWebTokenHandlerDecryptTo
Expires = DateTime.MaxValue,
NotBefore = DateTime.MinValue,
IssuedAt = DateTime.MinValue,
AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP256_Public),
};

var jsonWebTokenHandler = new JsonWebTokenHandler();
var ecdsaToken = new JsonWebToken(jsonWebTokenHandler.CreateToken(ecdsaTokenDescriptor));

static Dictionary<string, object> AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender)
{
var epkJObject = new JObject();
epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty);
epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv);
epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X);
epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y);

Dictionary<string, object> additionalHeaderParams = new Dictionary<string, object>()
{
{ JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() },
{ JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() },
{ JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) }
};

return additionalHeaderParams;
}
#endif
var configurationThatThrows = CreateCustomConfigurationThatThrows();

Expand Down Expand Up @@ -175,7 +197,6 @@ public static TheoryData<TokenDecryptingTheoryData> JsonWebTokenHandlerDecryptTo
ValidationParameters = new ValidationParameters
{
TokenDecryptionKeys = [new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true)],
EphemeralDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true)
},
Result = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjI1MzQwMjMwMDgwMCwiaWF0IjowLCJuYmYiOjB9."
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#if NET472_OR_GREATER || NET6_0_OR_GREATER
using System;
using Newtonsoft.Json.Linq;
#endif
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
Expand Down Expand Up @@ -53,7 +57,9 @@ await jsonWebTokenHandler.ValidateTokenAsync(
if (validationParametersResult.IsSuccess != theoryData.ExpectedIsValid)
context.AddDiff($"validationParametersResult.IsSuccess != theoryData.ExpectedIsValid");

if (theoryData.ExpectedIsValid)
if (theoryData.ExpectedIsValid &&
tokenValidationParametersResult.IsValid &&
validationParametersResult.IsSuccess)
{
IdentityComparer.AreEqual(
tokenValidationParametersResult.ClaimsIdentity,
Expand Down Expand Up @@ -202,12 +208,93 @@ public static TheoryData<JsonWebTokenHandlerValidationParametersTheoryData> Json
"IDX10518:",
innerTypeExpected: typeof(SecurityTokenInvalidAlgorithmException))
},
new JsonWebTokenHandlerValidationParametersTheoryData("Valid_JWE")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultX509Key_2048),
ValidationParameters = CreateValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultX509Key_2048),
},
#if NET472 || NET6_0_OR_GREATER
new JsonWebTokenHandlerValidationParametersTheoryData("Valid_JWE_EcdhEs")
{
EncryptingCredentials = new EncryptingCredentials(
new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true),
SecurityAlgorithms.EcdhEsA256kw,
SecurityAlgorithms.Aes128CbcHmacSha256)
{
KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public
},
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
AdditionalHeaderParams = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public),
TokenValidationParameters = CreateTokenValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
ValidationParameters = CreateValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
},
#endif
new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_JWE_NoDecryptionKeys")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key),
ValidationParameters = CreateValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"),
},
new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_JWE_WrongDecryptionKey")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultRsaSecurityKey1),
ValidationParameters = CreateValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultRsaSecurityKey1),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
},
new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_JWE_WrongDecryptionKey")
{
EncryptingCredentials = new EncryptingCredentials(
KeyingMaterial.DefaultX509Key_2048,
SecurityAlgorithms.RsaPKCS1,
SecurityAlgorithms.Aes128CbcHmacSha256),
SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2,
TokenValidationParameters = CreateTokenValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultRsaSecurityKey1),
ValidationParameters = CreateValidationParameters(
Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key,
tokenDecryptionKey: KeyingMaterial.DefaultRsaSecurityKey1),
ExpectedIsValid = false,
ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
},
};

static TokenValidationParameters CreateTokenValidationParameters(
string issuer,
List<string> audiences,
SecurityKey issuerSigningKey,
SecurityKey tokenDecryptionKey = null,
List<string> validAlgorithms = null,
bool tryAllKeys = false) => new TokenValidationParameters
{
Expand All @@ -218,6 +305,7 @@ static TokenValidationParameters CreateTokenValidationParameters(
ValidateTokenReplay = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = issuerSigningKey,
TokenDecryptionKey = tokenDecryptionKey,
ValidAudiences = audiences,
ValidIssuer = issuer,
TryAllIssuerSigningKeys = tryAllKeys,
Expand All @@ -227,6 +315,7 @@ static ValidationParameters CreateValidationParameters(
string issuer,
List<string> audiences,
SecurityKey issuerSigningKey,
SecurityKey tokenDecryptionKey = null,
List<string> validAlgorithms = null,
bool tryAllKeys = false)
{
Expand All @@ -237,9 +326,31 @@ static ValidationParameters CreateValidationParameters(
validationParameters.TryAllIssuerSigningKeys = tryAllKeys;
if (validAlgorithms is not null)
validationParameters.ValidAlgorithms = validAlgorithms;
if (tokenDecryptionKey is not null)
validationParameters.TokenDecryptionKeys = [tokenDecryptionKey];

return validationParameters;
}

#if NET472 || NET6_0_OR_GREATER
static Dictionary<string, object> AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender)
{
var epkJObject = new JObject();
epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty);
epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv);
epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X);
epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y);

Dictionary<string, object> additionalHeaderParams = new Dictionary<string, object>()
{
{ JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() },
{ JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() },
{ JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) }
};

return additionalHeaderParams;
}
#endif
}
}

Expand Down
Loading