From 3ac0fb30b6b0c2b6bacc4d3fc1fa9db51eb9687c Mon Sep 17 00:00:00 2001 From: Ignacio Inglese Date: Wed, 23 Oct 2024 18:04:19 +0100 Subject: [PATCH] Regression tests: Signature (#2930) * Marked json web token as security artifact for logging * Added regression/comparison tests for signature validation scenarios --- .../JsonWebTokenHandler.ValidateToken.cs | 6 +- ...ndler.ValidateTokenAsyncTests.Signature.cs | 141 ++++++++++++++++++ ...ebTokenHandlerValidationParametersTests.cs | 66 -------- 3 files changed, 146 insertions(+), 67 deletions(-) create mode 100644 test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Signature.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs index bd576925d8..918788932f 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs @@ -144,7 +144,11 @@ private static JsonWebToken ValidateSignature(JsonWebToken jwtToken, TokenValida if (!jwtToken.IsSigned) { if (validationParameters.RequireSignedTokens) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10504, jwtToken))); + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidSignatureException( + LogHelper.FormatInvariant( + TokenLogMessages.IDX10504, + LogHelper.MarkAsSecurityArtifact(jwtToken, JwtTokenUtilities.SafeLogJwtToken)))); else return jwtToken; } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Signature.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Signature.cs new file mode 100644 index 0000000000..c72363b715 --- /dev/null +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandler.ValidateTokenAsyncTests.Signature.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable +using System; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Xunit; + +namespace Microsoft.IdentityModel.JsonWebTokens.Tests +{ + public partial class JsonWebTokenHandlerValidateTokenAsyncTests + { + [Theory, MemberData(nameof(ValidateTokenAsync_SignatureTestCases), DisableDiscoveryEnumeration = true)] + public async Task ValidateTokenAsync_Signature(ValidateTokenAsyncSignatureTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.ValidateTokenAsync_Signature", theoryData); + + string jwtString = CreateTokenWithSigningCredentials(theoryData.SigningCredentials); + + await ValidateAndCompareResults(jwtString, theoryData, context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData ValidateTokenAsync_SignatureTestCases + { + get + { + var theoryData = new TheoryData(); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Valid_SignatureIsValid") + { + SigningCredentials = KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2, + TokenValidationParameters = CreateTokenValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + ValidationParameters = CreateValidationParameters(KeyingMaterial.DefaultX509SigningCreds_2048_RsaSha2_Sha2.Key), + }); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Invalid_TokenIsNotSigned") + { + SigningCredentials = null, + TokenValidationParameters = CreateTokenValidationParameters(), + ValidationParameters = CreateValidationParameters(), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), + }); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysFalse") + { + SigningCredentials = Default.SymmetricSigningCredentials, + TokenValidationParameters = CreateTokenValidationParameters(Default.AsymmetricSigningKey), + ValidationParameters = CreateValidationParameters(Default.AsymmetricSigningKey), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), + // ValidateTokenAsync with ValidationParameters returns a different error message in the case where a + // key is not found in the IssuerSigningKeys collection and TryAllKeys is false. + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10502:"), + }); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysTrue") + { + SigningCredentials = Default.SymmetricSigningCredentials, + TokenValidationParameters = CreateTokenValidationParameters(Default.AsymmetricSigningKey, tryAllKeys: true), + ValidationParameters = CreateValidationParameters(Default.AsymmetricSigningKey, tryAllKeys: true), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503:"), + }); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdNotPresent_TryAllKeysFalse") + { + SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId, + TokenValidationParameters = CreateTokenValidationParameters(Default.AsymmetricSigningKey), + ValidationParameters = CreateValidationParameters(Default.AsymmetricSigningKey), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), + }); + + theoryData.Add(new ValidateTokenAsyncSignatureTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdNotPresent_TryAllKeysTrue") + { + SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId, + TokenValidationParameters = CreateTokenValidationParameters(Default.AsymmetricSigningKey, tryAllKeys: true), + ValidationParameters = CreateValidationParameters(Default.AsymmetricSigningKey, tryAllKeys: true), + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10517:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10517:"), + }); + + return theoryData; + + static TokenValidationParameters CreateTokenValidationParameters(SecurityKey? signingKey = null, bool tryAllKeys = false) + { + // only validate the signature + var tokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + ValidateIssuer = false, + ValidateLifetime = false, + ValidateTokenReplay = false, + ValidateIssuerSigningKey = false, + RequireSignedTokens = true, + IssuerSigningKey = signingKey, + TryAllIssuerSigningKeys = tryAllKeys, + }; + + return tokenValidationParameters; + } + + static ValidationParameters CreateValidationParameters( + SecurityKey? signingKey = null, bool tryAllKeys = false) + { + ValidationParameters validationParameters = new ValidationParameters(); + + if (signingKey is not null) + validationParameters.IssuerSigningKeys.Add(signingKey); + + // Skip all validations except signature + validationParameters.AlgorithmValidator = SkipValidationDelegates.SkipAlgorithmValidation; + validationParameters.AudienceValidator = SkipValidationDelegates.SkipAudienceValidation; + validationParameters.IssuerValidatorAsync = SkipValidationDelegates.SkipIssuerValidation; + validationParameters.IssuerSigningKeyValidator = SkipValidationDelegates.SkipIssuerSigningKeyValidation; + validationParameters.LifetimeValidator = SkipValidationDelegates.SkipLifetimeValidation; + validationParameters.TokenReplayValidator = SkipValidationDelegates.SkipTokenReplayValidation; + validationParameters.TypeValidator = SkipValidationDelegates.SkipTokenTypeValidation; + validationParameters.TryAllIssuerSigningKeys = tryAllKeys; + + return validationParameters; + } + } + } + + public class ValidateTokenAsyncSignatureTheoryData : ValidateTokenAsyncBaseTheoryData + { + public ValidateTokenAsyncSignatureTheoryData(string testId) : base(testId) { } + + public SigningCredentials? SigningCredentials { get; set; } + } + } +} +#nullable restore diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs index b7d9763e24..b5b3f1351d 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs @@ -114,72 +114,6 @@ public static TheoryData Json ExpectedException = ExpectedException.SecurityTokenMalformedException("IDX14100:"), ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenMalformedException("IDX14107:", typeof(SecurityTokenMalformedException)), }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_Issuer") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - Issuer = "InvalidIssuer", - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10205:"), - // ValidateTokenAsync with ValidationParameters returns a different error message to account for the - // removal of the ValidIssuer property from the ValidationParameters class. - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidIssuerException("IDX10212:"), - }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenNotSigned") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - SigningCredentials = null, - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10504:"), - }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysFalse") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - SigningCredentials = Default.SymmetricSigningCredentials, - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - // ValidateTokenAsync with ValidationParameters returns a different error message in the case where a - // key is not found in the IssuerSigningKeys collection and TryAllKeys is false. - ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10502:"), - }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdPresent_TryAllKeysTrue") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey, tryAllKeys: true), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey, tryAllKeys: true), - SigningCredentials = Default.SymmetricSigningCredentials, - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10503:"), - }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdNotPresent_TryAllKeysFalse") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey), - SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId, - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10500:"), - }, - new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenSignedWithDifferentKey_KeyIdNotPresent_TryAllKeysTrue") - { - TokenValidationParameters = CreateTokenValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey, tryAllKeys: true), - ValidationParameters = CreateValidationParameters( - Default.Issuer, Default.AsymmetricSigningKey, tryAllKeys: true), - SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2_NoKeyId, - ExpectedIsValid = false, - ExpectedException = ExpectedException.SecurityTokenSignatureKeyNotFoundException("IDX10517:"), - }, new JsonWebTokenHandlerValidationParametersTheoryData("Invalid_TokenSignedWithInvalidAlgorithm") { // Token is signed with HmacSha256 but only sha256 is considered valid for this test's purposes