From 701ac13a46ee180b2332b77deb5e224636a9cba3 Mon Sep 17 00:00:00 2001 From: id4s Date: Wed, 4 Jun 2025 09:25:00 -0700 Subject: [PATCH 1/2] remove experimental code from TokenValidationResult --- .../JsonWebTokenHandler.ValidateToken.cs | 2 +- .../Results/TokenValidationResult.cs | 59 +------------------ 2 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs index dde23a0707..10c1b10e6f 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs @@ -598,7 +598,7 @@ internal async ValueTask ValidateTokenPayloadAsync( } string tokenType = Validators.ValidateTokenType(jsonWebToken.Typ, jsonWebToken, validationParameters); - return new TokenValidationResult(jsonWebToken, this, validationParameters.Clone(), issuer, null) + return new TokenValidationResult(jsonWebToken, this, validationParameters.Clone(), issuer) { IsValid = true, TokenType = tokenType diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs index 987ddb77cc..b3b901f4fd 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Results/TokenValidationResult.cs @@ -7,7 +7,6 @@ using System.Security.Claims; using System.Threading; using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Tokens.Experimental; namespace Microsoft.IdentityModel.Tokens { @@ -18,7 +17,6 @@ namespace Microsoft.IdentityModel.Tokens public class TokenValidationResult { private readonly TokenValidationParameters _tokenValidationParameters; - private readonly ValidationParameters _validationParameters; private readonly TokenHandler _tokenHandler; // Fields lazily initialized in a thread-safe manner. _claimsIdentity is protected by the _claimsIdentitySyncObj @@ -39,10 +37,6 @@ public class TokenValidationResult private ClaimsIdentity _claimsIdentity; private Dictionary _claims; private Dictionary _propertyBag; - // TODO - lazy creation of _validationResults - private List _validationResults; - - private ValidationError _validationError; private Exception _exception; private bool _isValid; @@ -60,65 +54,19 @@ public TokenValidationResult() /// /// /// - /// /// This constructor is used by JsonWebTokenHandler as part of delaying creation of ClaimsIdentity. internal TokenValidationResult( SecurityToken securityToken, TokenHandler tokenHandler, TokenValidationParameters tokenValidationParameters, - string issuer, - List validationResults) + string issuer) { _tokenValidationParameters = tokenValidationParameters; _tokenHandler = tokenHandler; - _validationResults = validationResults; Issuer = issuer; SecurityToken = securityToken; } - /// - /// Initializes a new instance of using . - /// - /// The - /// - /// - /// - /// - /// - /// This constructor is used by JsonWebTokenHandler as part of delaying creation of ClaimsIdentity. - internal TokenValidationResult( - SecurityToken securityToken, - TokenHandler tokenHandler, - ValidationParameters validationParameters, - string issuer, - List validationResults, - ValidationError validationError) - { - _validationParameters = validationParameters; - _tokenHandler = tokenHandler; - _validationResults = validationResults; - Issuer = issuer; - SecurityToken = securityToken; - _validationError = validationError; - } - - /// - /// Initializes a new instance of using . - /// - /// - /// - /// - /// This constructor is used by JsonWebTokenHandler as part of delaying creation of ClaimsIdentity. - internal TokenValidationResult( - TokenHandler tokenHandler, - ValidationParameters validationParameters, - ValidationError validationError) - { - _tokenHandler = tokenHandler; - _validationError = validationError; - _validationParameters = validationParameters; - } - /// /// The created from the validated security token. /// @@ -183,8 +131,6 @@ internal ClaimsIdentity ClaimsIdentityNoLocking { if (_tokenValidationParameters != null) _claimsIdentity = _tokenHandler.CreateClaimsIdentityInternal(SecurityToken, _tokenValidationParameters, Issuer); - else if (_validationParameters != null) - _claimsIdentity = _tokenHandler.CreateClaimsIdentityInternal(SecurityToken, _validationParameters, Issuer); } _claimsIdentityInitialized = true; @@ -242,9 +188,6 @@ public Exception Exception get { HasValidOrExceptionWasRead = true; - if (_exception is null && _validationError is not null) - return _validationError.GetException(); - return _exception; } set From ee654131c4d5129030163eb36b6970e7e808eb6b Mon Sep 17 00:00:00 2001 From: id4s Date: Wed, 4 Jun 2025 14:36:56 -0700 Subject: [PATCH 2/2] move all experimental code into partial classes --- .../JwtTokenUtilities.DecryptTokenResult.cs | 41 ++++++++ .../JwtTokenUtilities.cs | 82 +++++++--------- .../Saml/SamlTokenUtilities.Experimental.cs | 67 +++++++++++++ .../Saml/SamlTokenUtilities.cs | 56 +---------- .../SecurityTokenException.Experimental.cs | 63 +++++++++++++ .../Exceptions/SecurityTokenException.cs | 53 +---------- .../TokenHandler.Internal.cs | 20 ++++ .../TokenHandler.cs | 20 ---- .../XmlValidationException.Experimental.cs | 63 +++++++++++++ .../Exceptions/XmlValidationException.cs | 47 +--------- .../Reference.Experimental.cs | 48 ++++++++++ src/Microsoft.IdentityModel.Xml/Reference.cs | 35 +------ .../Signature.Experimental.cs | 93 +++++++++++++++++++ src/Microsoft.IdentityModel.Xml/Signature.cs | 79 +--------------- .../SignedInfo.Experimental.cs | 49 ++++++++++ src/Microsoft.IdentityModel.Xml/SignedInfo.cs | 39 +------- 16 files changed, 484 insertions(+), 371 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.Experimental.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.Experimental.cs create mode 100644 src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.Experimental.cs create mode 100644 src/Microsoft.IdentityModel.Xml/Reference.Experimental.cs create mode 100644 src/Microsoft.IdentityModel.Xml/Signature.Experimental.cs create mode 100644 src/Microsoft.IdentityModel.Xml/SignedInfo.Experimental.cs diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs index a595fa56bd..231ceee5c6 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.DecryptTokenResult.cs @@ -3,8 +3,10 @@ using System; using System.Text; +using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens.Experimental; +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; namespace Microsoft.IdentityModel.JsonWebTokens { @@ -127,5 +129,44 @@ internal static ValidationResult DecryptJwtToken( ex); } } + + private static ValidationError GetDecryptionError( + JwtTokenDecryptionParameters decryptionParameters, + bool algorithmNotSupportedByCryptoProvider, + StringBuilder exceptionStrings, + StringBuilder keysAttempted, +#pragma warning disable CA1801 // Review unused parameters + CallContext callContext) +#pragma warning restore CA1801 // Review unused parameters + { + if (keysAttempted is not null) + return new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10603, + LogHelper.MarkAsNonPII(keysAttempted.ToString()), + exceptionStrings?.ToString() ?? string.Empty, + LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), + ValidationFailureType.TokenDecryptionFailed, + typeof(SecurityTokenDecryptionFailedException), + ValidationError.GetCurrentStackFrame()); + else if (algorithmNotSupportedByCryptoProvider) + return new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10619, + LogHelper.MarkAsNonPII(decryptionParameters.Alg), + LogHelper.MarkAsNonPII(decryptionParameters.Enc)), + ValidationFailureType.TokenDecryptionFailed, + typeof(SecurityTokenDecryptionFailedException), + ValidationError.GetCurrentStackFrame()); + else + return new ValidationError( + new MessageDetail( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), + ValidationFailureType.TokenDecryptionFailed, + typeof(SecurityTokenDecryptionFailedException), + ValidationError.GetCurrentStackFrame()); + } + } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 7a8bcdc3cf..ff7d7a01b2 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -12,7 +12,6 @@ using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; using Microsoft.IdentityModel.Tokens.Json; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; @@ -284,7 +283,10 @@ internal static string DecryptJwtToken( if (!cryptoProviderFactory.IsSupportedAlgorithm(decryptionParameters.Enc, key)) { if (LogHelper.IsEnabled(EventLogLevel.Warning)) - LogHelper.LogWarning(TokenLogMessages.IDX10611, LogHelper.MarkAsNonPII(decryptionParameters.Enc), LogHelper.MarkAsNonPII(key.KeyId)); + LogHelper.LogWarning( + TokenLogMessages.IDX10611, + LogHelper.MarkAsNonPII(decryptionParameters.Enc), + LogHelper.MarkAsNonPII(key.KeyId)); algorithmNotSupportedByCryptoProvider = true; continue; @@ -313,17 +315,7 @@ internal static string DecryptJwtToken( (keysAttempted ??= new StringBuilder()).AppendLine(key.KeyId); } - if (!decryptionSucceeded) - { - ValidationError validationError = GetDecryptionError( - decryptionParameters, - algorithmNotSupportedByCryptoProvider, - exceptionStrings, - keysAttempted, - null); - - throw LogHelper.LogExceptionMessage(validationError.GetException()); - } + ValidateDecryption(decryptionParameters, decryptionSucceeded, algorithmNotSupportedByCryptoProvider, exceptionStrings, keysAttempted); try { @@ -334,46 +326,38 @@ internal static string DecryptJwtToken( } catch (Exception ex) { - throw LogHelper.LogExceptionMessage(new SecurityTokenDecompressionFailedException(GetIDX10679LogMessage(zipAlgorithm), ex)); + throw LogHelper.LogExceptionMessage( + new SecurityTokenDecompressionFailedException( + GetIDX10679LogMessage(zipAlgorithm), + ex)); } } - private static ValidationError GetDecryptionError( - JwtTokenDecryptionParameters decryptionParameters, - bool algorithmNotSupportedByCryptoProvider, - StringBuilder exceptionStrings, - StringBuilder keysAttempted, -#pragma warning disable CA1801 // Review unused parameters - CallContext callContext) -#pragma warning restore CA1801 // Review unused parameters + private static void ValidateDecryption(JwtTokenDecryptionParameters decryptionParameters, bool decryptionSucceeded, bool algorithmNotSupportedByCryptoProvider, StringBuilder exceptionStrings, StringBuilder keysAttempted) { - if (keysAttempted is not null) - return new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10603, - LogHelper.MarkAsNonPII(keysAttempted.ToString()), - exceptionStrings?.ToString() ?? string.Empty, - LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), - ValidationFailureType.TokenDecryptionFailed, - typeof(SecurityTokenDecryptionFailedException), - ValidationError.GetCurrentStackFrame()); - else if (algorithmNotSupportedByCryptoProvider) - return new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10619, - LogHelper.MarkAsNonPII(decryptionParameters.Alg), - LogHelper.MarkAsNonPII(decryptionParameters.Enc)), - ValidationFailureType.TokenDecryptionFailed, - typeof(SecurityTokenDecryptionFailedException), - ValidationError.GetCurrentStackFrame()); - else - return new ValidationError( - new MessageDetail( - TokenLogMessages.IDX10609, - LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)), - ValidationFailureType.TokenDecryptionFailed, - typeof(SecurityTokenDecryptionFailedException), - ValidationError.GetCurrentStackFrame()); + if (!decryptionSucceeded && keysAttempted is not null) + throw LogHelper.LogExceptionMessage( + new SecurityTokenDecryptionFailedException( + LogHelper.FormatInvariant( + TokenLogMessages.IDX10603, + LogHelper.MarkAsNonPII(keysAttempted.ToString()), + (object)exceptionStrings ?? string.Empty, + LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)))); + + if (!decryptionSucceeded && algorithmNotSupportedByCryptoProvider) + throw LogHelper.LogExceptionMessage( + new SecurityTokenDecryptionFailedException( + LogHelper.FormatInvariant( + TokenLogMessages.IDX10619, + LogHelper.MarkAsNonPII(decryptionParameters.Alg), + LogHelper.MarkAsNonPII(decryptionParameters.Enc)))); + + if (!decryptionSucceeded) + throw LogHelper.LogExceptionMessage( + new SecurityTokenDecryptionFailedException( + LogHelper.FormatInvariant( + TokenLogMessages.IDX10609, + LogHelper.MarkAsSecurityArtifact(decryptionParameters.EncodedToken, SafeLogJwtToken)))); } private static byte[] DecryptToken(CryptoProviderFactory cryptoProviderFactory, SecurityKey key, string encAlg, byte[] ciphertext, byte[] headerAscii, byte[] initializationVector, byte[] authenticationTag) diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.Experimental.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.Experimental.cs new file mode 100644 index 0000000000..9b862f8bf7 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.Experimental.cs @@ -0,0 +1,67 @@ + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens.Experimental; +using Microsoft.IdentityModel.Xml; + +namespace Microsoft.IdentityModel.Tokens.Saml +{ + /// + /// A class which contains useful methods for processing saml tokens. + /// + internal partial class SamlTokenUtilities + { + /// + /// Returns a to use when validating the signature of a token. + /// + /// The field of the token being validated + /// The to be used for validating the token. + /// Returns a to use for signature validation. + /// If key fails to resolve, then null is returned + internal static SecurityKey ResolveTokenSigningKey(KeyInfo tokenKeyInfo, ValidationParameters validationParameters) + { + if (tokenKeyInfo is null || validationParameters.IssuerSigningKeys is null) + return null; + + for (int i = 0; i < validationParameters.IssuerSigningKeys.Count; i++) + { + if (tokenKeyInfo.MatchesKey(validationParameters.IssuerSigningKeys[i])) + return validationParameters.IssuerSigningKeys[i]; + } + + return null; + } + + /// + /// Fetches current configuration from the ConfigurationManager of + /// and populates ValidIssuers and IssuerSigningKeys. + /// + /// the token validation parameters to update. + /// + /// New ValidationParameters with ValidIssuers and IssuerSigningKeys updated. + internal static async Task PopulateValidationParametersWithCurrentConfigurationAsync( + ValidationParameters validationParameters, + CancellationToken cancellationToken) + { + if (validationParameters.ConfigurationManager == null) + { + return validationParameters; + } + + var currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); + var validationParametersCloned = validationParameters.Clone(); + + validationParametersCloned.ValidIssuers.Add(currentConfiguration.Issuer); + + foreach (SecurityKey key in currentConfiguration.SigningKeys) + { + validationParametersCloned.IssuerSigningKeys.Add(key); + } + + return validationParametersCloned; + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs index 5531c3f7bf..2cb5efed83 100644 --- a/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs @@ -12,14 +12,13 @@ using System.Threading.Tasks; using Microsoft.IdentityModel.Logging; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; -using Microsoft.IdentityModel.Tokens.Experimental; namespace Microsoft.IdentityModel.Tokens.Saml { /// /// A class which contains useful methods for processing saml tokens. /// - internal class SamlTokenUtilities + internal partial class SamlTokenUtilities { /// /// Returns a to use when validating the signature of a token. @@ -48,29 +47,6 @@ internal static SecurityKey ResolveTokenSigningKey(KeyInfo tokenKeyInfo, TokenVa return null; } - /// - /// Returns a to use when validating the signature of a token. - /// - /// The field of the token being validated - /// The to be used for validating the token. - /// Returns a to use for signature validation. - /// If key fails to resolve, then null is returned - internal static SecurityKey ResolveTokenSigningKey(KeyInfo tokenKeyInfo, ValidationParameters validationParameters) - { - if (tokenKeyInfo is null || validationParameters.IssuerSigningKeys is null) - return null; - - for (int i = 0; i < validationParameters.IssuerSigningKeys.Count; i++) - { - if (tokenKeyInfo.MatchesKey(validationParameters.IssuerSigningKeys[i])) - return validationParameters.IssuerSigningKeys[i]; - } - - return null; - } - - - /// /// Creates 's from . /// @@ -176,36 +152,6 @@ internal static async Task PopulateValidationParamete validationParametersCloned.ValidIssuers = (validationParametersCloned.ValidIssuers == null ? issuers : validationParametersCloned.ValidIssuers.Concat(issuers)); validationParametersCloned.IssuerSigningKeys = (validationParametersCloned.IssuerSigningKeys == null ? currentConfiguration.SigningKeys : validationParametersCloned.IssuerSigningKeys.Concat(currentConfiguration.SigningKeys)); return validationParametersCloned; - - } - - /// - /// Fetches current configuration from the ConfigurationManager of - /// and populates ValidIssuers and IssuerSigningKeys. - /// - /// the token validation parameters to update. - /// - /// New ValidationParameters with ValidIssuers and IssuerSigningKeys updated. - internal static async Task PopulateValidationParametersWithCurrentConfigurationAsync( - ValidationParameters validationParameters, - CancellationToken cancellationToken) - { - if (validationParameters.ConfigurationManager == null) - { - return validationParameters; - } - - var currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); - var validationParametersCloned = validationParameters.Clone(); - - validationParametersCloned.ValidIssuers.Add(currentConfiguration.Issuer); - - foreach (SecurityKey key in currentConfiguration.SigningKeys) - { - validationParametersCloned.IssuerSigningKeys.Add(key); - } - - return validationParametersCloned; } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.Experimental.cs b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.Experimental.cs new file mode 100644 index 0000000000..a450cb8794 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.Experimental.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Microsoft.IdentityModel.Tokens.Experimental; + +#if !NET8_0_OR_GREATER +using System.Text; +#endif + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Represents a security token exception. + /// + public partial class SecurityTokenException : Exception + { + [NonSerialized] + private string _stackTrace; + + [NonSerialized] + private ValidationError _validationError; + + /// + /// Sets the that caused the exception. + /// + /// + internal void SetValidationError(ValidationError validationError) + { + _validationError = validationError; + } + + /// + /// Gets the stack trace that is captured when the exception is created. + /// + public override string StackTrace + { + get + { + if (_stackTrace == null) + { + if (_validationError == null) + return base.StackTrace; +#if NET8_0_OR_GREATER + _stackTrace = new StackTrace(_validationError.StackFrames).ToString(); +#else + StringBuilder sb = new(); + foreach (StackFrame frame in _validationError.StackFrames) + { + sb.Append(frame.ToString()); + sb.Append(Environment.NewLine); + } + + _stackTrace = sb.ToString(); +#endif + } + + return _stackTrace; + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs index ace51f36c3..0ec75b08c3 100644 --- a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs +++ b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs @@ -2,33 +2,20 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Runtime.Serialization; #if NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER using Microsoft.IdentityModel.Logging; #endif -using Microsoft.IdentityModel.Tokens.Experimental; - -#if !NET8_0_OR_GREATER -using System.Text; -#endif - - namespace Microsoft.IdentityModel.Tokens { /// /// Represents a security token exception. /// [Serializable] - public class SecurityTokenException : Exception + public partial class SecurityTokenException : Exception { - [NonSerialized] - private string _stackTrace; - - private ValidationError _validationError; - /// /// Initializes a new instance of the class. /// @@ -70,44 +57,6 @@ protected SecurityTokenException(SerializationInfo info, StreamingContext contex { } - /// - /// Sets the that caused the exception. - /// - /// - internal void SetValidationError(ValidationError validationError) - { - _validationError = validationError; - } - - /// - /// Gets the stack trace that is captured when the exception is created. - /// - public override string StackTrace - { - get - { - if (_stackTrace == null) - { - if (_validationError == null) - return base.StackTrace; -#if NET8_0_OR_GREATER - _stackTrace = new StackTrace(_validationError.StackFrames).ToString(); -#else - StringBuilder sb = new(); - foreach (StackFrame frame in _validationError.StackFrames) - { - sb.Append(frame.ToString()); - sb.Append(Environment.NewLine); - } - - _stackTrace = sb.ToString(); -#endif - } - - return _stackTrace; - } - } - /// /// Gets or sets the source of the exception. /// diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs index 5a5d334f0c..1e5d510dfe 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.Internal.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.IdentityModel.Tokens.Experimental; @@ -43,5 +44,24 @@ internal virtual Task> Validat "ValidateTokenAsync(SecurityToken token, ValidationParameters validationParameters, CallContext callContext, CancellationToken cancellationToken)"), MarkAsNonPII(GetType().FullName)))); } + + /// + /// Called by base class to create a . + /// Currently only used by the JsonWebTokenHandler when called with ValidationParameters to allow for a Lazy creation. + /// + /// the that has the Claims. + /// the that was used to validate the token. + /// the 'issuer' to use by default when creating a Claim. + /// A . + /// + internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer) + { + throw LogExceptionMessage( + new NotImplementedException( + FormatInvariant( + LogMessages.IDX10267, + MarkAsNonPII("internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer)"), + MarkAsNonPII(GetType().FullName)))); + } } } diff --git a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs index 6b5becda7a..c293f930ac 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenHandler.cs @@ -5,7 +5,6 @@ using System.ComponentModel; using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.IdentityModel.Tokens.Experimental; using static Microsoft.IdentityModel.Logging.LogHelper; namespace Microsoft.IdentityModel.Tokens @@ -125,25 +124,6 @@ internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken secur MarkAsNonPII("internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, TokenValidationParameters tokenValidationParameters, string issuer)"), MarkAsNonPII(GetType().FullName)))); } - - /// - /// Called by base class to create a . - /// Currently only used by the JsonWebTokenHandler when called with ValidationParameters to allow for a Lazy creation. - /// - /// the that has the Claims. - /// the that was used to validate the token. - /// the 'issuer' to use by default when creating a Claim. - /// A . - /// - internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer) - { - throw LogExceptionMessage( - new NotImplementedException( - FormatInvariant( - LogMessages.IDX10267, - MarkAsNonPII("internal virtual ClaimsIdentity CreateClaimsIdentityInternal(SecurityToken securityToken, ValidationParameters validationParameters, string issuer)"), - MarkAsNonPII(GetType().FullName)))); - } #endregion } } diff --git a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.Experimental.cs b/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.Experimental.cs new file mode 100644 index 0000000000..883c85883a --- /dev/null +++ b/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.Experimental.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using Microsoft.IdentityModel.Tokens.Experimental; + +#if !NET8_0_OR_GREATER +using System.Text; +#endif + +namespace Microsoft.IdentityModel.Xml +{ + /// + /// This exception is thrown when a problem occurs when validating the XML <Signature>. + /// + public partial class XmlValidationException : XmlException + { + [NonSerialized] + private string _stackTrace; + + [NonSerialized] + private ValidationError _validationError; + + /// + /// Sets the that caused the exception. + /// + /// + internal void SetValidationError(ValidationError validationError) + { + _validationError = validationError; + } + + /// + /// Gets the stack trace that is captured when the exception is created. + /// + public override string StackTrace + { + get + { + if (_stackTrace == null) + { + if (_validationError == null) + return base.StackTrace; +#if NET8_0_OR_GREATER + _stackTrace = new StackTrace(_validationError.StackFrames).ToString(); +#else + StringBuilder sb = new(); + foreach (StackFrame frame in _validationError.StackFrames) + { + sb.Append(frame.ToString()); + sb.Append(Environment.NewLine); + } + + _stackTrace = sb.ToString(); +#endif + } + + return _stackTrace; + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.cs b/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.cs index efcdeffd47..67af8f4631 100644 --- a/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.cs +++ b/src/Microsoft.IdentityModel.Xml/Exceptions/XmlValidationException.cs @@ -2,12 +2,10 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Runtime.Serialization; #pragma warning disable IDE0005 // Using directive is unnecessary. using System.Text; #pragma warning restore IDE0005 // Using directive is unnecessary. -using Microsoft.IdentityModel.Tokens.Experimental; namespace Microsoft.IdentityModel.Xml { @@ -15,13 +13,8 @@ namespace Microsoft.IdentityModel.Xml /// This exception is thrown when a problem occurs when validating the XML <Signature>. /// [Serializable] - public class XmlValidationException : XmlException + public partial class XmlValidationException : XmlException { - [NonSerialized] - private string _stackTrace; - - private ValidationError _validationError; - /// /// Initializes a new instance of the class. /// @@ -59,43 +52,5 @@ protected XmlValidationException(SerializationInfo info, StreamingContext contex : base(info, context) { } - - /// - /// Sets the that caused the exception. - /// - /// - internal void SetValidationError(ValidationError validationError) - { - _validationError = validationError; - } - - /// - /// Gets the stack trace that is captured when the exception is created. - /// - public override string StackTrace - { - get - { - if (_stackTrace == null) - { - if (_validationError == null) - return base.StackTrace; -#if NET8_0_OR_GREATER - _stackTrace = new StackTrace(_validationError.StackFrames).ToString(); -#else - StringBuilder sb = new(); - foreach (StackFrame frame in _validationError.StackFrames) - { - sb.Append(frame.ToString()); - sb.Append(Environment.NewLine); - } - - _stackTrace = sb.ToString(); -#endif - } - - return _stackTrace; - } - } } } diff --git a/src/Microsoft.IdentityModel.Xml/Reference.Experimental.cs b/src/Microsoft.IdentityModel.Xml/Reference.Experimental.cs new file mode 100644 index 0000000000..5ee1e4ccc4 --- /dev/null +++ b/src/Microsoft.IdentityModel.Xml/Reference.Experimental.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Security.Cryptography; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; + +namespace Microsoft.IdentityModel.Xml +{ + /// + /// Represents a XmlDsig Reference element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Reference + /// + public partial class Reference : DSigElement + { +#nullable enable + /// + /// Verifies that the equals the hashed value of the after + /// have been applied. + /// + /// supplies the . + /// contextual information for diagnostics. + /// if is null. + internal SignatureValidationError? Verify( + CryptoProviderFactory cryptoProviderFactory, +#pragma warning disable CA1801 // Review unused parameters + CallContext callContext) +#pragma warning restore CA1801 + { + if (cryptoProviderFactory == null) + return SignatureValidationError.NullParameter( + nameof(cryptoProviderFactory), + ValidationError.GetCurrentStackFrame()); + + if (!Utility.AreEqual(ComputeDigest(cryptoProviderFactory), Convert.FromBase64String(DigestValue))) + return new SignatureValidationError( + new MessageDetail( + LogMessages.IDX30201, + Uri ?? Id), + ValidationFailureType.XmlValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + ValidationError.GetCurrentStackFrame()); + + return null; + } +#nullable restore + } +} diff --git a/src/Microsoft.IdentityModel.Xml/Reference.cs b/src/Microsoft.IdentityModel.Xml/Reference.cs index 44bc737bec..9217c47862 100644 --- a/src/Microsoft.IdentityModel.Xml/Reference.cs +++ b/src/Microsoft.IdentityModel.Xml/Reference.cs @@ -7,7 +7,6 @@ using System.Security.Cryptography; using System.Xml; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; using static Microsoft.IdentityModel.Logging.LogHelper; using static Microsoft.IdentityModel.Xml.XmlUtil; @@ -16,7 +15,7 @@ namespace Microsoft.IdentityModel.Xml /// /// Represents a XmlDsig Reference element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Reference /// - public class Reference : DSigElement + public partial class Reference : DSigElement { private CanonicalizingTransfrom _canonicalizingTransfrom; private string _digestMethod; @@ -127,38 +126,6 @@ public void Verify(CryptoProviderFactory cryptoProviderFactory) throw LogValidationException(LogMessages.IDX30201, Uri ?? Id); } -#nullable enable - /// - /// Verifies that the equals the hashed value of the after - /// have been applied. - /// - /// supplies the . - /// contextual information for diagnostics. - /// if is null. - internal SignatureValidationError? Verify( - CryptoProviderFactory cryptoProviderFactory, -#pragma warning disable CA1801 // Review unused parameters - CallContext callContext) -#pragma warning restore CA1801 - { - if (cryptoProviderFactory == null) - return SignatureValidationError.NullParameter( - nameof(cryptoProviderFactory), - ValidationError.GetCurrentStackFrame()); - - if (!Utility.AreEqual(ComputeDigest(cryptoProviderFactory), Convert.FromBase64String(DigestValue))) - return new SignatureValidationError( - new MessageDetail( - LogMessages.IDX30201, - Uri ?? Id), - ValidationFailureType.XmlValidationFailed, - typeof(SecurityTokenInvalidSignatureException), - ValidationError.GetCurrentStackFrame()); - - return null; - } -#nullable restore - /// /// Writes into a stream and then hashes the bytes. /// diff --git a/src/Microsoft.IdentityModel.Xml/Signature.Experimental.cs b/src/Microsoft.IdentityModel.Xml/Signature.Experimental.cs new file mode 100644 index 0000000000..d9e29b20a7 --- /dev/null +++ b/src/Microsoft.IdentityModel.Xml/Signature.Experimental.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; + +namespace Microsoft.IdentityModel.Xml +{ + /// + /// Represents a XmlDsig Signature element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Signature + /// + public partial class Signature : DSigElement + { +#nullable enable + internal SignatureValidationError? Verify( + SecurityKey key, + CryptoProviderFactory cryptoProviderFactory, +#pragma warning disable CA1801 // Review unused parameters + CallContext callContext) +#pragma warning restore CA1801 + { + if (key is null) + return SignatureValidationError.NullParameter( + nameof(key), + ValidationError.GetCurrentStackFrame()); + + if (cryptoProviderFactory is null) + return SignatureValidationError.NullParameter( + nameof(cryptoProviderFactory), + ValidationError.GetCurrentStackFrame()); + + if (SignedInfo is null) + return new SignatureValidationError( + new MessageDetail(LogMessages.IDX30212), + ValidationFailureType.SignatureValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + ValidationError.GetCurrentStackFrame()); + + if (!cryptoProviderFactory.IsSupportedAlgorithm(SignedInfo.SignatureMethod, key)) + return new SignatureValidationError( + new MessageDetail(LogMessages.IDX30207, SignedInfo.SignatureMethod, cryptoProviderFactory.GetType()), + ValidationFailureType.XmlValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + ValidationError.GetCurrentStackFrame()); + + var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, SignedInfo.SignatureMethod); + if (signatureProvider is null) + return new SignatureValidationError( + new MessageDetail(LogMessages.IDX30203, cryptoProviderFactory, LogHelper.MarkAsNonPII(key.KeyId), SignedInfo.SignatureMethod), + ValidationFailureType.XmlValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + ValidationError.GetCurrentStackFrame()); + + SignatureValidationError? validationError = null; + + try + { + using (var memoryStream = new MemoryStream()) + { + SignedInfo.GetCanonicalBytes(memoryStream); + if (!signatureProvider.Verify(memoryStream.ToArray(), Convert.FromBase64String(SignatureValue))) + { + validationError = new SignatureValidationError( + new MessageDetail(LogMessages.IDX30200, cryptoProviderFactory, LogHelper.MarkAsNonPII(key.KeyId)), + ValidationFailureType.XmlValidationFailed, + typeof(SecurityTokenInvalidSignatureException), + ValidationError.GetCurrentStackFrame()); + } + } + + if (validationError is null) + { + validationError = SignedInfo.Verify(cryptoProviderFactory, callContext); + validationError?.AddCurrentStackFrame(); + } + } + finally + { + if (signatureProvider is not null) + cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider); + } + + if (validationError is not null) + return validationError; + + return null; // no error + } +#nullable restore + } +} diff --git a/src/Microsoft.IdentityModel.Xml/Signature.cs b/src/Microsoft.IdentityModel.Xml/Signature.cs index f5ef70b93a..dc4e47847b 100644 --- a/src/Microsoft.IdentityModel.Xml/Signature.cs +++ b/src/Microsoft.IdentityModel.Xml/Signature.cs @@ -5,7 +5,6 @@ using System.IO; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; using static Microsoft.IdentityModel.Logging.LogHelper; using static Microsoft.IdentityModel.Xml.XmlUtil; @@ -14,7 +13,7 @@ namespace Microsoft.IdentityModel.Xml /// /// Represents a XmlDsig Signature element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Signature /// - public class Signature : DSigElement + public partial class Signature : DSigElement { private string _signatureValue; private SignedInfo _signedInfo; @@ -126,81 +125,5 @@ public void Verify(SecurityKey key, CryptoProviderFactory cryptoProviderFactory) cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider); } } - -#nullable enable - internal SignatureValidationError? Verify( - SecurityKey key, - CryptoProviderFactory cryptoProviderFactory, -#pragma warning disable CA1801 // Review unused parameters - CallContext callContext) -#pragma warning restore CA1801 - { - if (key is null) - return SignatureValidationError.NullParameter( - nameof(key), - ValidationError.GetCurrentStackFrame()); - - if (cryptoProviderFactory is null) - return SignatureValidationError.NullParameter( - nameof(cryptoProviderFactory), - ValidationError.GetCurrentStackFrame()); - - if (SignedInfo is null) - return new SignatureValidationError( - new MessageDetail(LogMessages.IDX30212), - ValidationFailureType.SignatureValidationFailed, - typeof(SecurityTokenInvalidSignatureException), - ValidationError.GetCurrentStackFrame()); - - if (!cryptoProviderFactory.IsSupportedAlgorithm(SignedInfo.SignatureMethod, key)) - return new SignatureValidationError( - new MessageDetail(LogMessages.IDX30207, SignedInfo.SignatureMethod, cryptoProviderFactory.GetType()), - ValidationFailureType.XmlValidationFailed, - typeof(SecurityTokenInvalidSignatureException), - ValidationError.GetCurrentStackFrame()); - - var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, SignedInfo.SignatureMethod); - if (signatureProvider is null) - return new SignatureValidationError( - new MessageDetail(LogMessages.IDX30203, cryptoProviderFactory, LogHelper.MarkAsNonPII(key.KeyId), SignedInfo.SignatureMethod), - ValidationFailureType.XmlValidationFailed, - typeof(SecurityTokenInvalidSignatureException), - ValidationError.GetCurrentStackFrame()); - - SignatureValidationError? validationError = null; - - try - { - using (var memoryStream = new MemoryStream()) - { - SignedInfo.GetCanonicalBytes(memoryStream); - if (!signatureProvider.Verify(memoryStream.ToArray(), Convert.FromBase64String(SignatureValue))) - { - validationError = new SignatureValidationError( - new MessageDetail(LogMessages.IDX30200, cryptoProviderFactory, LogHelper.MarkAsNonPII(key.KeyId)), - ValidationFailureType.XmlValidationFailed, - typeof(SecurityTokenInvalidSignatureException), - ValidationError.GetCurrentStackFrame()); - } - } - - if (validationError is null) - { - validationError = SignedInfo.Verify(cryptoProviderFactory, callContext); - validationError?.AddCurrentStackFrame(); - } - } - finally - { - if (signatureProvider is not null) - cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider); - } - - if (validationError is not null) - return validationError; - - return null; // no error - } -#nullable restore } } diff --git a/src/Microsoft.IdentityModel.Xml/SignedInfo.Experimental.cs b/src/Microsoft.IdentityModel.Xml/SignedInfo.Experimental.cs new file mode 100644 index 0000000000..bf0f969ab0 --- /dev/null +++ b/src/Microsoft.IdentityModel.Xml/SignedInfo.Experimental.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Experimental; + +namespace Microsoft.IdentityModel.Xml +{ + /// + /// Represents a XmlDsig SignedInfo element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-SignedInfo + /// + public partial class SignedInfo : DSigElement + { +#nullable enable + /// + /// Verifies the digest of all + /// + /// supplies any required cryptographic operators. + /// contextual information for diagnostics. + internal SignatureValidationError? Verify( + CryptoProviderFactory cryptoProviderFactory, +#pragma warning disable CA1801 + CallContext callContext) +#pragma warning restore CA1801 + { + if (cryptoProviderFactory == null) + return SignatureValidationError.NullParameter( + nameof(cryptoProviderFactory), + ValidationError.GetCurrentStackFrame()); + + SignatureValidationError? validationError = null; + + for (int i = 0; i < References.Count; i++) + { + var reference = References[i]; + validationError = reference.Verify(cryptoProviderFactory, callContext); + + if (validationError is not null) + { + validationError.AddCurrentStackFrame(); + break; + } + } + + return validationError; + } +#nullable restore + } +} diff --git a/src/Microsoft.IdentityModel.Xml/SignedInfo.cs b/src/Microsoft.IdentityModel.Xml/SignedInfo.cs index e9c04c03e0..bc2729ab83 100644 --- a/src/Microsoft.IdentityModel.Xml/SignedInfo.cs +++ b/src/Microsoft.IdentityModel.Xml/SignedInfo.cs @@ -7,7 +7,7 @@ using System.Xml; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.Tokens.Experimental; +//using Microsoft.IdentityModel.Tokens.Experimental; using static Microsoft.IdentityModel.Logging.LogHelper; namespace Microsoft.IdentityModel.Xml @@ -15,7 +15,7 @@ namespace Microsoft.IdentityModel.Xml /// /// Represents a XmlDsig SignedInfo element as per: https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-SignedInfo /// - public class SignedInfo : DSigElement + public partial class SignedInfo : DSigElement { private DSigSerializer _dsigSerializer = DSigSerializer.Default; private string _canonicalizationMethod = SecurityAlgorithms.ExclusiveC14n; @@ -113,41 +113,6 @@ public void Verify(CryptoProviderFactory cryptoProviderFactory) reference.Verify(cryptoProviderFactory); } -#nullable enable - /// - /// Verifies the digest of all - /// - /// supplies any required cryptographic operators. - /// contextual information for diagnostics. - internal SignatureValidationError? Verify( - CryptoProviderFactory cryptoProviderFactory, -#pragma warning disable CA1801 - CallContext callContext) -#pragma warning restore CA1801 - { - if (cryptoProviderFactory == null) - return SignatureValidationError.NullParameter( - nameof(cryptoProviderFactory), - ValidationError.GetCurrentStackFrame()); - - SignatureValidationError? validationError = null; - - for (int i = 0; i < References.Count; i++) - { - var reference = References[i]; - validationError = reference.Verify(cryptoProviderFactory, callContext); - - if (validationError is not null) - { - validationError.AddCurrentStackFrame(); - break; - } - } - - return validationError; - } -#nullable restore - /// /// Writes the Canonicalized bytes into a stream. ///