Skip to content

Commit 2477877

Browse files
authored
Regression tests: JWE Decryption (#2940)
* Added regression/comparison tests for JWE decryption scenarios. Removed previous attempt at adding regression tests in favour of the partial classes approach. * Added comments to clarify the EPK parameters for elliptic curve tests
1 parent 38ddcdc commit 2477877

File tree

2 files changed

+183
-292
lines changed

2 files changed

+183
-292
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#nullable enable
5+
#if NET472 || NET6_0_OR_GREATER
6+
using System;
7+
using Newtonsoft.Json.Linq;
8+
#endif
9+
using System.Collections.Generic;
10+
using System.Threading.Tasks;
11+
using Microsoft.IdentityModel.TestUtils;
12+
using Microsoft.IdentityModel.Tokens;
13+
using Xunit;
14+
15+
namespace Microsoft.IdentityModel.JsonWebTokens.Tests
16+
{
17+
public partial class JsonWebTokenHandlerValidateTokenAsyncTests
18+
{
19+
[Theory, MemberData(nameof(ValidateTokenAsync_DecryptionTestCases), DisableDiscoveryEnumeration = true)]
20+
public async Task ValidateTokenAsync_Decryption(ValidateTokenAsyncDecryptionTheoryData theoryData)
21+
{
22+
var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_Decryption", theoryData);
23+
24+
string jwtString = CreateEncryptedToken(theoryData.EncryptingCredentials, theoryData.AdditionalHeaderClaims);
25+
26+
await ValidateAndCompareResults(jwtString, theoryData, context);
27+
28+
TestUtilities.AssertFailIfErrors(context);
29+
}
30+
31+
public static TheoryData<ValidateTokenAsyncDecryptionTheoryData> ValidateTokenAsync_DecryptionTestCases
32+
{
33+
get
34+
{
35+
var theoryData = new TheoryData<ValidateTokenAsyncDecryptionTheoryData>();
36+
37+
theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_Aes128Cbc_HmacSha256")
38+
{
39+
EncryptingCredentials = new EncryptingCredentials(
40+
KeyingMaterial.DefaultX509Key_2048,
41+
SecurityAlgorithms.RsaPKCS1,
42+
SecurityAlgorithms.Aes128CbcHmacSha256),
43+
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509Key_2048),
44+
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509Key_2048),
45+
});
46+
47+
#if NET472 || NET6_0_OR_GREATER
48+
theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Valid_JWE_EcdhEs")
49+
{
50+
EncryptingCredentials = new EncryptingCredentials(
51+
new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true),
52+
SecurityAlgorithms.EcdhEsA256kw,
53+
SecurityAlgorithms.Aes128CbcHmacSha256)
54+
{
55+
KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public
56+
},
57+
AdditionalHeaderClaims = AdditionalEcdhEsHeaderParameters(KeyingMaterial.JsonWebKeyP521_Public),
58+
TokenValidationParameters = CreateTokenValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
59+
ValidationParameters = CreateValidationParameters(new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true)),
60+
});
61+
#endif
62+
63+
theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_NoDecryptionKeys")
64+
{
65+
EncryptingCredentials = new EncryptingCredentials(
66+
KeyingMaterial.DefaultX509Key_2048,
67+
SecurityAlgorithms.RsaPKCS1,
68+
SecurityAlgorithms.Aes128CbcHmacSha256),
69+
TokenValidationParameters = CreateTokenValidationParameters(),
70+
ValidationParameters = CreateValidationParameters(),
71+
ExpectedIsValid = false,
72+
ExpectedException = ExpectedException.SecurityTokenDecryptionFailedException("IDX10609:"),
73+
});
74+
75+
theoryData.Add(new ValidateTokenAsyncDecryptionTheoryData("Invalid_JWE_WrongDecryptionKey")
76+
{
77+
EncryptingCredentials = new EncryptingCredentials(
78+
KeyingMaterial.DefaultX509Key_2048,
79+
SecurityAlgorithms.RsaPKCS1,
80+
SecurityAlgorithms.Aes128CbcHmacSha256),
81+
TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
82+
ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultRsaSecurityKey1),
83+
ExpectedIsValid = false,
84+
ExpectedException = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
85+
// Avoid comparing the full exception message as the stack traces for the inner exceptions are different.
86+
ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenKeyWrapException("IDX10618:"),
87+
});
88+
89+
return theoryData;
90+
91+
static TokenValidationParameters CreateTokenValidationParameters(
92+
SecurityKey? tokenDecryptionKey = null, bool tryAllKeys = false)
93+
{
94+
// Skip all validations. We just want to decrypt the JWE.
95+
var tokenValidationParameters = new TokenValidationParameters
96+
{
97+
ValidateAudience = false,
98+
ValidateIssuer = false,
99+
ValidateLifetime = false,
100+
ValidateTokenReplay = false,
101+
ValidateIssuerSigningKey = false,
102+
RequireSignedTokens = false,
103+
TokenDecryptionKey = tokenDecryptionKey,
104+
};
105+
106+
return tokenValidationParameters;
107+
}
108+
109+
static ValidationParameters CreateValidationParameters(SecurityKey? tokenDecryptionKey = null)
110+
{
111+
ValidationParameters validationParameters = new ValidationParameters();
112+
113+
if (tokenDecryptionKey is not null)
114+
validationParameters.TokenDecryptionKeys = [tokenDecryptionKey];
115+
116+
117+
// Skip all validations. We just want to decrypt the JWE
118+
validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation;
119+
validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation;
120+
validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation;
121+
validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation;
122+
validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation;
123+
validationParameters.SignatureValidator = SkipValidationDelegates.SkipSignatureValidation;
124+
validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation;
125+
validationParameters.TokenTypeValidator = SkipValidationDelegates.SkipTokenTypeValidation;
126+
127+
return validationParameters;
128+
}
129+
130+
131+
#if NET472 || NET6_0_OR_GREATER
132+
static Dictionary<string, object> AdditionalEcdhEsHeaderParameters(JsonWebKey publicKeySender)
133+
{
134+
// Create the Ephemeral Public Key (Epk) header parameter as a JWK.
135+
var epkJObject = new JObject();
136+
epkJObject.Add(JsonWebKeyParameterNames.Kty, publicKeySender.Kty);
137+
epkJObject.Add(JsonWebKeyParameterNames.Crv, publicKeySender.Crv);
138+
epkJObject.Add(JsonWebKeyParameterNames.X, publicKeySender.X);
139+
epkJObject.Add(JsonWebKeyParameterNames.Y, publicKeySender.Y);
140+
141+
// Set the Ephemeral Public Key (Epk) header parameter, along with the
142+
// Agreement PartyUInfo (Apu) and Agreement PartyVInfo (Apv) header parameters
143+
// to ensure that the ECDH-ES key agreement is successful.
144+
Dictionary<string, object> additionalHeaderParams = new Dictionary<string, object>()
145+
{
146+
{ JsonWebTokens.JwtHeaderParameterNames.Apu, Guid.NewGuid().ToString() },
147+
{ JsonWebTokens.JwtHeaderParameterNames.Apv, Guid.NewGuid().ToString() },
148+
{ JsonWebTokens.JwtHeaderParameterNames.Epk, epkJObject.ToString(Newtonsoft.Json.Formatting.None) }
149+
};
150+
151+
return additionalHeaderParams;
152+
}
153+
#endif
154+
}
155+
}
156+
157+
public class ValidateTokenAsyncDecryptionTheoryData : ValidateTokenAsyncBaseTheoryData
158+
{
159+
public ValidateTokenAsyncDecryptionTheoryData(string testId) : base(testId) { }
160+
161+
public EncryptingCredentials? EncryptingCredentials { get; set; }
162+
163+
public Dictionary<string, object>? AdditionalHeaderClaims { get; set; } = null;
164+
}
165+
166+
private static string CreateEncryptedToken(
167+
EncryptingCredentials? encryptingCredentials,
168+
Dictionary<string, object>? additionalHeaderClaims = null)
169+
{
170+
JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler();
171+
172+
SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor
173+
{
174+
Subject = Default.ClaimsIdentity,
175+
EncryptingCredentials = encryptingCredentials,
176+
AdditionalHeaderClaims = additionalHeaderClaims,
177+
};
178+
179+
return jsonWebTokenHandler.CreateToken(securityTokenDescriptor);
180+
}
181+
}
182+
}
183+
#nullable restore

0 commit comments

Comments
 (0)