From a40eb6b36793b88145d0960414ed7b0b8e131e03 Mon Sep 17 00:00:00 2001 From: Kelly Song Date: Tue, 18 Jun 2024 10:50:37 -0700 Subject: [PATCH 1/3] rm app context switches --- .../AuthenticatedEncryptionProvider.cs | 9 +-- .../LogMessages.cs | 2 +- .../X509EncryptingCredentials.cs | 14 +--- .../JsonWebTokenHandlerTests.cs | 50 +------------- .../X509EncryptingCredentialsTests.cs | 8 +-- ...tyTokenHandlerTests.WithContextSwitches.cs | 66 ------------------- 6 files changed, 9 insertions(+), 140 deletions(-) delete mode 100644 test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs diff --git a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs index b6b063936d..b9459a1af8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs @@ -33,7 +33,6 @@ private struct AuthenticatedKeys private DecryptionDelegate DecryptFunction; private EncryptionDelegate EncryptFunction; private const string _className = "Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionProvider"; - internal const string _skipValidationOfAuthenticationTagLength = "Switch.Microsoft.IdentityModel.SkipAuthenticationTagLengthValidation"; /// /// Initializes a new instance of the class used for encryption and decryption. @@ -167,8 +166,7 @@ private AuthenticatedEncryptionResult EncryptWithAesCbc(byte[] plaintext, byte[] private byte[] DecryptWithAesCbc(byte[] ciphertext, byte[] authenticatedData, byte[] iv, byte[] authenticationTag) { // Verify authentication Tag - if (ShouldValidateAuthenticationTagLength() - && SymmetricSignatureProvider.ExpectedSignatureSizeInBytes.TryGetValue(Algorithm, out int expectedTagLength) + if (SymmetricSignatureProvider.ExpectedSignatureSizeInBytes.TryGetValue(Algorithm, out int expectedTagLength) && expectedTagLength != authenticationTag.Length) throw LogHelper.LogExceptionMessage(new SecurityTokenDecryptionFailedException( LogHelper.FormatInvariant(LogMessages.IDX10625, authenticationTag.Length, expectedTagLength, Base64UrlEncoder.Encode(authenticationTag), Algorithm))); @@ -197,11 +195,6 @@ private byte[] DecryptWithAesCbc(byte[] ciphertext, byte[] authenticatedData, by } } - private static bool ShouldValidateAuthenticationTagLength() - { - return !(AppContext.TryGetSwitch(_skipValidationOfAuthenticationTagLength, out bool skipValidation) && skipValidation); - } - private AuthenticatedKeys CreateAuthenticatedKeys() { ValidateKeySize(Key, Algorithm); diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index 679fbf2b8f..abc9364743 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -136,7 +136,7 @@ internal static class LogMessages // public const string IDX10622 = "IDX10622:"; // public const string IDX10623 = "IDX10623:"; // public const string IDX10624 = "IDX10624:"; - public const string IDX10625 = "IDX10625: Failed to verify the authenticationTag length, the actual tag length '{0}' does not match the expected tag length '{1}'. authenticationTag: '{2}', algorithm: '{3}' See: https://aka.ms/IdentityModel/SkipAuthenticationTagLengthValidation"; + public const string IDX10625 = "IDX10625: Failed to verify the authenticationTag length, the actual tag length '{0}' does not match the expected tag length '{1}'. authenticationTag: '{2}', algorithm: '{3}'."; // public const string IDX10627 = "IDX10627:"; public const string IDX10628 = "IDX10628: Cannot set the MinimumSymmetricKeySizeInBits to less than '{0}'."; public const string IDX10630 = "IDX10630: The '{0}' for signing cannot be smaller than '{1}' bits. KeySize: '{2}'."; diff --git a/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs index ec0f483dd8..6a92eaedaa 100644 --- a/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs +++ b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs @@ -11,8 +11,6 @@ namespace Microsoft.IdentityModel.Tokens /// public class X509EncryptingCredentials : EncryptingCredentials { - internal const string _useShortNameForRsaOaepKey = "Switch.Microsoft.IdentityModel.UseShortNameForRsaOaepKey"; - /// /// Designed to construct based on a x509 certificate. /// @@ -23,7 +21,7 @@ public class X509EncryptingCredentials : EncryptingCredentials /// /// if 'certificate' is null. public X509EncryptingCredentials(X509Certificate2 certificate) - : this(certificate, GetEncryptionAlgorithm(), SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm) + : this(certificate, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm) { } @@ -50,15 +48,5 @@ public X509Certificate2 Certificate get; private set; } - - private static string GetEncryptionAlgorithm() - { - return ShouldUseShortNameForRsaOaepKey() ? SecurityAlgorithms.RsaOAEP : SecurityAlgorithms.DefaultAsymmetricKeyWrapAlgorithm; - } - - private static bool ShouldUseShortNameForRsaOaepKey() - { - return AppContext.TryGetSwitch(_useShortNameForRsaOaepKey, out var useKeyWrap) && useKeyWrap; - } } } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index bd041cac2f..6b67a4f4e3 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -4190,10 +4190,9 @@ public static TheoryData IncludeSecurityTokenOnFailureTes } [Theory, MemberData(nameof(ValidateAuthenticationTagLengthTheoryData))] - public void ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData) + public async Task ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData) { // arrange - AppContext.SetSwitch(AuthenticatedEncryptionProvider._skipValidationOfAuthenticationTagLength, theoryData.EnableAppContextSwitch); var payload = new JObject() { { JwtRegisteredClaimNames.Email, "Bob@contoso.com" }, @@ -4217,9 +4216,7 @@ public void ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData var jweWithExtraCharacters = jwe + "_cannoli_hunts_truffles_"; // act - // calling ValidateTokenAsync.Result to prevent tests from sharing app context switch property - // normally, we would want to await ValidateTokenAsync().ConfigureAwait(false) - var tokenValidationResult = jsonWebTokenHandler.ValidateTokenAsync(jweWithExtraCharacters, theoryData.ValidationParameters).Result; + var tokenValidationResult = await jsonWebTokenHandler.ValidateTokenAsync(jweWithExtraCharacters, theoryData.ValidationParameters).ConfigureAwait(false); // assert Assert.Equal(theoryData.IsValid, tokenValidationResult.IsValid); @@ -4281,47 +4278,6 @@ public static TheoryData ValidateAuthenticationTagLengthT ValidIssuer = "http://Default.Issuer.com", }, IsValid = false - }, - new("A128CBC-HS256_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - Algorithm = SecurityAlgorithms.Aes128CbcHmacSha256, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true - }, - new("A192CBC-HS384_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - Algorithm = SecurityAlgorithms.Aes192CbcHmacSha384, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes192CbcHmacSha384), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true - }, - new("A256CBC-HS512_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes256CbcHmacSha512), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = signingCredentials512.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true } }; } @@ -4370,8 +4326,6 @@ public CreateTokenTheoryData(string testId) : base(testId) public IEnumerable ExpectedDecryptionKeys { get; set; } public Dictionary ExpectedClaims { get; set; } - - public bool EnableAppContextSwitch { get; set; } = false; } // Overrides CryptoProviderFactory.CreateAuthenticatedEncryptionProvider to create AuthenticatedEncryptionProviderMock that provides AesGcm encryption. diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs index 6590737fca..0168963d58 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs @@ -42,7 +42,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = null, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'certificate'"), TestId = "NullCertificate" @@ -58,7 +58,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = String.Empty, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "EmptyEncString" @@ -74,7 +74,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "NullEncString" @@ -82,7 +82,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, TestId = "ValidTest" } diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs deleted file mode 100644 index 7e87575e11..0000000000 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Xunit; - -namespace System.IdentityModel.Tokens.Jwt.Tests -{ - [CollectionDefinition("JwtSecurityTokenHandlerTestsWithContextSwitches", DisableParallelization = true)] - public class JwtSecurityTokenHandlerTestsWithContextSwitches - { - [Theory] - [InlineData(SecurityAlgorithms.RsaOAEP, true)] - [InlineData(SecurityAlgorithms.RsaOaepKeyWrap, false)] - public void JwtSecurityTokenHandler_CreateToken_AddShortFormMappingForRsaOAEP(string algorithm, bool useShortNameForRsaOaepKey) - { - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, useShortNameForRsaOaepKey); - var encryptingCredentials = new X509EncryptingCredentials(Default.Certificate); - JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); - - JwtSecurityToken token = CreateJwtSecurityToken(tokenHandler, encryptingCredentials); - - Assert.Equal(token.Header.Alg, algorithm); - - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, false); - } - - [Theory] - [InlineData(SecurityAlgorithms.RsaOAEP, true)] - [InlineData(SecurityAlgorithms.RsaOaepKeyWrap, false)] - public void JsonWebTokenHandler_CreateToken_AddShortFormMappingForRsaOAEP(string algorithm, bool useShortNameForRsaOaepKey) - { - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, useShortNameForRsaOaepKey); - var encryptingCredentials = new X509EncryptingCredentials(Default.Certificate); - JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); - - JsonWebToken jsonToken = new JsonWebToken(CreateJwtSecurityTokenAsString(tokenHandler, encryptingCredentials)); - - Assert.Equal(jsonToken.Alg, algorithm); - - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, false); - } - - private JwtSecurityToken CreateJwtSecurityToken(JwtSecurityTokenHandler tokenHandler, X509EncryptingCredentials encryptingCredentials) - { - return tokenHandler.CreateJwtSecurityToken(CreateTokenDescriptor(encryptingCredentials)); - } - - private string CreateJwtSecurityTokenAsString(JsonWebTokenHandler tokenHandler, X509EncryptingCredentials encryptingCredentials) - { - return tokenHandler.CreateToken(CreateTokenDescriptor(encryptingCredentials)); - } - - private SecurityTokenDescriptor CreateTokenDescriptor(X509EncryptingCredentials encryptingCredentials) - { - return new SecurityTokenDescriptor - { - Issuer = Default.Issuer, - SigningCredentials = Default.AsymmetricSigningCredentials, - EncryptingCredentials = encryptingCredentials, - }; - } - } -} From 836ffaad4b440ad1b2134909b68d9a375712b8eb Mon Sep 17 00:00:00 2001 From: Kelly Song Date: Tue, 18 Jun 2024 11:58:31 -0700 Subject: [PATCH 2/3] update tests --- .../EncryptingCredentialsTests.cs | 8 ++++---- .../MultiThreadingTests.cs | 4 ++-- .../X509EncryptingCredentialsTests.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs index 7b44f1b844..ad787f01d4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs @@ -57,7 +57,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = null, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'key'"), TestId = "NullKey" @@ -73,7 +73,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = String.Empty, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "EmptyEncString" @@ -89,7 +89,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "NullEncString" @@ -97,7 +97,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, TestId = "ValidTest" } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs index c9e87b0abf..68b75501d5 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs @@ -121,7 +121,7 @@ public static TheoryData MultiThreadingCreateAndVerify { Claims = Default.PayloadDictionary, SigningCredentials = new SigningCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaSha256, SecurityAlgorithms.Sha256), - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes128CbcHmacSha256) + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes128CbcHmacSha256) }; var tokenValidationParametersEncryptedRsaKW = new TokenValidationParameters @@ -174,7 +174,7 @@ public static TheoryData MultiThreadingCreateAndVerify { Claims = Default.PayloadDictionary, SigningCredentials = new SigningCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaSha256, SecurityAlgorithms.Sha256), - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes128CbcHmacSha256) + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes128CbcHmacSha256) }; var tokenValidationParametersEncryptedRsaKWCng = new TokenValidationParameters diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs index 0168963d58..2dee4d2e62 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs @@ -82,7 +82,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOAEP, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, TestId = "ValidTest" } From 14cdfb5033f451e3fdff38b6b10818535e8bebde Mon Sep 17 00:00:00 2001 From: Kelly Song Date: Tue, 18 Jun 2024 12:06:56 -0700 Subject: [PATCH 3/3] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1991880f9..5a9f0c8101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ See the [releases](https://github.com/AzureAD/azure-activedirectory-identitymode ### Breaking changes: - IdentityModel 8x no longer supports .net461, which has reached end of life and is no longer supported. See issue [#2544](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2544) for details. - Two IdentityModel extension dlls `Microsoft.IdentityModel.KeyVaultExtensions` and `Microsoft.IdentityModel.ManagedKeyVaultSecurityKey` were using ADAL, which is no longer supported . The affected packages have been removed, as the replacement is to use [Microsoft.Identity.Web](https://github.com/AzureAD/microsoft-identity-web/wiki/Certificates). See issue [#2454](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2454) for details. -- `AppContext.SetSwitch` which were included in IdentityModel 7x, have been removed and are the default in IdentityModel 8x. The result is a more performant IdentityModel by default. See issue [#2629](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2629) for details. +- `AppContext.SetSwitch` which were included in IdentityModel 7x, have been removed and are the default in IdentityModel 8x. The result is a more performant IdentityModel by default. See issue [#2629](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2629) and https://aka.ms/IdentityModel8x for details. 7.6.1 =====