diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs index d71f0e8103..98dd5b89bc 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateSignature.cs @@ -241,7 +241,14 @@ private static Result ValidateSignatureWithKey( callContext); if (!result.IsSuccess) - return new(result.UnwrapError()); // Because we return an interface type, we need to explicitly create the Result. + return new ExceptionDetail( + new MessageDetail( + TokenLogMessages.IDX10518, + result.UnwrapError().MessageDetail.Message), + ValidationFailureType.SignatureValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + new StackFrame(true), + result.UnwrapError()); SignatureProvider signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg); try diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index e352e1ec0e..8a282dd2fc 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -106,6 +106,7 @@ internal static class LogMessages //public const string IDX10515 = "IDX10515: Signature validation failed. Unable to match key: \nKeyInfo: '{0}'.\nExceptions caught:\n '{1}'. \ntoken: '{2}'. Valid Lifetime: '{3}'. Valid Issuer: '{4}'"; //public const string IDX10516 = "IDX10516: Signature validation failed. Unable to match key: \nkid: '{0}'. \nNumber of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'. \ntoken: '{4}'. Valid Lifetime: '{5}'. Valid Issuer: '{6}'"; public const string IDX10517 = "IDX10517: Signature validation failed. The token's kid is missing. Keys tried: '{0}'. Number of keys in TokenValidationParameters: '{1}'. \nNumber of keys in Configuration: '{2}'. \nExceptions caught:\n '{3}'.\ntoken: '{4}'. See https://aka.ms/IDX10503 for details."; + public const string IDX10518 = "IDX10518: Signature validation failed. Algorithm validation failed with error: '{0}'."; // encryption / decryption // public const string IDX10600 = "IDX10600:"; diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs index 382002bc0c..20a676e1a1 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/Details/ExceptionDetail.cs @@ -28,7 +28,7 @@ public ExceptionDetail( ValidationFailureType failureType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type exceptionType, StackFrame stackFrame) - : this(MessageDetail, failureType, exceptionType, stackFrame, null) + : this(MessageDetail, failureType, exceptionType, stackFrame, innerException: null) { } @@ -57,6 +57,23 @@ public ExceptionDetail( }; } + public ExceptionDetail( + MessageDetail messageDetail, + ValidationFailureType failureType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type exceptionType, + StackFrame stackFrame, + ExceptionDetail innerExceptionDetail) + { + InnerExceptionDetail = innerExceptionDetail; + MessageDetail = messageDetail; + _exceptionType = exceptionType; + FailureType = failureType; + StackFrames = new List(4) + { + stackFrame + }; + } + /// /// Creates an instance of an using /// @@ -64,10 +81,13 @@ public ExceptionDetail( public Exception GetException() { Exception exception; - if (InnerException == null) + if (InnerException == null && InnerExceptionDetail == null) exception = Activator.CreateInstance(_exceptionType, MessageDetail.Message) as Exception; else - exception = Activator.CreateInstance(_exceptionType, MessageDetail.Message, InnerException) as Exception; + exception = Activator.CreateInstance( + _exceptionType, + MessageDetail.Message, + InnerException ?? InnerExceptionDetail.GetException()) as Exception; if (exception is SecurityTokenException securityTokenException) securityTokenException.ExceptionDetail = this; @@ -103,6 +123,11 @@ protected virtual void AddAdditionalInformation(Exception exception) /// public Exception InnerException { get; } + /// + /// Gets the details for the inner exception that occurred. + /// + public ExceptionDetail InnerExceptionDetail { get; } + /// /// Gets the message details that are used to generate the exception message. /// diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs index 4e67044860..1a4c36f830 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerValidationParametersTests.cs @@ -186,14 +186,32 @@ public static TheoryData Json 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 + TokenValidationParameters = CreateTokenValidationParameters( + Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, + validAlgorithms: [SecurityAlgorithms.Sha256]), + ValidationParameters = CreateValidationParameters( + Default.Issuer, [Default.Audience], KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2.Key, + validAlgorithms: [SecurityAlgorithms.Sha256]), + SigningCredentials = KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2, + ExpectedIsValid = false, + ExpectedException = ExpectedException.SecurityTokenInvalidSignatureException("IDX10511:"), + ExpectedExceptionValidationParameters = ExpectedException.SecurityTokenInvalidSignatureException( + "IDX10518:", + innerTypeExpected: typeof(SecurityTokenInvalidAlgorithmException)) + }, }; static TokenValidationParameters CreateTokenValidationParameters( string issuer, List audiences, SecurityKey issuerSigningKey, + List validAlgorithms = null, bool tryAllKeys = false) => new TokenValidationParameters { + ValidAlgorithms = validAlgorithms, ValidateAudience = true, ValidateIssuer = true, ValidateLifetime = true, @@ -209,6 +227,7 @@ static ValidationParameters CreateValidationParameters( string issuer, List audiences, SecurityKey issuerSigningKey, + List validAlgorithms = null, bool tryAllKeys = false) { ValidationParameters validationParameters = new ValidationParameters(); @@ -216,6 +235,8 @@ static ValidationParameters CreateValidationParameters( audiences.ForEach(audience => validationParameters.ValidAudiences.Add(audience)); validationParameters.IssuerSigningKeys.Add(issuerSigningKey); validationParameters.TryAllIssuerSigningKeys = tryAllKeys; + if (validAlgorithms is not null) + validationParameters.ValidAlgorithms = validAlgorithms; return validationParameters; }