From 4cfe7de4bdae86b95b0fb398584d894d642db96b Mon Sep 17 00:00:00 2001 From: Roja Ennam Date: Thu, 21 Apr 2022 15:20:06 -0700 Subject: [PATCH] parent 087f414af23fe10f806c5d6fcf9a1b8a92c9ad34 author Roja Ennam 1650579606 -0700 committer brentschmaltz 1652933215 -0700 Creating EcdhKeyExchangeProvider Adding target for net core 3.1 This reverts commit 5c51220085ffa4452d46799d1bae8a3a6bf0f8a8. Adding immediate retry on network failure + better logging during configuation retrieval (#1784) * Adding immediate retry on network failure to the token validation flow using ConfigurationManager (specifically inside of HttpDocumentRetriever) * Adding better logging during configuration retrieval Adding more information on key location to error messages (#1786) rename JwtHandler -> JsonWebTokenHandler adjust tests to throw invalid signature Simplify the EventBasedLRUCache and Allows Skipping LRU (#1783) * simplify the EventBasedLRUCache bit and added the _maintainLRU flag to skip the maintenance of LRU * resolved review comments * 1. let the event queue task continue to run for 2 min after the queue is empty 2. stop the task when the InMemoryCryptoProviderCache.Dispose() is called * skip all operations on _doubleLinkedList when _maintainLRU = false * fixed the failed test MaintainLRUOrder (_maintainLRU needs to set to true) Update DisposableObjectPool to dispose on Free() when full When the internal `items` array of DisposableObjectPool is full, calls to Free() (that are trying to return over-allocated instances during a spike in calls to Allocate() "drop" the object by doing nothing. Since the object is not disposed, before it can be garbage collected it has to wait in the finalization queue for finalization to call dispose. This change updates Free() to directly dispose those objects which can't be returned to the pool, allowing them to avoid a potentially long wait in the finalizer queue. param check for null fix check for tenantId (#1801) update version to 6.15.2 Adding LKG feature into JwtSecurityTokenHandler Adding tests for SignatureValidatorUsingConfiguration address comment add IsRecoverableConfiguration mark metadata address as non-PII Make M.IM.Tokens visible to S2S.Tokens (#1807) added the ValidateTokenAsyc() and ReadToken() methods to all token handlers (#1810) * added the ValidateTokenAsyc() method to all token handlers * implement the ValidateTokenAsync() for JwtSecurityTokenHandler * added the TokenHandler.ReadToken() method * return async result via .ConfigureAwait(false).GetAwaiter().GetResult(), and don't catch general Exception * added expected exceptions to the TokenHandler.cs() and removed CA1031 from GlobalSuppression as we are now caching specific exceptions * added more comments * updated all token handlers to catch the general exception (like in JsonWebTokenHandler) to be consistent * updated comments * always return the first ClaimsIdentity from the ClaimsPrincipal as TokenValidationResult.ClaimsIdentity * return the first identity from the ClaimsPrincipal in Saml2SecurityTokenHandler.ValidateTokenAsync() so it is consistent with Saml1 update version to 6.16.1 (#1811) update patch version after release of 6.16.0 Configuration validator (#1825) Introduce an ConfigurationValidator class that can be used to apply validation rules to a retrieved configuration. Simplify strings comparison with Ordinal option update version update version for next release Add 'cty' claim to JWE header Address comments Fix tests (#1838) Fix DEF test (#1839) Copying work from broken topic branch EcdsaKeyWrap Provider Constants Ecdh test Creating EcdhKeyExchangeProvider Removed unnecesary code and comments from ReferenceTests Created Ecdh Security Key Removing EcdhSecurityKey since ECDsaSecurityKey is the same thing Modifying ctor for EcdhKeyExchangeProvider Refactoring + overloads for ctrs in KeyExchangeProvider Differentiate in between ECDH-ES and ECDH-ES+A{128|192|256}KW. Setting AlgorithmID accordingly to alg and enc values. Returning CEK as SecurityKey Changes to Reference test to reflect the changes enc is not optional for key exchange provider apu and apv can be null/empty for generate cek added comments to guide ECDH-ES Adding Jwt header params for epk, apu and apv Adding supported algorithms for symmetric keywrap Adding ECDH-ES logic/cases to JsonWebTokenHandler added test for creating a JWE Test cases for Jwe using Ecdh-ES Reference test clean up Use of apv and apu included Aes192KW inclusion LogMessages update in EcdhEs Key Exchange Provider Removed unsued ctors Added direct tests for EcdhEsKeyExchangeProvider Cleaned up reference test Refactoring ctors in EcdhEsKeyExchangeProvider, including new LogMessage Removed unused file that was part of an earlier commit Addressing feed back from PR EcdhKeyExchangeProvider's constructor refactoring: Params for keys are both SecurityKeys, new error message to better describe when we were unable to obtain ECParameters, and some refactoring for readbility. Refactoring for JsonWebTokenHandler.ResolveTokenDecryptionKey for string comparison JwtTokenUtulities.GetSecurityKey refactor for readbility, null clauses for apu amd apv being null, and changed the creating of SymmetricSecurityKey to match with what is already in place for difference cases. Test case scenario for the metnioned above. Removed unnecesary code Adding target for net core 3.1 Copying work from broken topic branch EcdsaKeyWrap Provider Constants Ecdh test Creating EcdhKeyExchangeProvider Removed unnecesary code and comments from ReferenceTests Created Ecdh Security Key Removing EcdhSecurityKey since ECDsaSecurityKey is the same thing Modifying ctor for EcdhKeyExchangeProvider Refactoring + overloads for ctrs in KeyExchangeProvider Differentiate in between ECDH-ES and ECDH-ES+A{128|192|256}KW. Setting AlgorithmID accordingly to alg and enc values. Returning CEK as SecurityKey Changes to Reference test to reflect the changes enc is not optional for key exchange provider apu and apv can be null/empty for generate cek added test for creating a JWE Test cases for Jwe using Ecdh-ES Reference test clean up Use of apv and apu included Aes192KW inclusion LogMessages update in EcdhEs Key Exchange Provider Removed unsued ctors Added direct tests for EcdhEsKeyExchangeProvider Cleaned up reference test Refactoring ctors in EcdhEsKeyExchangeProvider, including new LogMessage Removed unused file that was part of an earlier commit Addressing feed back from PR EcdhKeyExchangeProvider's constructor refactoring: Params for keys are both SecurityKeys, new error message to better describe when we were unable to obtain ECParameters, and some refactoring for readbility. Refactoring for JsonWebTokenHandler.ResolveTokenDecryptionKey for string comparison JwtTokenUtulities.GetSecurityKey refactor for readbility, null clauses for apu amd apv being null, and changed the creating of SymmetricSecurityKey to match with what is already in place for difference cases. Test case scenario for the metnioned above. Removed unnecesary code Adding target for net core 3.1 removing extra letter from rebase Adding NET_CORE defined when using netcore 3.1 Fixing dupe code from rebase onto --- Wilson.sln | 7 + build/commonTest.props | 6 +- build/strongNameBypass.reg | Bin 7182 -> 7424 bytes build/strongNameBypass2.reg | Bin 7710 -> 7976 bytes build/targets.props | 2 +- build/targetsTest.props | 2 +- buildConfiguration.xml | 4 +- .../IIdentityLogger.cs | 49 +++ .../LogEntry.cs | 55 +++ ...icrosoft.IdentityModel.Abstractions.csproj | 25 ++ .../NullIdentityModelLogger.cs | 53 +++ .../Properties/AssemblyInfo.cs | 37 ++ .../GlobalSuppressions.cs | 6 +- .../JsonWebTokenHandler.cs | 95 ++++- .../JwtHeaderParameterNames.cs | 15 + .../JwtTokenUtilities.cs | 41 ++ .../IdentityModelTelemetryUtil.cs | 2 + .../LogHelper.cs | 96 ++++- .../Microsoft.IdentityModel.Logging.csproj | 4 + ...tyModel.Protocols.SignedHttpRequest.csproj | 2 +- .../GlobalSuppressions.cs | 4 + ...dentityModel.Protocols.WsFederation.csproj | 2 +- .../Configuration/ConfigurationManager.cs | 2 +- .../GlobalSuppressions.cs | 3 + ...rosoft.IdentityModel.TestExtensions.csproj | 2 +- .../Properties/AssemblyInfo.cs | 37 ++ .../AsymmetricAdapter.cs | 10 +- .../AsymmetricSignatureProvider.cs | 4 +- .../CallContext.cs | 1 - .../CryptoProviderFactory.cs | 2 +- .../ECDsaAdapter.cs | 6 +- .../ECDsaSecurityKey.cs | 4 +- .../EncryptingCredentials.cs | 5 + .../Encryption/EcdhKeyExchangeProvider.cs | 244 ++++++++++++ .../Encryption/SymmetricKeyWrapProvider.cs | 8 + .../Exceptions/SecurityTokenException.cs | 2 +- .../GlobalSuppressions.cs | 4 +- .../JsonWebKeyConverter.cs | 4 +- .../LogMessages.cs | 5 + .../Microsoft.IdentityModel.Tokens.csproj | 4 +- .../RsaSecurityKey.cs | 2 +- .../SecurityAlgorithms.cs | 8 + .../SupportedAlgorithms.cs | 42 ++- .../X509SecurityKey.cs | 4 +- .../Serialization/DefaultContractResolver.cs | 4 +- .../json/Serialization/JsonTypeReflector.cs | 4 +- .../GlobalSuppressions.cs | 6 + .../JwtSecurityTokenHandler.cs | 164 +++++++- .../JsonWebTokenHandlerTests.cs | 10 +- ...onWebKeySetBadRsaDataMissingComponent.json | 2 +- .../JsonWebKeySetUnrecognizedKty.json | 2 +- .../OpenIdConnectMessageTests.cs | 8 +- ...ectMetadataBadRsaDataMissingComponent.json | 2 +- .../OpenIdConnectMetadataUnrecognizedKty.json | 2 +- .../KeyingMaterial.cs | 8 + .../ReferenceTokens.cs | 34 +- .../References.cs | 127 +++++++ .../AsymmetricAdapterTests.cs | 8 +- .../CryptoProviderCacheTests.cs | 2 +- .../EcdhEsTests.cs | 197 ++++++++++ .../JweUsingEchdTests.cs | 351 ++++++++++++++++++ .../ReferenceTests.cs | 37 ++ 62 files changed, 1766 insertions(+), 112 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Abstractions/IIdentityLogger.cs create mode 100644 src/Microsoft.IdentityModel.Abstractions/LogEntry.cs create mode 100644 src/Microsoft.IdentityModel.Abstractions/Microsoft.IdentityModel.Abstractions.csproj create mode 100644 src/Microsoft.IdentityModel.Abstractions/NullIdentityModelLogger.cs create mode 100644 src/Microsoft.IdentityModel.Abstractions/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.IdentityModel.TestExtensions/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Encryption/EcdhKeyExchangeProvider.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/EcdhEsTests.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/JweUsingEchdTests.cs diff --git a/Wilson.sln b/Wilson.sln index 3b3881eec7..214fd70f7a 100644 --- a/Wilson.sln +++ b/Wilson.sln @@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Val EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.SampleTests", "test\Microsoft.IdentityModel.SampleTests\Microsoft.IdentityModel.SampleTests.csproj", "{578FDF8F-6568-448A-AB93-D94269593932}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Abstractions", "src\Microsoft.IdentityModel.Abstractions\Microsoft.IdentityModel.Abstractions.csproj", "{8057C69A-3D1E-46A3-86E4-E6B26249DD25}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -227,6 +229,10 @@ Global {578FDF8F-6568-448A-AB93-D94269593932}.Debug|Any CPU.Build.0 = Debug|Any CPU {578FDF8F-6568-448A-AB93-D94269593932}.Release|Any CPU.ActiveCfg = Release|Any CPU {578FDF8F-6568-448A-AB93-D94269593932}.Release|Any CPU.Build.0 = Release|Any CPU + {8057C69A-3D1E-46A3-86E4-E6B26249DD25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8057C69A-3D1E-46A3-86E4-E6B26249DD25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8057C69A-3D1E-46A3-86E4-E6B26249DD25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8057C69A-3D1E-46A3-86E4-E6B26249DD25}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -266,6 +272,7 @@ Global {DA585910-0E6C-45A5-AABD-30917130FD63} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} {D17F097F-6024-40BA-A7A0-015BB90F203B} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} {578FDF8F-6568-448A-AB93-D94269593932} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} + {8057C69A-3D1E-46A3-86E4-E6B26249DD25} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2F681326-7ED4-45F6-BD1D-1119EA388F42} diff --git a/build/commonTest.props b/build/commonTest.props index f2841c11e1..1758fb1fde 100644 --- a/build/commonTest.props +++ b/build/commonTest.props @@ -15,14 +15,14 @@ true $(TestTargets) $(TestOnlyCoreTargets) - $(DotNetCoreAppRuntimeVersion) + $(DotNetCoreAppRuntimeVersion) - + $(DefineConstants);NET_CORE - + diff --git a/build/strongNameBypass.reg b/build/strongNameBypass.reg index 242b5f53cb6c53b94332f7f7c8f9ad2739ac10de..82303344d8602ee158b975f068f03e090131100a 100644 GIT binary patch delta 31 mcmeCPXt3JACo@@qc^khYLlQ$VLkU9>Ln1>mL&@ZaBDw&LhX}6# delta 7 OcmZp$>a*CuCj$Tq(E?il diff --git a/build/strongNameBypass2.reg b/build/strongNameBypass2.reg index ec084be794661cbf003cf0b7dcaf2814b0724db3..78248392a3ec8ca9579b4430bcbe30d735b9b1f6 100644 GIT binary patch delta 37 rcmbPdv%+qJoZRFB=5wNs3`q>d3?&Rj42cZMKsu8lpCNCuqmViP*Lw;b delta 7 OcmZ2sH_v8+oE!iQg95Dp diff --git a/build/targets.props b/build/targets.props index 3c93d991a6..08da21dfe8 100644 --- a/build/targets.props +++ b/build/targets.props @@ -1,6 +1,6 @@ - net45;net461;net472;netstandard2.0 + net45;net461;net472;netstandard2.0;netcoreapp3.1 netstandard2.0 diff --git a/build/targetsTest.props b/build/targetsTest.props index 3bcbc92921..3d4426f8fb 100644 --- a/build/targetsTest.props +++ b/build/targetsTest.props @@ -1,6 +1,6 @@ - net452;net461;net472;netcoreapp2.1 + net452;net461;net472;netcoreapp2.1;netcoreapp3.1 netcoreapp2.1 diff --git a/buildConfiguration.xml b/buildConfiguration.xml index 4a4bf1609a..4981fd7080 100644 --- a/buildConfiguration.xml +++ b/buildConfiguration.xml @@ -19,6 +19,8 @@ + + @@ -36,7 +38,7 @@ - + diff --git a/src/Microsoft.IdentityModel.Abstractions/IIdentityLogger.cs b/src/Microsoft.IdentityModel.Abstractions/IIdentityLogger.cs new file mode 100644 index 0000000000..36c1f008c6 --- /dev/null +++ b/src/Microsoft.IdentityModel.Abstractions/IIdentityLogger.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System.Diagnostics.Tracing; + +namespace Microsoft.IdentityModel.Abstractions +{ + /// + /// Interface that needs to be implemented by classes providing logging in Microsoft identity libraries. + /// + public interface IIdentityLogger + { + /// + /// Checks to see if logging is enabled at given . + /// + /// Log level of an Event. + bool IsEnabled(EventLevel eventLevel); + + /// + /// Writes a log entry. + /// + /// Defines a structured message to be logged at the provided . + void Log(LogEntry entry); + } +} diff --git a/src/Microsoft.IdentityModel.Abstractions/LogEntry.cs b/src/Microsoft.IdentityModel.Abstractions/LogEntry.cs new file mode 100644 index 0000000000..1b4813ac7b --- /dev/null +++ b/src/Microsoft.IdentityModel.Abstractions/LogEntry.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System.Diagnostics.Tracing; + +namespace Microsoft.IdentityModel.Abstractions +{ + /// + /// Defines the structure of a log entry. + /// + public class LogEntry + { + /// + /// Defines the . + /// + public EventLevel EventLevel { get; set; } + + /// + /// Message to be logged. + /// + public string Message { get; set; } + + /// + /// A unique identifier for a request that can help with diagnostics across components. + /// + /// + /// Also referred to as ActivityId in Microsoft.IdentityModel.Tokens.CallContext. + /// + public string CorrelationId { get; set; } + } +} diff --git a/src/Microsoft.IdentityModel.Abstractions/Microsoft.IdentityModel.Abstractions.csproj b/src/Microsoft.IdentityModel.Abstractions/Microsoft.IdentityModel.Abstractions.csproj new file mode 100644 index 0000000000..7b65997e6a --- /dev/null +++ b/src/Microsoft.IdentityModel.Abstractions/Microsoft.IdentityModel.Abstractions.csproj @@ -0,0 +1,25 @@ + + + + + + Microsoft.IdentityModel.Abstractions + A package containing thin abstractions for Microsoft.IdentityModel. + true + Microsoft.IdentityModel.Abstractions + .NET;Windows;Authentication;Identity;Abstractions + + + + full + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/Microsoft.IdentityModel.Abstractions/NullIdentityModelLogger.cs b/src/Microsoft.IdentityModel.Abstractions/NullIdentityModelLogger.cs new file mode 100644 index 0000000000..ccb9ecf2f7 --- /dev/null +++ b/src/Microsoft.IdentityModel.Abstractions/NullIdentityModelLogger.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System.Diagnostics.Tracing; + +namespace Microsoft.IdentityModel.Abstractions +{ + /// + /// A minimalistic implementation that is disabled by default and doesn't log. + /// + public sealed class NullIdentityModelLogger : IIdentityLogger + { + /// + /// Default instance of . + /// + public static NullIdentityModelLogger Instance { get; } = new NullIdentityModelLogger(); + + private NullIdentityModelLogger() { } + + /// + public bool IsEnabled(EventLevel eventLevel) => false; + + /// + public void Log(LogEntry entry) + { + // no-op + } + } +} diff --git a/src/Microsoft.IdentityModel.Abstractions/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.Abstractions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..37fd5aeab4 --- /dev/null +++ b/src/Microsoft.IdentityModel.Abstractions/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyInformationalVersion("0.0.1")] +[assembly: AssemblyFileVersion("0.0.1")] +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: AssemblyVersion("0.0.1")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs index 1f2f7c2804..239d4efed6 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs @@ -24,10 +24,8 @@ [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateSignature(System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~Microsoft.IdentityModel.JsonWebTokens.JsonWebToken")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is returned in the TokenValidationResult", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateJWE(Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~Microsoft.IdentityModel.Tokens.TokenValidationResult")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is returned in the TokenValidationResult", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateJWS(System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~Microsoft.IdentityModel.Tokens.TokenValidationResult")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken(System.String,Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters)~Microsoft.IdentityModel.Tokens.TokenValidationResult")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.GetConfigurationAndLogError(Microsoft.IdentityModel.Tokens.TokenValidationParameters)~Microsoft.IdentityModel.Tokens.BaseConfiguration")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is returned in the TokenValidationResult", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenAsync(System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters)~System.Threading.Tasks.Task{Microsoft.IdentityModel.Tokens.TokenValidationResult}")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenAsync(System.String,Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters)~System.Threading.Tasks.Task{Microsoft.IdentityModel.Tokens.TokenValidationResult}")] [assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Vendored component", Scope = "module")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateTokenPrivate(System.String,Microsoft.IdentityModel.Tokens.SigningCredentials,Microsoft.IdentityModel.Tokens.EncryptingCredentials,System.String,System.Collections.Generic.IDictionary{System.String,System.Object},System.String)~System.String")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateTokenPrivate(System.String,Microsoft.IdentityModel.Tokens.SigningCredentials,Microsoft.IdentityModel.Tokens.EncryptingCredentials,System.String,System.Collections.Generic.IDictionary{System.String,System.Object},System.Collections.Generic.IDictionary{System.String,System.Object},System.String)~System.String")] +[assembly: SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "It is used within a defined if condition", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.GetSecurityKey(Microsoft.IdentityModel.Tokens.EncryptingCredentials,Microsoft.IdentityModel.Tokens.CryptoProviderFactory,System.Collections.Generic.IDictionary{System.String,System.Object},System.Byte[]@)~Microsoft.IdentityModel.Tokens.SecurityKey")] diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index c472994987..307bfc1092 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -320,7 +320,14 @@ public virtual string CreateToken(SecurityTokenDescriptor tokenDescriptor) payload[JwtRegisteredClaimNames.Nbf] = EpochTime.GetIntDate(tokenDescriptor.NotBefore.Value); } - return CreateTokenPrivate(payload.ToString(Formatting.None), tokenDescriptor.SigningCredentials, tokenDescriptor.EncryptingCredentials, tokenDescriptor.CompressionAlgorithm, tokenDescriptor.AdditionalHeaderClaims, tokenDescriptor.AdditionalInnerHeaderClaims, tokenDescriptor.TokenType); + return CreateTokenPrivate( + payload.ToString(Formatting.None), + tokenDescriptor.SigningCredentials, + tokenDescriptor.EncryptingCredentials, + tokenDescriptor.CompressionAlgorithm, + tokenDescriptor.AdditionalHeaderClaims, + tokenDescriptor.AdditionalInnerHeaderClaims, + tokenDescriptor.TokenType); } /// @@ -406,7 +413,11 @@ public virtual string CreateToken(string payload, SigningCredentials signingCred /// , , and/or /// are present inside of . /// A JWE in compact serialization format. - public virtual string CreateToken(string payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary additionalHeaderClaims) + public virtual string CreateToken( + string payload, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + IDictionary additionalHeaderClaims) { if (string.IsNullOrEmpty(payload)) throw LogHelper.LogArgumentNullException(nameof(payload)); @@ -491,7 +502,13 @@ public virtual string CreateToken(string payload, SigningCredentials signingCred /// , , and/or /// are present inside of . /// A JWE in compact serialization format. - public virtual string CreateToken(string payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, string compressionAlgorithm, IDictionary additionalHeaderClaims, IDictionary additionalInnerHeaderClaims) + public virtual string CreateToken( + string payload, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + string compressionAlgorithm, + IDictionary additionalHeaderClaims, + IDictionary additionalInnerHeaderClaims) { if (string.IsNullOrEmpty(payload)) throw LogHelper.LogArgumentNullException(nameof(payload)); @@ -511,7 +528,14 @@ public virtual string CreateToken(string payload, SigningCredentials signingCred if (additionalInnerHeaderClaims == null) throw LogHelper.LogArgumentNullException(nameof(additionalInnerHeaderClaims)); - return CreateTokenPrivate(payload, signingCredentials, encryptingCredentials, compressionAlgorithm, additionalHeaderClaims, additionalInnerHeaderClaims, null); + return CreateTokenPrivate( + payload, + signingCredentials, + encryptingCredentials, + compressionAlgorithm, + additionalHeaderClaims, + additionalInnerHeaderClaims, + null); } /// @@ -531,7 +555,12 @@ public virtual string CreateToken(string payload, SigningCredentials signingCred /// , , and/or /// are present inside of . /// A JWE in compact serialization format. - public virtual string CreateToken(string payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, string compressionAlgorithm, IDictionary additionalHeaderClaims) + public virtual string CreateToken( + string payload, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + string compressionAlgorithm, + IDictionary additionalHeaderClaims) { if (string.IsNullOrEmpty(payload)) throw LogHelper.LogArgumentNullException(nameof(payload)); @@ -551,7 +580,14 @@ public virtual string CreateToken(string payload, SigningCredentials signingCred return CreateTokenPrivate(payload, signingCredentials, encryptingCredentials, compressionAlgorithm, additionalHeaderClaims, null, null); } - private string CreateTokenPrivate(string payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, string compressionAlgorithm, IDictionary additionalHeaderClaims, IDictionary additionalInnerHeaderClaims, string tokenType) + private string CreateTokenPrivate( + string payload, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + string compressionAlgorithm, + IDictionary additionalHeaderClaims, + IDictionary additionalInnerHeaderClaims, + string tokenType) { if (additionalHeaderClaims?.Count > 0 && additionalHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any()) throw LogHelper.LogExceptionMessage(new SecurityTokenException(LogHelper.FormatInvariant(LogMessages.IDX14116, LogHelper.MarkAsNonPII(nameof(additionalHeaderClaims)), LogHelper.MarkAsNonPII(string.Join(", ", JwtTokenUtilities.DefaultHeaderParameters))))); @@ -561,7 +597,7 @@ private string CreateTokenPrivate(string payload, SigningCredentials signingCred var header = CreateDefaultJWSHeader(signingCredentials, tokenType); - if (encryptingCredentials == null && additionalHeaderClaims != null && additionalHeaderClaims.Count > 0) + if (encryptingCredentials == null && additionalHeaderClaims != null && additionalHeaderClaims.Count > 0) header.Merge(JObject.FromObject(additionalHeaderClaims)); header.Merge(JObject.FromObject(AddCtyClaimDefaultValue(additionalInnerHeaderClaims))); @@ -591,6 +627,7 @@ private string CreateTokenPrivate(string payload, SigningCredentials signingCred { LogHelper.LogExceptionMessage(new SecurityTokenException(LogHelper.FormatInvariant(LogMessages.IDX14307, ex, payload))); } + payload = jsonPayload != null ? jsonPayload.ToString(Formatting.None) : payload; var rawPayload = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payload)); var message = rawHeader + "." + rawPayload; @@ -630,6 +667,13 @@ private static byte[] CompressToken(string token, string compressionAlgorithm) return compressionProvider.Compress(Encoding.UTF8.GetBytes(token)) ?? throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(TokenLogMessages.IDX10680, LogHelper.MarkAsNonPII(compressionAlgorithm)))); } + private static StringComparison GetStringComparisonRuleIf509(SecurityKey securityKey) => (securityKey is X509SecurityKey) + ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + + private static StringComparison GetStringComparisonRuleIf509OrECDsa(SecurityKey securityKey) => (securityKey is X509SecurityKey + || securityKey is ECDsaSecurityKey) + ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + /// /// Creates a from a . /// @@ -852,13 +896,14 @@ private static string EncryptTokenPrivate(string innerJwt, EncryptingCredentials throw LogHelper.LogExceptionMessage(new ArgumentException(TokenLogMessages.IDX10620)); byte[] wrappedKey = null; - SecurityKey securityKey = JwtTokenUtilities.GetSecurityKey(encryptingCredentials, cryptoProviderFactory, out wrappedKey); + SecurityKey securityKey = JwtTokenUtilities.GetSecurityKey(encryptingCredentials, cryptoProviderFactory, additionalHeaderClaims, out wrappedKey); using (var encryptionProvider = cryptoProviderFactory.CreateAuthenticatedEncryptionProvider(securityKey, encryptingCredentials.Enc)) { if (encryptionProvider == null) throw LogHelper.LogExceptionMessage(new SecurityTokenEncryptionFailedException(LogMessages.IDX14103)); + // we will have to make changes to creteing the JWE header and add the epk value as per https://datatracker.ietf.org/doc/html/rfc7518#section-4.1 and see example here: https://datatracker.ietf.org/doc/html/rfc7518#appendix-C var header = CreateDefaultJWEHeader(encryptingCredentials, compressionAlgorithm, tokenType); if (additionalHeaderClaims != null) @@ -909,6 +954,9 @@ internal IEnumerable GetContentEncryptionKeys(JsonWebToken jwtToken keys = new List { key }; } + // on decryption for ECDH-ES, we get the public key from the EPK value see: https://datatracker.ietf.org/doc/html/rfc7518#appendix-C + // we need the ECDSASecurityKey for the receiver, use TokenValidationParameters.TokenDecryptionKey + // control gets here if: // 1. User specified delegate: TokenDecryptionKeyResolver returned null // 2. ResolveTokenDecryptionKey returned null @@ -916,7 +964,8 @@ internal IEnumerable GetContentEncryptionKeys(JsonWebToken jwtToken if (keys == null) keys = JwtTokenUtilities.GetAllDecryptionKeys(validationParameters); - if (jwtToken.Alg.Equals(JwtConstants.DirectKeyUseAlg)) + if (jwtToken.Alg.Equals(JwtConstants.DirectKeyUseAlg, StringComparison.Ordinal) + || jwtToken.Alg.Equals(SecurityAlgorithms.EcdhEs, StringComparison.Ordinal)) return keys; var unwrappedKeys = new List(); @@ -927,6 +976,24 @@ internal IEnumerable GetContentEncryptionKeys(JsonWebToken jwtToken { try { +#if NET472 || NETCOREAPP3_1 + if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(jwtToken.Alg)) + { + //// on decryption we get the public key from the EPK value see: https://datatracker.ietf.org/doc/html/rfc7518#appendix-C + var ecdhKeyExchangeProvider = new EcdhKeyExchangeProvider( + key as ECDsaSecurityKey, + validationParameters.TokenDecryptionKey as ECDsaSecurityKey, + jwtToken.Alg, + jwtToken.Enc); + jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apu, out string apu); + jwtToken.TryGetHeaderValue(JwtHeaderParameterNames.Apv, out string apv); + SecurityKey kdf = ecdhKeyExchangeProvider.GenerateKdf(apu, apv); + var kwp = key.CryptoProviderFactory.CreateKeyWrapProviderForUnwrap(kdf, ecdhKeyExchangeProvider.GetEncryptionAlgorithm()); + var unwrappedKey = kwp.UnwrapKey(Base64UrlEncoder.DecodeBytes(jwtToken.EncryptedKey)); + unwrappedKeys.Add(new SymmetricSecurityKey(unwrappedKey)); + } + else +#endif if (key.CryptoProviderFactory.IsSupportedAlgorithm(jwtToken.Alg, key)) { var kwp = key.CryptoProviderFactory.CreateKeyWrapProviderForUnwrap(key, jwtToken.Alg); @@ -963,17 +1030,18 @@ protected virtual SecurityKey ResolveTokenDecryptionKey(string token, JsonWebTok if (validationParameters == null) throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + StringComparison stringComparison = GetStringComparisonRuleIf509OrECDsa(validationParameters.TokenDecryptionKey); if (!string.IsNullOrEmpty(jwtToken.Kid)) { if (validationParameters.TokenDecryptionKey != null - && string.Equals(validationParameters.TokenDecryptionKey.KeyId, jwtToken.Kid, validationParameters.TokenDecryptionKey is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + && string.Equals(validationParameters.TokenDecryptionKey.KeyId, jwtToken.Kid, stringComparison)) return validationParameters.TokenDecryptionKey; if (validationParameters.TokenDecryptionKeys != null) { foreach (var key in validationParameters.TokenDecryptionKeys) { - if (key != null && string.Equals(key.KeyId, jwtToken.Kid, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + if (key != null && string.Equals(key.KeyId, jwtToken.Kid, GetStringComparisonRuleIf509OrECDsa(key))) return key; } } @@ -983,7 +1051,7 @@ protected virtual SecurityKey ResolveTokenDecryptionKey(string token, JsonWebTok { if (validationParameters.TokenDecryptionKey != null) { - if (string.Equals(validationParameters.TokenDecryptionKey.KeyId, jwtToken.X5t, validationParameters.TokenDecryptionKey is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + if (string.Equals(validationParameters.TokenDecryptionKey.KeyId, jwtToken.X5t, stringComparison)) return validationParameters.TokenDecryptionKey; var x509Key = validationParameters.TokenDecryptionKey as X509SecurityKey; @@ -995,7 +1063,7 @@ protected virtual SecurityKey ResolveTokenDecryptionKey(string token, JsonWebTok { foreach (var key in validationParameters.TokenDecryptionKeys) { - if (key != null && string.Equals(key.KeyId, jwtToken.X5t, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + if (key != null && string.Equals(key.KeyId, jwtToken.X5t, GetStringComparisonRuleIf509(key))) return key; var x509Key = key as X509SecurityKey; @@ -1004,6 +1072,7 @@ protected virtual SecurityKey ResolveTokenDecryptionKey(string token, JsonWebTok } } } + return null; } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtHeaderParameterNames.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtHeaderParameterNames.cs index 01f58b747d..f23f335540 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtHeaderParameterNames.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtHeaderParameterNames.cs @@ -93,5 +93,20 @@ public struct JwtHeaderParameterNames /// See: https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.3 /// public const string Zip = "zip"; + + /// + /// See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.1.1 + /// + public const string Epk = "epk"; + + /// + /// See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.1.2 + /// + public const string Apu = "apu"; + + /// + /// See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.1.3 + /// + public const string Apv = "apv"; } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 854aabc271..a1039f4edc 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -279,6 +279,15 @@ public static byte[] GenerateKeyBytes(int sizeInBits) } internal static SecurityKey GetSecurityKey(EncryptingCredentials encryptingCredentials, CryptoProviderFactory cryptoProviderFactory, out byte[] wrappedKey) + { + return GetSecurityKey(encryptingCredentials, cryptoProviderFactory, null, out wrappedKey); + } + + internal static SecurityKey GetSecurityKey( + EncryptingCredentials encryptingCredentials, + CryptoProviderFactory cryptoProviderFactory, + IDictionary additionalHeaderClaims, + out byte[] wrappedKey) { SecurityKey securityKey = null; KeyWrapProvider kwProvider = null; @@ -292,6 +301,38 @@ internal static SecurityKey GetSecurityKey(EncryptingCredentials encryptingCrede securityKey = encryptingCredentials.Key; } +#if NET472 || NETCOREAPP3_1 + else if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(encryptingCredentials.Alg)) + { + // on decryption we get the public key from the EPK value see: https://datatracker.ietf.org/doc/html/rfc7518#appendix-C + string apu = null, apv = null; + if (additionalHeaderClaims != null && additionalHeaderClaims.Count > 0) + { + if (additionalHeaderClaims.TryGetValue(JwtHeaderParameterNames.Apu, out object objApu)) + apu = (objApu != null) ? objApu.ToString() : null; + + if (additionalHeaderClaims.TryGetValue(JwtHeaderParameterNames.Apv, out object objApv)) + apv = (objApv != null) ? objApv.ToString() : null; + } + + EcdhKeyExchangeProvider ecdhKeyExchangeProvider = new EcdhKeyExchangeProvider(encryptingCredentials.Key as ECDsaSecurityKey, encryptingCredentials.KeyExchangePublicKey, encryptingCredentials.Alg, encryptingCredentials.Enc); + SecurityKey kdf = ecdhKeyExchangeProvider.GenerateKdf(apu, apv); + kwProvider = cryptoProviderFactory.CreateKeyWrapProvider(kdf, ecdhKeyExchangeProvider.GetEncryptionAlgorithm()); + + // only 128, 384 and 512 AesKeyWrap for CEK algorithm + if (SecurityAlgorithms.Aes128KW.Equals(kwProvider.Algorithm, StringComparison.Ordinal)) + securityKey = new SymmetricSecurityKey(JwtTokenUtilities.GenerateKeyBytes(256)); + else if (SecurityAlgorithms.Aes192KW.Equals(kwProvider.Algorithm, StringComparison.Ordinal)) + securityKey = new SymmetricSecurityKey(JwtTokenUtilities.GenerateKeyBytes(384)); + else if (SecurityAlgorithms.Aes256KW.Equals(kwProvider.Algorithm, StringComparison.Ordinal)) + securityKey = new SymmetricSecurityKey(JwtTokenUtilities.GenerateKeyBytes(512)); + else + throw LogHelper.LogExceptionMessage( + new SecurityTokenEncryptionFailedException(LogHelper.FormatInvariant(TokenLogMessages.IDX10617, LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes128KW), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes192KW), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes256KW), LogHelper.MarkAsNonPII(kwProvider.Algorithm)))); + + wrappedKey = kwProvider.WrapKey(((SymmetricSecurityKey)securityKey).Key); + } +#endif else { if (!cryptoProviderFactory.IsSupportedAlgorithm(encryptingCredentials.Alg, encryptingCredentials.Key)) diff --git a/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs b/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs index 04fca859cf..c92ddf8699 100644 --- a/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs +++ b/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs @@ -59,6 +59,8 @@ public static class IdentityModelTelemetryUtil "ID_NET472"; #elif NETSTANDARD2_0 "ID_NETSTANDARD2_0"; +#elif NETCOREAPP3_1 + "ID_NETCOREAPP3_1"; #endif /// diff --git a/src/Microsoft.IdentityModel.Logging/LogHelper.cs b/src/Microsoft.IdentityModel.Logging/LogHelper.cs index 474e5a1434..91c96871b6 100644 --- a/src/Microsoft.IdentityModel.Logging/LogHelper.cs +++ b/src/Microsoft.IdentityModel.Logging/LogHelper.cs @@ -29,6 +29,8 @@ using System.Diagnostics.Tracing; using System.Globalization; using System.Linq; +using System.Reflection; +using Microsoft.IdentityModel.Abstractions; namespace Microsoft.IdentityModel.Logging { @@ -37,6 +39,36 @@ namespace Microsoft.IdentityModel.Logging /// public class LogHelper { + /// + /// Gets or sets a logger to which logs will be written to. + /// + public static IIdentityLogger Logger { get; set; } = NullIdentityModelLogger.Instance; + + /// + /// Indicates whether the log message header (contains library version, date/time, and PII debugging information) has been written. + /// + private static bool _isHeaderWritten { get; set; } = false; + + /// + /// The log message that indicates the current library version. + /// + private static string _versionLogMessage = "Microsoft.IdentityModel version: {0}. "; + + /// + /// The log message that indicates the date. + /// + private static string _dateLogMessage = "Date: {0}. "; + + /// + /// The log message that is shown when PII is off. + /// + private static string _piiOffLogMessage = "PII (personally identifiable information) logging is currently turned off. Set IdentityModelEventSource.ShowPII to 'true' to view the full details of exceptions. "; + + /// + /// The log message that is shown when PII is off. + /// + private static string _piiOnLogMessage = "PII (personally identifiable information) logging is currently turned on. Set IdentityModelEventSource.ShowPII to 'false' to hide PII from log messages. "; + /// /// Logs an exception using the event source logger and returns new exception. /// @@ -44,7 +76,7 @@ public class LogHelper /// EventLevel is set to Error. public static ArgumentNullException LogArgumentNullException(string argument) { - return LogArgumentException(EventLevel.Error, argument, "IDX10000: The parameter '{0}' cannot be a 'null' or an empty object.", argument); + return LogArgumentException(EventLevel.Error, argument, "IDX10000: The parameter '{0}' cannot be a 'null' or an empty object. ", argument); } /// @@ -254,6 +286,9 @@ public static Exception LogExceptionMessage(EventLevel eventLevel, Exception exc if (IdentityModelEventSource.Logger.IsEnabled() && IdentityModelEventSource.Logger.LogLevel >= eventLevel) IdentityModelEventSource.Logger.Write(eventLevel, exception.InnerException, exception.Message); + if (Logger.IsEnabled(eventLevel)) + Logger.Log(WriteEntry(eventLevel, exception.InnerException, exception.Message, null)); + return exception; } @@ -264,8 +299,11 @@ public static Exception LogExceptionMessage(EventLevel eventLevel, Exception exc /// An object array that contains zero or more objects to format. public static void LogInformation(string message, params object[] args) { - if (IdentityModelEventSource.Logger.IsEnabled()) + if (IdentityModelEventSource.Logger.IsEnabled() && IdentityModelEventSource.Logger.LogLevel >= EventLevel.Informational) IdentityModelEventSource.Logger.WriteInformation(message, args); + + if (Logger.IsEnabled(EventLevel.Informational)) + Logger.Log(WriteEntry(EventLevel.Informational, null, message, args)); } /// @@ -277,6 +315,9 @@ public static void LogVerbose(string message, params object[] args) { if (IdentityModelEventSource.Logger.IsEnabled()) IdentityModelEventSource.Logger.WriteVerbose(message, args); + + if (Logger.IsEnabled(EventLevel.Verbose)) + Logger.Log(WriteEntry(EventLevel.Verbose, null, message, args)); } /// @@ -288,6 +329,9 @@ public static void LogWarning(string message, params object[] args) { if (IdentityModelEventSource.Logger.IsEnabled()) IdentityModelEventSource.Logger.WriteWarning(message, args); + + if (Logger.IsEnabled(EventLevel.Warning)) + Logger.Log(WriteEntry(EventLevel.Warning, null, message, args)); } /// @@ -310,6 +354,9 @@ private static T LogExceptionImpl(EventLevel eventLevel, string argumentName, if (IdentityModelEventSource.Logger.IsEnabled() && IdentityModelEventSource.Logger.LogLevel >= eventLevel) IdentityModelEventSource.Logger.Write(eventLevel, innerException, message); + if (Logger.IsEnabled(eventLevel)) + Logger.Log(WriteEntry(eventLevel, innerException, message, null)); + if (innerException != null) if (string.IsNullOrEmpty(argumentName)) return (T)Activator.CreateInstance(typeof(T), message, innerException); @@ -371,5 +418,50 @@ public static object MarkAsNonPII(object arg) { return new NonPII(arg); } + + /// + /// Creates a by using the provided event level, exception argument, string argument and arguments list. + /// + /// + /// + /// The log message. + /// An object array that contains zero or more objects to format. + private static LogEntry WriteEntry(EventLevel level, Exception innerException, string message, params object[] args) + { + if (string.IsNullOrEmpty(message)) + return null; + + if (innerException != null) + { + // if PII is turned off and 'innerException' is a System exception only display the exception type + if (!IdentityModelEventSource.ShowPII && !LogHelper.IsCustomException(innerException)) + message = string.Format(CultureInfo.InvariantCulture, "Message: {0}, InnerException: {1}. ", message, innerException.GetType()); + else // otherwise it's safe to display the entire exception message + message = string.Format(CultureInfo.InvariantCulture, "Message: {0}, InnerException: {1}. ", message, innerException.Message); + } + + // Logs basic information (library version, DateTime, whether PII is ON/OFF) once before any log messages are written. + if (!_isHeaderWritten) + { + _isHeaderWritten = true; + + LogEntry headerEntry = new LogEntry(); + headerEntry.EventLevel = EventLevel.LogAlways; + headerEntry.Message = string.Format(CultureInfo.InvariantCulture, _versionLogMessage, typeof(IdentityModelEventSource).GetTypeInfo().Assembly.GetName().Version.ToString()); + Logger.Log(headerEntry); + + headerEntry.Message = string.Format(CultureInfo.InvariantCulture, _dateLogMessage, DateTime.UtcNow); + Logger.Log(headerEntry); + + headerEntry.Message = IdentityModelEventSource.ShowPII ? _piiOnLogMessage : _piiOffLogMessage; + Logger.Log(headerEntry); + } + + LogEntry entry = new LogEntry(); + entry.EventLevel = level; + entry.Message = args != null ? FormatInvariant(message, args) : message; + + return entry; + } } } diff --git a/src/Microsoft.IdentityModel.Logging/Microsoft.IdentityModel.Logging.csproj b/src/Microsoft.IdentityModel.Logging/Microsoft.IdentityModel.Logging.csproj index 5c9a54d36a..7d92669e5d 100644 --- a/src/Microsoft.IdentityModel.Logging/Microsoft.IdentityModel.Logging.csproj +++ b/src/Microsoft.IdentityModel.Logging/Microsoft.IdentityModel.Logging.csproj @@ -26,4 +26,8 @@ + + + + diff --git a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj index f427c7885e..aaaa2d1951 100644 --- a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj +++ b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.Protocols.WsFederation/GlobalSuppressions.cs index dc2dbb01cc..e95ed268c4 100644 --- a/src/Microsoft.IdentityModel.Protocols.WsFederation/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/GlobalSuppressions.cs @@ -31,3 +31,7 @@ [assembly: SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Doesn't own object", Scope = "member", Target = "~M:Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMetadataSerializer.ReadEntityDescriptor(System.Xml.XmlReader)~Microsoft.IdentityModel.Protocols.WsFederation.WsFederationConfiguration")] [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Breaking change", Scope = "member", Target = "~P:Microsoft.IdentityModel.Protocols.WsFederation.SecurityTokenServiceTypeRoleDescriptor.KeyInfos")] +#if NETCOREAPP3_1 +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Protocols.WsFederation.QueryHelper.ParseNullableQuery(System.String)~System.Collections.Generic.IDictionary{System.String,System.Collections.Generic.IList{System.String}}")] +#endif + diff --git a/src/Microsoft.IdentityModel.Protocols.WsFederation/Microsoft.IdentityModel.Protocols.WsFederation.csproj b/src/Microsoft.IdentityModel.Protocols.WsFederation/Microsoft.IdentityModel.Protocols.WsFederation.csproj index 248ae24373..b1a70f7884 100644 --- a/src/Microsoft.IdentityModel.Protocols.WsFederation/Microsoft.IdentityModel.Protocols.WsFederation.csproj +++ b/src/Microsoft.IdentityModel.Protocols.WsFederation/Microsoft.IdentityModel.Protocols.WsFederation.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs index dde466a6d1..ea797c6d7f 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs @@ -163,7 +163,7 @@ public async Task GetConfigurationAsync(CancellationToken cancel) if (_configValidator != null) { ConfigurationValidationResult result = _configValidator.Validate(configuration); - if (!result.Succeeded) + if (!result.Succeeded) LogHelper.LogWarning(LogMessages.IDX20810, result.ErrorMessage); } diff --git a/src/Microsoft.IdentityModel.Protocols/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.Protocols/GlobalSuppressions.cs index e79eb2c67c..dd628eadeb 100644 --- a/src/Microsoft.IdentityModel.Protocols/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.Protocols/GlobalSuppressions.cs @@ -9,3 +9,6 @@ [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Previously released as returning an array", Scope = "member", Target = "~P:Microsoft.IdentityModel.Protocols.HttpRequestData.Body")] [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Previously released read/write", Scope = "member", Target = "~P:Microsoft.IdentityModel.Protocols.HttpRequestData.Headers")] [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Previously released read/write", Scope = "member", Target = "~P:Microsoft.IdentityModel.Protocols.HttpRequestData.PropertyBag")] +#if NETCOREAPP3_1 +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Protocols.AuthenticationProtocolMessage.BuildRedirectUrl~System.String")] +#endif diff --git a/src/Microsoft.IdentityModel.TestExtensions/Microsoft.IdentityModel.TestExtensions.csproj b/src/Microsoft.IdentityModel.TestExtensions/Microsoft.IdentityModel.TestExtensions.csproj index c5895d612d..8948aa7508 100644 --- a/src/Microsoft.IdentityModel.TestExtensions/Microsoft.IdentityModel.TestExtensions.csproj +++ b/src/Microsoft.IdentityModel.TestExtensions/Microsoft.IdentityModel.TestExtensions.csproj @@ -1,6 +1,6 @@ - + Microsoft.IdentityModel.TestExtensions diff --git a/src/Microsoft.IdentityModel.TestExtensions/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.TestExtensions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..37fd5aeab4 --- /dev/null +++ b/src/Microsoft.IdentityModel.TestExtensions/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyInformationalVersion("0.0.1")] +[assembly: AssemblyFileVersion("0.0.1")] +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: AssemblyVersion("0.0.1")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] diff --git a/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs b/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs index 870f02bb35..0e45d9f58a 100644 --- a/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs +++ b/src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs @@ -33,7 +33,7 @@ using System.Reflection; #endif -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 using System.Security.Cryptography.X509Certificates; #endif @@ -235,7 +235,7 @@ private void InitializeUsingRsa(RSA rsa, string algorithm) } #endif -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 if (algorithm.Equals(SecurityAlgorithms.RsaSsaPssSha256) || algorithm.Equals(SecurityAlgorithms.RsaSsaPssSha256Signature) || algorithm.Equals(SecurityAlgorithms.RsaSsaPssSha384) || @@ -270,7 +270,7 @@ private void InitializeUsingRsaSecurityKey(RsaSecurityKey rsaSecurityKey, string } else { -#if NET472 +#if NET472 || NETCOREAPP3_1 var rsa = RSA.Create(rsaSecurityKey.Parameters); #else var rsa = RSA.Create(); @@ -323,8 +323,8 @@ private bool VerifyWithECDsa(byte[] bytes, byte[] signature) return ECDsa.VerifyHash(HashAlgorithm.ComputeHash(bytes), signature); } -#region NET61+ related code -#if NET461 || NET472 || NETSTANDARD2_0 + #region NET61+ related code +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 // HasAlgorithmName was introduced into Net46 internal AsymmetricAdapter(SecurityKey key, string algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, bool requirePrivateKey) : this(key, algorithm, hashAlgorithm, requirePrivateKey) diff --git a/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs b/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs index dc1cd213d7..25a88d696b 100644 --- a/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/AsymmetricSignatureProvider.cs @@ -185,8 +185,8 @@ private static PrivateKeyStatus FoundPrivateKey(SecurityKey key) return PrivateKeyStatus.Unknown; } - -#if NET461 || NET472 || NETSTANDARD2_0 + +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// Creating a Signature requires the use of a . /// This method returns the diff --git a/src/Microsoft.IdentityModel.Tokens/CallContext.cs b/src/Microsoft.IdentityModel.Tokens/CallContext.cs index 359d94d067..5e1195377d 100644 --- a/src/Microsoft.IdentityModel.Tokens/CallContext.cs +++ b/src/Microsoft.IdentityModel.Tokens/CallContext.cs @@ -28,7 +28,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics.Tracing; namespace Microsoft.IdentityModel.Tokens { diff --git a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs index c206c468b5..b953b18c61 100644 --- a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs @@ -342,7 +342,7 @@ public virtual SignatureProvider CreateForVerifying(SecurityKey key, string algo return CreateSignatureProvider(key, algorithm, false, cacheProvider); } -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// Creates a for a specific algorithm. /// diff --git a/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs b/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs index 8a566076cd..d40690b0e0 100644 --- a/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs +++ b/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs @@ -52,7 +52,7 @@ internal class ECDsaAdapter /// internal ECDsaAdapter() { -#if NET472 +#if NET472 || NETCOREAPP3_1 CreateECDsaFunction = CreateECDsaUsingECParams; #elif NETSTANDARD2_0 // Although NETSTANDARD2_0 specifies that ECParameters are supported, we still need to call SupportsECParameters() @@ -278,7 +278,7 @@ private static bool SupportsCNGKey() } } -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// Creates an ECDsa object using the and . /// 'ECParameters' structure is available in .NET Framework 4.7+, .NET Standard 1.6+, and .NET Core 1.0+. @@ -366,7 +366,7 @@ internal static string GetCrvParameterValue(ECCurve curve) /// True if structure is supported, false otherwise. internal static bool SupportsECParameters() { -#if NET472 +#if NET472 || NETCOREAPP3_1 return true; #else try diff --git a/src/Microsoft.IdentityModel.Tokens/ECDsaSecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/ECDsaSecurityKey.cs index 11cb930038..355d69a72b 100644 --- a/src/Microsoft.IdentityModel.Tokens/ECDsaSecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/ECDsaSecurityKey.cs @@ -116,7 +116,7 @@ public override int KeySize /// https://datatracker.ietf.org/doc/html/rfc7638 public override bool CanComputeJwkThumbprint() { -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 if (ECDsaAdapter.SupportsECParameters()) return true; #endif @@ -130,7 +130,7 @@ public override bool CanComputeJwkThumbprint() /// https://datatracker.ietf.org/doc/html/rfc7638 public override byte[] ComputeJwkThumbprint() { -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 if (ECDsaAdapter.SupportsECParameters()) { ECParameters parameters = ECDsa.ExportParameters(false); diff --git a/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs b/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs index 5e0db2b8d5..93344ffc12 100644 --- a/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs +++ b/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs @@ -109,6 +109,11 @@ public string Enc private set => _enc = string.IsNullOrEmpty(value) ? throw LogHelper.LogArgumentNullException("enc") : value; } + /// + /// Public key used in Key Agreement Algorithms + /// + public SecurityKey KeyExchangePublicKey { get; set; } + /// /// Users can override the default with this property. This factory will be used for creating encryption providers. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Encryption/EcdhKeyExchangeProvider.cs b/src/Microsoft.IdentityModel.Tokens/Encryption/EcdhKeyExchangeProvider.cs new file mode 100644 index 0000000000..07e1260df2 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Encryption/EcdhKeyExchangeProvider.cs @@ -0,0 +1,244 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System; +using System.Text; +using System.Security.Cryptography; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ +#if NET472 || NETCOREAPP3_1 + /// + /// Provides a Security Key that can be used as Content Encryption Key (CEK) for use with a JWE + /// + public class EcdhKeyExchangeProvider + { + /// + /// Number of bits in the desired output key + /// + public int KeyDataLen { get; set; } + + private ECDiffieHellman _ecdhPublic; + private ECDiffieHellman _ecdhPrivate; + private ECParameters _ecParamsPublic; + private ECParameters _ecParamsPrivate; + private string _algorithmId; + + /// + /// Initializes a new instance of used for CEKs + /// The that will be used for cryptographic operations and represents the private key. + /// The that will be used for cryptographic operations and represents the public key. + /// alg header parameter value. + /// enc header parameter value. + /// + public EcdhKeyExchangeProvider(SecurityKey privateKey, SecurityKey publicKey, string alg, string enc) + { + if (privateKey == null) + throw LogHelper.LogArgumentNullException(nameof(privateKey)); + + if (publicKey is null) + throw LogHelper.LogArgumentNullException(nameof(publicKey)); + + ValidateAlgAndEnc(alg, enc); + SetKeyDataLenAndEncryptionAlgorithm(alg, enc); + _ecParamsPublic = GetECParametersFromKey(publicKey, false, nameof(publicKey)); + _ecParamsPrivate = GetECParametersFromKey(privateKey, true, nameof(privateKey)); + ValidateCurves(nameof(privateKey), nameof(publicKey)); + _ecdhPublic = ECDiffieHellman.Create(_ecParamsPublic); + _ecdhPrivate = ECDiffieHellman.Create(_ecParamsPrivate); + } + + /// + /// Generates the KDF + /// + /// Agreement PartyUInfo (optional). When used, the PartyVInfo value contains information about the producer, + /// represented as a base64url-encoded string. + /// Agreement PartyVInfo (optional). When used, the PartyUInfo value contains information about the recipient, + /// represented as a base64url-encoded string. + /// Returns that represents the key generated + public SecurityKey GenerateKdf(string apu = null, string apv = null) + { + //The "apu" and "apv" values MUST be distinct when used (per rfc7518 section 4.6.2) https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 + if (!string.IsNullOrEmpty(apu) && !string.IsNullOrEmpty(apv) && apu.Equals(apv)) + throw LogHelper.LogArgumentException( + nameof(apu), + LogHelper.FormatInvariant( + LogMessages.IDX11001, + LogHelper.MarkAsNonPII(nameof(apu)), + LogHelper.MarkAsNonPII(apu), + LogHelper.MarkAsNonPII(nameof(apv)), + LogHelper.MarkAsNonPII(apv)) + ); + + int kdfLength = KeyDataLen / 8; // number of octets + // prepend bytes that represent n = ceiling of (keydatalen / hashlen), see section 5.8.1.1: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf + // hashlen is always 256 for ecdh-es, see: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 + // for supported algorithms it is always '1', for saml might be different + byte[] prepend = new byte[4] { 0, 0, 0, 1 }; + SetAppendBytes(apu, apv, out byte[] append); + byte[] kdf = new byte[kdfLength]; + + // JWA's spec https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 specifies SHA256, saml might be different + byte[] derivedKey = _ecdhPrivate.DeriveKeyFromHash(_ecdhPublic.PublicKey, HashAlgorithmName.SHA256, prepend, append); + Array.Copy(derivedKey, kdf, kdfLength); + + return new SymmetricSecurityKey(kdf); + } + + private void SetAppendBytes(string apu, string apv, out byte[] append) + { + byte[] encBytes = Encoding.ASCII.GetBytes(_algorithmId); + byte[] apuBytes = Base64UrlEncoder.DecodeBytes(string.IsNullOrEmpty(apu) ? string.Empty : apu); + byte[] apvBytes = Base64UrlEncoder.DecodeBytes(string.IsNullOrEmpty(apv) ? string.Empty : apv); + byte[] numOctetsEnc = BitConverter.GetBytes(encBytes.Length); + byte[] numOctetsApu = BitConverter.GetBytes(apuBytes.Length); + byte[] numOctetsApv = BitConverter.GetBytes(apvBytes.Length); + byte[] keyDataLengthBytes = BitConverter.GetBytes(KeyDataLen); + + if (BitConverter.IsLittleEndian) + { + // these representations need to be big-endian + Array.Reverse(numOctetsEnc); + Array.Reverse(numOctetsApu); + Array.Reverse(numOctetsApv); + Array.Reverse(keyDataLengthBytes); + } + + append = Concat(numOctetsEnc, encBytes, numOctetsApu, apuBytes, numOctetsApv, apvBytes, keyDataLengthBytes); + } + + private void SetKeyDataLenAndEncryptionAlgorithm(string alg, string enc = null) + { + if (SecurityAlgorithms.EcdhEs.Equals(alg, StringComparison.InvariantCulture)) + { + _algorithmId = enc; + if (SecurityAlgorithms.Aes128Gcm.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 128; + else if (SecurityAlgorithms.Aes192Gcm.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 192; + else if (SecurityAlgorithms.Aes256Gcm.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 256; + else if (SecurityAlgorithms.Aes128CbcHmacSha256.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 128; + else if (SecurityAlgorithms.Aes192CbcHmacSha384.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 192; + else if (SecurityAlgorithms.Aes256CbcHmacSha512.Equals(enc, StringComparison.InvariantCulture)) + KeyDataLen = 256; + } + else + { + _algorithmId = alg; + + if (SecurityAlgorithms.EcdhEsA128kw.Equals(alg, StringComparison.InvariantCulture)) + KeyDataLen = 128; + else if (SecurityAlgorithms.EcdhEsA192kw.Equals(alg, StringComparison.InvariantCulture)) + KeyDataLen = 192; + else if (SecurityAlgorithms.EcdhEsA256kw.Equals(alg, StringComparison.InvariantCulture)) + KeyDataLen = 256; + } + } + + private static void ValidateAlgAndEnc(string alg, string enc) + { + if (string.IsNullOrEmpty(alg)) + throw LogHelper.LogArgumentNullException(alg); + if (string.IsNullOrEmpty(enc)) + throw LogHelper.LogArgumentNullException(enc); + + if (!SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(alg) && !SecurityAlgorithms.EcdhEs.Equals(alg, StringComparison.InvariantCulture)) + throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10652, LogHelper.MarkAsNonPII(alg)))); + + if (!SupportedAlgorithms.SymmetricEncryptionAlgorithms.Contains(enc)) + throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10715, LogHelper.MarkAsNonPII(enc)))); + } + + private void ValidateCurves(string privateKeyArgName, string publicKeyArgName) + { + if (_ecParamsPrivate.Curve.Equals(_ecParamsPublic.Curve)) + { + throw LogHelper.LogArgumentException( + privateKeyArgName, + LogHelper.FormatInvariant( + LogMessages.IDX11000, + LogHelper.MarkAsNonPII(privateKeyArgName), + LogHelper.MarkAsNonPII(_ecParamsPrivate.Curve.ToString()), + LogHelper.MarkAsNonPII(publicKeyArgName), + LogHelper.MarkAsNonPII(_ecParamsPublic.Curve.ToString())) + ); + } + } + + private static ECParameters GetECParametersFromKey(SecurityKey key, bool isPrivate, string nameOfKey) + { + if (key is ECDsaSecurityKey ecdsaKey) + { + return ecdsaKey.ECDsa.ExportParameters(isPrivate); + } + else if (key is JsonWebKey jwk + && JsonWebKeyConverter.TryConvertToECDsaSecurityKey(jwk, out SecurityKey securityKey)) + { + return ((ECDsaSecurityKey)securityKey).ECDsa.ExportParameters(isPrivate); + } + else + { + throw LogHelper.LogArgumentException( + nameOfKey, + LogHelper.FormatInvariant(LogMessages.IDX11002, LogHelper.MarkAsNonPII(nameOfKey))); + } + } + + private static byte[] Concat(params byte[][] arrays) + { + int outputLength = 0; + foreach (byte[] arr in arrays) + outputLength += arr.Length; + + byte[] output = new byte[outputLength]; + int x = 0; + foreach (byte[] arr in arrays) + { + Array.Copy(arr, 0, output, x, arr.Length); + x += arr.Length; + } + + return output; + } + + internal string GetEncryptionAlgorithm() + { + if (_algorithmId.Equals(SecurityAlgorithms.EcdhEsA128kw, StringComparison.Ordinal)) + return SecurityAlgorithms.Aes128KW; + if (_algorithmId.Equals(SecurityAlgorithms.EcdhEsA192kw, StringComparison.Ordinal)) + return SecurityAlgorithms.Aes192KW; + if (_algorithmId.Equals(SecurityAlgorithms.EcdhEsA256kw, StringComparison.Ordinal)) + return SecurityAlgorithms.Aes256KW; + return _algorithmId; + } + } +#endif +} diff --git a/src/Microsoft.IdentityModel.Tokens/Encryption/SymmetricKeyWrapProvider.cs b/src/Microsoft.IdentityModel.Tokens/Encryption/SymmetricKeyWrapProvider.cs index e27870a6b2..ff79d0c9b6 100644 --- a/src/Microsoft.IdentityModel.Tokens/Encryption/SymmetricKeyWrapProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/Encryption/SymmetricKeyWrapProvider.cs @@ -345,6 +345,14 @@ private void ValidateKeySize(byte[] key, string algorithm) return; } + if (SecurityAlgorithms.Aes192KW.Equals(algorithm) || SecurityAlgorithms.Aes192KeyWrap.Equals(algorithm)) + { + if (key.Length != 24) + throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(key), LogHelper.FormatInvariant(LogMessages.IDX10662, LogHelper.MarkAsNonPII(algorithm), LogHelper.MarkAsNonPII(128), Key.KeyId, LogHelper.MarkAsNonPII(key.Length << 3)))); + + return; + } + if (SecurityAlgorithms.Aes256KW.Equals(algorithm) || (SecurityAlgorithms.Aes256KeyWrap.Equals(algorithm))) { if (key.Length != 32) diff --git a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs index 2f19b51bc4..49aef307ea 100644 --- a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs +++ b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs @@ -76,7 +76,7 @@ protected SecurityTokenException(SerializationInfo info, StreamingContext contex { } -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// When overridden in a derived class, sets the System.Runtime.Serialization.SerializationInfo /// with information about the exception. diff --git a/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs index dc503193ff..05cab2360d 100644 --- a/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs @@ -56,8 +56,10 @@ [assembly: SuppressMessage("Design", "CA1001:Types That own disposable fields should be disposable", Justification = "Exceptions can occurr if disposed of", Scope = "type", Target = "~T:Microsoft.IdentityModel.Tokens.EventBasedLRUCache`2")] [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Consistency", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.AdditionalInnerHeaderClaims")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as validation", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt(Microsoft.IdentityModel.Tokens.SecurityToken,System.Nullable{System.DateTime},System.Nullable{System.DateTime},System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration,System.Text.StringBuilder,System.Int32,System.Int32)")] -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used to determine appropriate code path to take.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.ConvertFromECDsaSecurityKey(Microsoft.IdentityModel.Tokens.ECDsaSecurityKey)~Microsoft.IdentityModel.Tokens.JsonWebKey")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used to determine appropriate code path to take.", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.EcdhSecurityKey.PrivateKeyStatus")] +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.EcdhKeyExchangeProvider.GenerateKdf(System.String,System.String)~Microsoft.IdentityModel.Tokens.SecurityKey")] #endif #if !NET472 [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as validation", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.ECDsaAdapter.SupportsECParameters~System.Boolean")] diff --git a/src/Microsoft.IdentityModel.Tokens/JsonWebKeyConverter.cs b/src/Microsoft.IdentityModel.Tokens/JsonWebKeyConverter.cs index 8a1e4dbb2f..8a8216e88f 100644 --- a/src/Microsoft.IdentityModel.Tokens/JsonWebKeyConverter.cs +++ b/src/Microsoft.IdentityModel.Tokens/JsonWebKeyConverter.cs @@ -57,7 +57,7 @@ public static JsonWebKey ConvertFromSecurityKey(SecurityKey key) return ConvertFromSymmetricSecurityKey(symmetricKey); else if (key is X509SecurityKey x509Key) return ConvertFromX509SecurityKey(x509Key); -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 else if (key is ECDsaSecurityKey ecdsaSecurityKey) return ConvertFromECDsaSecurityKey(ecdsaSecurityKey); #endif @@ -181,7 +181,7 @@ public static JsonWebKey ConvertFromSymmetricSecurityKey(SymmetricSecurityKey ke }; } -#if NET472 || NETSTANDARD2_0 +#if NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// Converts a into a /// diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index 96ae25e1e8..4c3adb1d4c 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -260,6 +260,11 @@ internal static class LogMessages public const string IDX10900 = "IDX10900: EventBasedLRUCache._eventQueue encountered an error while processing a cache operation. Exception '{0}'."; public const string IDX10901 = "IDX10901: CryptoProviderCacheOptions.SizeLimit must be greater than 10. Value: '{0}'"; public const string IDX10902 = "IDX10902: Object disposed exception in '{0}': '{1}'"; + + // Crypto Errors + public const string IDX11000 = "IDX11000: Cannot create EcdhKeyExchangeProvider. '{0}'\'s Curve '{1}' does not match with '{2}'\'s curve '{3}'."; + public const string IDX11001 = "IDX11001: Cannot generate KDF. '{0}':'{1}' and '{2}':'{3}' must be different."; + public const string IDX11002 = "IDX11002: Cannot create the EcdhKeyExchangeProvider. Unable to obtain ECParameters from {0}. Verify the SecurityKey is an ECDsaSecurityKey or JsonWebKey and that properties Crv, X, Y, and D (if used for a private key) are contained in the provided SecurityKey."; #pragma warning restore 1591 } } diff --git a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj index d54ff4ea3c..615bd32a84 100644 --- a/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj +++ b/src/Microsoft.IdentityModel.Tokens/Microsoft.IdentityModel.Tokens.csproj @@ -16,7 +16,7 @@ $(DefineConstants);TRACE;DESKTOP;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CAS;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FAST_REVERSE;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_EMIT;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY; - + $(DefineConstants);TRACE;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FAST_REVERSE;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;$(AdditionalConstants) @@ -30,7 +30,7 @@ - + diff --git a/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs index eacd408112..a3b4dcfee7 100644 --- a/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs @@ -100,7 +100,7 @@ public override bool HasPrivateKey { // imitate signing byte[] hash = new byte[20]; -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 Rsa.SignData(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); #else if (Rsa is RSACryptoServiceProvider rsaCryptoServiceProvider) diff --git a/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs b/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs index bdbafc8e04..25947ca389 100644 --- a/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs +++ b/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs @@ -51,6 +51,7 @@ public static class SecurityAlgorithms // See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.1 public const string Aes128KW = "A128KW"; + public const string Aes192KW = "A192KW"; public const string Aes256KW = "A256KW"; public const string RsaPKCS1 = "RSA1_5"; public const string RsaOAEP = "RSA-OAEP"; @@ -119,6 +120,13 @@ public static class SecurityAlgorithms internal const string DefaultAsymmetricKeyWrapAlgorithm = RsaOaepKeyWrap; internal const string DefaultSymmetricEncryptionAlgorithm = Aes128CbcHmacSha256; + // See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6 + public const string EcdhEsA128kw = "ECDH-ES+A128KW"; + public const string EcdhEsA192kw = "ECDH-ES+A192KW"; + public const string EcdhEsA256kw = "ECDH-ES+A256KW"; + + // See: https://datatracker.ietf.org/doc/html/rfc7518#section-4.6 + public const string EcdhEs = "ECDH-ES"; #pragma warning restore 1591 } } diff --git a/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs b/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs index a11545c8cf..1bd5656ecd 100644 --- a/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs +++ b/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs @@ -38,6 +38,10 @@ namespace Microsoft.IdentityModel.Tokens /// internal static class SupportedAlgorithms { + //there might be a better place to put these values + private const int EcdsaMinKeySize = 256; + private const int RsaMinKeySize = 2048; + internal static readonly ICollection EcdsaSigningAlgorithms = new Collection { SecurityAlgorithms.EcdsaSha256, @@ -100,8 +104,13 @@ internal static class SupportedAlgorithms { SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128KeyWrap, + SecurityAlgorithms.Aes192KW, + SecurityAlgorithms.Aes192KeyWrap, SecurityAlgorithms.Aes256KW, - SecurityAlgorithms.Aes256KeyWrap + SecurityAlgorithms.Aes256KeyWrap, + SecurityAlgorithms.EcdhEsA128kw, + SecurityAlgorithms.EcdhEsA192kw, + SecurityAlgorithms.EcdhEsA256kw }; internal static readonly ICollection SymmetricSigningAlgorithms = new Collection @@ -114,7 +123,14 @@ internal static class SupportedAlgorithms SecurityAlgorithms.HmacSha512Signature }; -#if NET461 || NET472 || NETSTANDARD2_0 + internal static readonly ICollection EcdsaWrapAlgorithms = new Collection + { + SecurityAlgorithms.EcdhEsA128kw, + SecurityAlgorithms.EcdhEsA192kw, + SecurityAlgorithms.EcdhEsA256kw + }; + +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 /// /// Creating a Signature requires the use of a . /// This method returns the @@ -317,7 +333,7 @@ internal static bool IsSupportedRsaKeyWrap(string algorithm, SecurityKey key) return false; if (key is RsaSecurityKey || key is X509SecurityKey || (key is JsonWebKey rsaJsonWebKey && rsaJsonWebKey.Kty == JsonWebAlgorithmsKeyTypes.RSA)) - return key.KeySize >= 2048; + return key.KeySize >= RsaMinKeySize; return false; } @@ -336,6 +352,24 @@ internal static bool IsSupportedSymmetricKeyWrap(string algorithm, SecurityKey k return (key is SymmetricSecurityKey || (key is JsonWebKey jsonWebKey && jsonWebKey.Kty == JsonWebAlgorithmsKeyTypes.Octet)); } + internal static bool IsSupportedEcdsaKeyWrap(string algorithm, SecurityKey key) + { + if (key == null) + return false; + + if (string.IsNullOrEmpty(algorithm)) + return false; + + if (!EcdsaWrapAlgorithms.Contains(algorithm)) + return false; + + if (key is ECDsaSecurityKey || (key is JsonWebKey ecdJsonWebKey && ecdJsonWebKey.Kty == JsonWebAlgorithmsKeyTypes.EllipticCurve)) + return key.KeySize >= EcdsaMinKeySize; + + // todo: check if curve is in approved curves (P-256, P-384, or P-521) - only these 3 + return false; + } + internal static bool IsSupportedRsaAlgorithm(string algorithm, SecurityKey key) { return RsaSigningAlgorithms.Contains(algorithm) @@ -349,7 +383,7 @@ private static bool IsSupportedRsaPss(SecurityKey key) // RSA-PSS is not available on .NET 4.5 LogHelper.LogInformation(LogMessages.IDX10692); return false; -#elif NET461 || NET472 || NETSTANDARD2_0 +#elif NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 // RSACryptoServiceProvider doesn't support RSA-PSS if (key is RsaSecurityKey rsa && rsa.Rsa is RSACryptoServiceProvider) { diff --git a/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs index c5eca70c76..b291029ce3 100644 --- a/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs @@ -102,7 +102,7 @@ public AsymmetricAlgorithm PrivateKey { if (!_privateKeyAvailabilityDetermined) { -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 _privateKey = RSACertificateExtensions.GetRSAPrivateKey(Certificate); #else _privateKey = Certificate.PrivateKey; @@ -129,7 +129,7 @@ public AsymmetricAlgorithm PublicKey { if (_publicKey == null) { -#if NET461 || NET472 || NETSTANDARD2_0 +#if NET461 || NET472 || NETSTANDARD2_0 || NETCOREAPP3_1 _publicKey = RSACertificateExtensions.GetRSAPublicKey(Certificate); #else _publicKey = Certificate.PublicKey.Key; diff --git a/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/DefaultContractResolver.cs b/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/DefaultContractResolver.cs index 90d06a3ed9..de93ca4c12 100644 --- a/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/DefaultContractResolver.cs +++ b/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/DefaultContractResolver.cs @@ -1377,7 +1377,7 @@ protected virtual IValueProvider CreateMemberValueProvider(MemberInfo member) // warning - this method use to cause errors with Intellitrace. Retest in VS Ultimate after changes IValueProvider valueProvider; -#if !(PORTABLE40 || PORTABLE || DOTNET || NETSTANDARD2_0) +#if !(PORTABLE40 || PORTABLE || DOTNET || NETSTANDARD2_0 || NETCOREAPP3_1) if (DynamicCodeGeneration) { valueProvider = new DynamicValueProvider(member); @@ -1700,4 +1700,4 @@ public string GetResolvedPropertyName(string propertyName) return ResolvePropertyName(propertyName); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/JsonTypeReflector.cs b/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/JsonTypeReflector.cs index 9300178d03..93e860a6d0 100644 --- a/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/JsonTypeReflector.cs +++ b/src/Microsoft.IdentityModel.Tokens/opensource/json/Serialization/JsonTypeReflector.cs @@ -515,7 +515,7 @@ public static ReflectionDelegateFactory ReflectionDelegateFactory { get { -#if !(PORTABLE40 || PORTABLE || DOTNET || NETSTANDARD2_0) +#if !(PORTABLE40 || PORTABLE || DOTNET || NETSTANDARD2_0 || NETCOREAPP3_1) if (DynamicCodeGeneration) { return DynamicReflectionDelegateFactory.Instance; @@ -528,4 +528,4 @@ public static ReflectionDelegateFactory ReflectionDelegateFactory } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.IdentityModel.Validators/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.Validators/GlobalSuppressions.cs index f7bdda6d6e..efc6bd454b 100644 --- a/src/Microsoft.IdentityModel.Validators/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.Validators/GlobalSuppressions.cs @@ -6,3 +6,9 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Needs to be ignored", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.IsValidIssuer(System.String,System.String,System.String)~System.Boolean")] +#if NETCOREAPP3_1 +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.CreateV1Authority(System.String)~System.String")] +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.IsValidIssuer(System.String,System.String,System.String)~System.Boolean")] +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Adding StringComparison.Ordinal adds a performance penalty.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Validators.AadIssuerValidator.#ctor(System.Net.Http.HttpClient,System.String)")] +#endif + diff --git a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs index 1db8ec5889..fbcdd7a642 100644 --- a/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs +++ b/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs @@ -362,9 +362,24 @@ public virtual string CreateEncodedJwt(SecurityTokenDescriptor tokenDescriptor) /// If is provided, then a JWS will be created. /// /// A Base64UrlEncoded string in 'Compact Serialization Format'. - public virtual string CreateEncodedJwt(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials) + public virtual string CreateEncodedJwt( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, null, null, null, null, null).RawData; + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, + null, null, null, null, null).RawData; } /// @@ -385,9 +400,25 @@ public virtual string CreateEncodedJwt(string issuer, string audience, ClaimsIde /// /// A Base64UrlEncoded string in 'Compact Serialization Format'. /// If 'expires' <= 'notBefore'. - public virtual string CreateEncodedJwt(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) + public virtual string CreateEncodedJwt( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, encryptingCredentials, null, null, null, null).RawData; + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, + encryptingCredentials, null, null, null, null).RawData; } /// @@ -409,9 +440,27 @@ public virtual string CreateEncodedJwt(string issuer, string audience, ClaimsIde /// /// A Base64UrlEncoded string in 'Compact Serialization Format'. /// If 'expires' <= 'notBefore'. - public virtual string CreateEncodedJwt(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary claimCollection) + public virtual string CreateEncodedJwt( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + IDictionary claimCollection) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, encryptingCredentials, claimCollection, null, null, null).RawData; + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, + encryptingCredentials, + claimCollection, null, null, null).RawData; } /// @@ -460,9 +509,25 @@ public virtual JwtSecurityToken CreateJwtSecurityToken(SecurityTokenDescriptor t /// /// A . /// If <= . - public virtual JwtSecurityToken CreateJwtSecurityToken(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) + public virtual JwtSecurityToken CreateJwtSecurityToken( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, encryptingCredentials, null, null, null, null); + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, + encryptingCredentials, null, null, null, null); } /// @@ -487,9 +552,27 @@ public virtual JwtSecurityToken CreateJwtSecurityToken(string issuer, string aud /// /// A . /// If <= . - public virtual JwtSecurityToken CreateJwtSecurityToken(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary claimCollection) + public virtual JwtSecurityToken CreateJwtSecurityToken( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + IDictionary claimCollection) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, encryptingCredentials, claimCollection, null, null, null); + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, + encryptingCredentials, + claimCollection, null, null, null); } /// @@ -511,9 +594,23 @@ public virtual JwtSecurityToken CreateJwtSecurityToken(string issuer, string aud /// /// A . /// If <= . - public virtual JwtSecurityToken CreateJwtSecurityToken(string issuer = null, string audience = null, ClaimsIdentity subject = null, DateTime? notBefore = null, DateTime? expires = null, DateTime? issuedAt = null, SigningCredentials signingCredentials = null) + public virtual JwtSecurityToken CreateJwtSecurityToken( + string issuer = null, + string audience = null, + ClaimsIdentity subject = null, + DateTime? notBefore = null, + DateTime? expires = null, + DateTime? issuedAt = null, + SigningCredentials signingCredentials = null) { - return CreateJwtSecurityTokenPrivate(issuer, audience, subject, notBefore, expires, issuedAt, signingCredentials, null, null, null, null, null); + return CreateJwtSecurityTokenPrivate( + issuer, + audience, + subject, + notBefore, + expires, + issuedAt, + signingCredentials, null, null, null, null, null); } /// @@ -541,7 +638,19 @@ public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescripto tokenDescriptor.AdditionalInnerHeaderClaims); } - private JwtSecurityToken CreateJwtSecurityTokenPrivate(string issuer, string audience, ClaimsIdentity subject, DateTime? notBefore, DateTime? expires, DateTime? issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, IDictionary claimCollection, string tokenType, IDictionary additionalHeaderClaims, IDictionary additionalInnerHeaderClaims) + private JwtSecurityToken CreateJwtSecurityTokenPrivate( + string issuer, + string audience, + ClaimsIdentity subject, + DateTime? notBefore, + DateTime? expires, + DateTime? issuedAt, + SigningCredentials signingCredentials, + EncryptingCredentials encryptingCredentials, + IDictionary claimCollection, + string tokenType, + IDictionary additionalHeaderClaims, + IDictionary additionalInnerHeaderClaims) { if (SetDefaultTimesOnTokenCreation && (!expires.HasValue || !issuedAt.HasValue || !notBefore.HasValue)) { @@ -570,16 +679,21 @@ private JwtSecurityToken CreateJwtSecurityTokenPrivate(string issuer, string aud LogHelper.LogInformation(LogMessages.IDX12722, rawHeader, rawPayload, rawSignature); if (encryptingCredentials != null) - return EncryptToken(new JwtSecurityToken(header, payload, rawHeader, rawPayload, rawSignature), encryptingCredentials, tokenType, additionalHeaderClaims); + return EncryptToken( + new JwtSecurityToken(header, payload, rawHeader, rawPayload, rawSignature), + encryptingCredentials, + tokenType, + additionalHeaderClaims); return new JwtSecurityToken(header, payload, rawHeader, rawPayload, rawSignature); } - private JwtSecurityToken EncryptToken(JwtSecurityToken innerJwt, EncryptingCredentials encryptingCredentials, string tokenType, IDictionary additionalHeaderClaims) + private JwtSecurityToken EncryptToken( + JwtSecurityToken innerJwt, + EncryptingCredentials encryptingCredentials, + string tokenType, + IDictionary additionalHeaderClaims) { - if (encryptingCredentials == null) - throw LogHelper.LogArgumentNullException(nameof(encryptingCredentials)); - var cryptoProviderFactory = encryptingCredentials.CryptoProviderFactory ?? encryptingCredentials.Key.CryptoProviderFactory; if (cryptoProviderFactory == null) @@ -980,7 +1094,14 @@ public override string WriteToken(SecurityToken token) if (jwtToken.InnerToken.SigningCredentials != null) encodedSignature = JwtTokenUtilities.CreateEncodedSignature(string.Concat(jwtToken.InnerToken.EncodedHeader, ".", jwtToken.EncodedPayload), jwtToken.InnerToken.SigningCredentials); - return EncryptToken(new JwtSecurityToken(jwtToken.InnerToken.Header, jwtToken.InnerToken.Payload, jwtToken.InnerToken.EncodedHeader, encodedPayload, encodedSignature), jwtToken.EncryptingCredentials, jwtToken.InnerToken.Header.Typ, null).RawData; + return EncryptToken( + new JwtSecurityToken( + jwtToken.InnerToken.Header, + jwtToken.InnerToken.Payload, + jwtToken.InnerToken.EncodedHeader, + encodedPayload, encodedSignature), + jwtToken.EncryptingCredentials, + jwtToken.InnerToken.Header.Typ, null).RawData; } // if EncryptingCredentials isn't set, then we need to create JWE @@ -991,7 +1112,10 @@ public override string WriteToken(SecurityToken token) encodedSignature = JwtTokenUtilities.CreateEncodedSignature(string.Concat(encodedHeader, ".", encodedPayload), jwtToken.SigningCredentials); if (jwtToken.EncryptingCredentials != null) - return EncryptToken(new JwtSecurityToken(header, jwtToken.Payload, encodedHeader, encodedPayload, encodedSignature), jwtToken.EncryptingCredentials, jwtToken.Header.Typ, null).RawData; + return EncryptToken( + new JwtSecurityToken(header, jwtToken.Payload, encodedHeader, encodedPayload, encodedSignature), + jwtToken.EncryptingCredentials, + jwtToken.Header.Typ, null).RawData; else return string.Concat(encodedHeader, ".", encodedPayload, ".", encodedSignature); } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index db078f3f6f..5c35f526ed 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -1227,10 +1227,11 @@ public static TheoryData CreateJWSWithAdditionalHeaderCla [Theory, MemberData(nameof(CreateJWEWithPayloadStringTheoryData))] public void CreateJWEWithPayloadString(CreateTokenTheoryData theoryData) { - var context = TestUtilities.WriteHeader($"{this}.CreateJWEWithAdditionalHeaderClaims", theoryData); + var context = TestUtilities.WriteHeader($"{this}.CreateJWEWithPayloadString", theoryData); var handler = new JsonWebTokenHandler(); string jwtTokenWithSigning = null; JsonWebToken jsonTokenWithSigning = null; + CompressionProviderFactory.Default = new CompressionProviderFactory(); try { var jwtToken = handler.CreateToken(theoryData.Payload, theoryData.TokenDescriptor.EncryptingCredentials, theoryData.TokenDescriptor.AdditionalHeaderClaims); @@ -1339,7 +1340,6 @@ public static TheoryData CreateJWEWithPayloadStringTheory TokenDescriptor = new SecurityTokenDescriptor { SigningCredentials = Default.SymmetricSigningCredentials, - CompressionAlgorithm = CompressionAlgorithms.Deflate, EncryptingCredentials = Default.SymmetricEncryptingCredentials, AdditionalHeaderClaims = new Dictionary{{JwtHeaderParameterNames.Cty, "str_outer"}}, AdditionalInnerHeaderClaims = new Dictionary{{JwtHeaderParameterNames.Cty, "str_inner"}} @@ -3390,7 +3390,7 @@ public AuthenticatedEncryptionProviderMock(SecurityKey key, string algorithm): b public override AuthenticatedEncryptionResult Encrypt(byte[] plaintext, byte[] authenticatedData) { - byte[] nonce = new byte[AesGcm.NonceSize]; + byte[] nonce = new byte[Tokens.AesGcm.NonceSize]; // Generate random nonce var random = RandomNumberGenerator.Create(); @@ -3401,10 +3401,10 @@ public override AuthenticatedEncryptionResult Encrypt(byte[] plaintext, byte[] a public override AuthenticatedEncryptionResult Encrypt(byte[] plaintext, byte[] authenticatedData, byte[] iv) { - byte[] authenticationTag = new byte[AesGcm.TagSize]; + byte[] authenticationTag = new byte[Tokens.AesGcm.TagSize]; byte[] ciphertext = new byte[plaintext.Length]; - using (var aes = new AesGcm(GetKeyBytes(Key))) + using (var aes = new Tokens.AesGcm(GetKeyBytes(Key))) { aes.Encrypt(iv, plaintext, ciphertext, authenticationTag, authenticatedData); } diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetBadRsaDataMissingComponent.json b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetBadRsaDataMissingComponent.json index de6d199291..7c95e7fd0e 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetBadRsaDataMissingComponent.json +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetBadRsaDataMissingComponent.json @@ -1,4 +1,4 @@ -{ +{ "keys": [ { "Kid": "pqoeamb2e5YVzR6_rqFpiCrFZgw", diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetUnrecognizedKty.json b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetUnrecognizedKty.json index b303605166..cb22a5e7ed 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetUnrecognizedKty.json +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/JsonWebKeySetUnrecognizedKty.json @@ -1,4 +1,4 @@ -{ +{ "keys": [ { "alg": "SHA256", diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs index e740923cc0..2118e5e807 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs @@ -250,6 +250,9 @@ public void OidcCreateAuthenticationRequestUrl(string testId, OpenIdConnectMessa #elif NET472 if (!message.SkuTelemetryValue.Equals("ID_NET472")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET472"); +#elif NETCOREAPP3_1 + if(!message.SkuTelemetryValue.Equals("ID_NETCOREAPP3_1")) + context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETCOREAPP3_1"); #elif NET_CORE if (!message.SkuTelemetryValue.Equals("ID_NETSTANDARD2_0")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETSTANDARD2_0"); @@ -510,7 +513,7 @@ public void OidcCreateLogoutRequestUrl(string testId, OpenIdConnectMessage messa TestUtilities.WriteHeader("OidcCreateLogoutRequestUrl - " + testId, true); var context = new CompareContext(); -// there is no net452 target, we bind to net45 + // there is no net452 target, we bind to net45 #if NET452 if (!message.SkuTelemetryValue.Equals("ID_NET45")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET45"); @@ -520,6 +523,9 @@ public void OidcCreateLogoutRequestUrl(string testId, OpenIdConnectMessage messa #elif NET472 if (!message.SkuTelemetryValue.Equals("ID_NET472")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET472"); +#elif NETCOREAPP3_1 + if (!message.SkuTelemetryValue.Equals("ID_NETCOREAPP3_1")) + context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETCOREAPP3_1"); #elif NET_CORE if (!message.SkuTelemetryValue.Equals("ID_NETSTANDARD2_0")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NETSTANDARD2_0"); diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataBadRsaDataMissingComponent.json b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataBadRsaDataMissingComponent.json index fdcac50e84..282c92eb20 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataBadRsaDataMissingComponent.json +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataBadRsaDataMissingComponent.json @@ -1,4 +1,4 @@ -{ +{ "issuer": "https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/", "authorization_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize", "token_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token", diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataUnrecognizedKty.json b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataUnrecognizedKty.json index 4388d9b6f0..e2f2b0738a 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataUnrecognizedKty.json +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadataUnrecognizedKty.json @@ -1,4 +1,4 @@ -{ +{ "issuer": "https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/", "authorization_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize", "token_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token", diff --git a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs index 9e3b2e29ec..e6295f9861 100644 --- a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs +++ b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs @@ -145,6 +145,14 @@ public static X509SecurityKey DefaultX509Key_2048_Public public static X509SecurityKey NotYetValidX509SecurityKey_Public = new X509SecurityKey(NotYetValidX509Cert_Public); public static SigningCredentials NotYetValidX509SigningCreds_Public = new SigningCredentials(NotYetValidX509SecurityKey_Public, SecurityAlgorithms.RsaSha256Signature); +#if NET472 || NETCOREAPP3_1 + //encoded strings for "AliceInformation", "BobInformation", "AliceNotMatchingInfo", and "BobNotMatchingInfo" + public static string ApuExample1 = "QWxpY2VJbmZvcm1hdGlvbg"; + public static string ApvExample1 = "Qm9iSW5mb3JtYXRpb24"; + public static string ApuExample2 = "QWxpY2VOb3RNYWNoaW5nSW5mbw"; + public static string ApvExample2 = "Qm9iTm90TWFjaGluZ0luZm8"; +#endif + public static SecurityKey DefaultAADSigningKey { get diff --git a/test/Microsoft.IdentityModel.TestUtils/ReferenceTokens.cs b/test/Microsoft.IdentityModel.TestUtils/ReferenceTokens.cs index 4c8c542b3a..ff2b5de815 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ReferenceTokens.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ReferenceTokens.cs @@ -29,7 +29,7 @@ namespace Microsoft.IdentityModel.TestUtils { public class ReferenceTokens { -#region Saml2 Token + #region Saml2 Token public static string AADJWKS = @"{""keys"":[{""kty"":""RSA"",""use"":""sig"",""kid"":""a3QN0BZS7s4nN-BdrjbF0Y_LdMM"",""x5t"":""a3QN0BZS7s4nN-BdrjbF0Y_LdMM"",""n"":""wESLNTU4mazfVL-vLuJq_8ggJhW1DYxE-EeFiSccia1TTeyBWTVfG5vgYPtHXmL1RYgZvNhIYppS0ZT2U_nnCt8ukONCMSBpeLh8TqZxkHBr2pzbaKzbcHpHrsoxxXLHINZ6L4g_ewqYJwxfshuyD65tlSm8obFdnbtiCoVM-oJPbOcPsrzVgp_L5JWDe5bp6lbXXjJnMKVNCVqum1i4Taa6PGNm3HtlSXBz0CFWLwJ6IvAY7XDNOal3-5y2md6vqhzffmu90mKQ2ZzVwUoIr7aKt7DVuBQke434skDTLmJVcq-iOIpnYiLtApefX1KyDUWgnfHY1YDTrBzQKeu4uw"",""e"":""AQAB"",""x5c"":[""MIIDBTCCAe2gAwIBAgIQY4RNIR0dX6dBZggnkhCRoDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE3MDIxMzAwMDAwMFoXDTE5MDIxNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMBEizU1OJms31S/ry7iav/IICYVtQ2MRPhHhYknHImtU03sgVk1Xxub4GD7R15i9UWIGbzYSGKaUtGU9lP55wrfLpDjQjEgaXi4fE6mcZBwa9qc22is23B6R67KMcVyxyDWei+IP3sKmCcMX7Ibsg+ubZUpvKGxXZ27YgqFTPqCT2znD7K81YKfy+SVg3uW6epW114yZzClTQlarptYuE2mujxjZtx7ZUlwc9AhVi8CeiLwGO1wzTmpd/uctpner6oc335rvdJikNmc1cFKCK+2irew1bgUJHuN+LJA0y5iVXKvojiKZ2Ii7QKXn19Ssg1FoJ3x2NWA06wc0CnruLsCAwEAAaMhMB8wHQYDVR0OBBYEFDAr/HCMaGqmcDJa5oualVdWAEBEMA0GCSqGSIb3DQEBCwUAA4IBAQAiUke5mA86R/X4visjceUlv5jVzCn/SIq6Gm9/wCqtSxYvifRXxwNpQTOyvHhrY/IJLRUp2g9/fDELYd65t9Dp+N8SznhfB6/Cl7P7FRo99rIlj/q7JXa8UB/vLJPDlr+NREvAkMwUs1sDhL3kSuNBoxrbLC5Jo4es+juQLXd9HcRraE4U3UZVhUS2xqjFOfaGsCbJEqqkjihssruofaxdKT1CPzPMANfREFJznNzkpJt4H0aMDgVzq69NxZ7t1JiIuc43xRjeiixQMRGMi1mAB75fTyfFJ/rWQ5J/9kh0HMZVtHsqICBF1tHMTMIK5rwoweY0cuCIpN7A/zMOQtoD""]},{""kty"":""RSA"",""use"":""sig"",""kid"":""2S4SCVGs8Sg9LS6AqLIq6DpW-g8"",""x5t"":""2S4SCVGs8Sg9LS6AqLIq6DpW-g8"",""n"":""oZ-QQrNuB4ei9ATYrT61ebPtvwwYWnsrTpp4ISSp6niZYb92XM0oUTNgqd_C1vGN8J-y9wCbaJWkpBf46CjdZehrqczPhzhHau8WcRXocSB1u_tuZhv1ooAZ4bAcy79UkeLiG60HkuTNJJC8CfaTp1R97szBhuk0Vz5yt4r5SpfewIlBCnZUYwkDS172H9WapQu-3P2Qjh0l-JLyCkdrhvizZUk0atq5_AIDKRU-A0pRGc-EZhUL0LqUMz6c6M2s_4GnQaScv44A5iZUDD15B6e8Apb2yARohkWmOnmRcTVfes8EkfxjzZEzm3cNkvP0ogILyISHKlkzy2OmlU6iXw"",""e"":""AQAB"",""x5c"":[""MIIDKDCCAhCgAwIBAgIQBHJvVNxP1oZO4HYKh+rypDANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwHhcNMTYxMTE2MDgwMDAwWhcNMTgxMTE2MDgwMDAwWjAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChn5BCs24Hh6L0BNitPrV5s+2/DBhaeytOmnghJKnqeJlhv3ZczShRM2Cp38LW8Y3wn7L3AJtolaSkF/joKN1l6GupzM+HOEdq7xZxFehxIHW7+25mG/WigBnhsBzLv1SR4uIbrQeS5M0kkLwJ9pOnVH3uzMGG6TRXPnK3ivlKl97AiUEKdlRjCQNLXvYf1ZqlC77c/ZCOHSX4kvIKR2uG+LNlSTRq2rn8AgMpFT4DSlEZz4RmFQvQupQzPpzozaz/gadBpJy/jgDmJlQMPXkHp7wClvbIBGiGRaY6eZFxNV96zwSR/GPNkTObdw2S8/SiAgvIhIcqWTPLY6aVTqJfAgMBAAGjWDBWMFQGA1UdAQRNMEuAEDUj0BrjP0RTbmoRPTRMY3WhJTAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXOCEARyb1TcT9aGTuB2Cofq8qQwDQYJKoZIhvcNAQELBQADggEBAGnLhDHVz2gLDiu9L34V3ro/6xZDiSWhGyHcGqky7UlzQH3pT5so8iF5P0WzYqVtogPsyC2LPJYSTt2vmQugD4xlu/wbvMFLcV0hmNoTKCF1QTVtEQiAiy0Aq+eoF7Al5fV1S3Sune0uQHimuUFHCmUuF190MLcHcdWnPAmzIc8fv7quRUUsExXmxSX2ktUYQXzqFyIOSnDCuWFm6tpfK5JXS8fW5bpqTlrysXXz/OW/8NFGq/alfjrya4ojrOYLpunGriEtNPwK7hxj1AlCYEWaRHRXaUIW1ByoSff/6Y6+ZhXPUe0cDlNRt/qIz5aflwO7+W8baTS4O8m/icu7ItE=""]}]}"; public static string Saml2Token_InclusiveNamespaces_WithoutPrefix = @@ -170,9 +170,9 @@ public static string Saml2Token_Formated } } -#endregion + #endregion -#region Saml Token + #region Saml Token public static string SamlToken_MissingMajorVersion = @"spn:fe78e0b4-6fe7-47e6-812c-fb75cee266a4add29489-7269-41f4-8841-b63c95564420d1ad9ce7-b322-4221-ab74-1e1011e1bbcbUser1@Cyrano.onmicrosoft.com1UserUser1https://sts.windows.net/add29489-7269-41f4-8841-b63c95564420/Ytfkc60mLe1Zgu7TBQpMv8nJ1SVxT0ZjsFHaFqSB2VI=NRV7REVbDRflg616G6gYg0fAGTEw8BhtyPzqaU+kPQI35S1vpgt12VlQ57PkY7Rs0Jucx9npno+bQVMKN2DNhhnzs9qoNY2V3TcdJCcwaMexinHoFXHA0+J6+vR3RWTXhX+iAnfudtKThqbh/mECRLrjyTdy6L+qNkP7sALCWrSVwJVRmzkTOUF8zG4AKY9dQziec94Zv4S7G3cFgj/i7ok2DfBi7AEMCu1lh3dsQAMDeCvt7binhIH2D2ad3iCfYyifDGJ2ncn9hIyxrEiBdS8hZzWijcLs6+HQhVaz9yhZL9u/ZxSRaisXClMdqrLFjUghJ82sVfgQdp7SF165+Q==MIIDBTCCAe2gAwIBAgIQY4RNIR0dX6dBZggnkhCRoDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE3MDIxMzAwMDAwMFoXDTE5MDIxNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMBEizU1OJms31S/ry7iav/IICYVtQ2MRPhHhYknHImtU03sgVk1Xxub4GD7R15i9UWIGbzYSGKaUtGU9lP55wrfLpDjQjEgaXi4fE6mcZBwa9qc22is23B6R67KMcVyxyDWei+IP3sKmCcMX7Ibsg+ubZUpvKGxXZ27YgqFTPqCT2znD7K81YKfy+SVg3uW6epW114yZzClTQlarptYuE2mujxjZtx7ZUlwc9AhVi8CeiLwGO1wzTmpd/uctpner6oc335rvdJikNmc1cFKCK+2irew1bgUJHuN+LJA0y5iVXKvojiKZ2Ii7QKXn19Ssg1FoJ3x2NWA06wc0CnruLsCAwEAAaMhMB8wHQYDVR0OBBYEFDAr/HCMaGqmcDJa5oualVdWAEBEMA0GCSqGSIb3DQEBCwUAA4IBAQAiUke5mA86R/X4visjceUlv5jVzCn/SIq6Gm9/wCqtSxYvifRXxwNpQTOyvHhrY/IJLRUp2g9/fDELYd65t9Dp+N8SznhfB6/Cl7P7FRo99rIlj/q7JXa8UB/vLJPDlr+NREvAkMwUs1sDhL3kSuNBoxrbLC5Jo4es+juQLXd9HcRraE4U3UZVhUS2xqjFOfaGsCbJEqqkjihssruofaxdKT1CPzPMANfREFJznNzkpJt4H0aMDgVzq69NxZ7t1JiIuc43xRjeiixQMRGMi1mAB75fTyfFJ/rWQ5J/9kh0HMZVtHsqICBF1tHMTMIK5rwoweY0cuCIpN7A/zMOQtoD"; @@ -241,7 +241,7 @@ public static string Saml2Token_Formated public static string SamlToken_SignatureMissing = @"http://Default.Audience.comBoburn:oasis:names:tc:SAML:1.0:cm:bearerUSABob@contoso.comBob555.1212DeveloperSalesJean-Sébastien"; - + public static string SamlToken_Formated { get @@ -314,9 +314,9 @@ public static string SamlToken_Formated } } -#endregion + #endregion -#region JWE compression tokens + #region JWE compression tokens /** * All the following tokens are signed with KeyingMaterial.DefaultSymmetricSigningCreds_256_Sha2, and encrypted with * EncryptingCredentials(KeyingMaterial.DefaultX509Key_2048, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512) @@ -343,35 +343,35 @@ public static string SamlToken_Formated // This token was signed with Default.SymmetricSigningCredentials and encryted with Default.SymmetricEncryptingCredentials. // This token includes two additional header claims: // { "int", 123 } and { "string", "string" }. - public static string JWEDirectEcryptionWithAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmcifQ..Jph7tSiacUx4bdAlUZE49g.sOsePYxZ44SXyynUeLwvlw3Q0SioyNtHFy1AdYEuk4YC6U_zhAFC5jRPQf4wymjB7WMuqHLkj0uv9f6hRtvefWJA-yF9UOUUaBln5u6wtliotiGDh0JcyLpOLjuZwqiFj0rLe-6VoowG_1hbXmYrpb8jtvu23vib_cx-hj3vjAUiIgD6Gay7xgh9yPoxzbsEa4enzOh7Cgc-Zlv1GyC4B4Pj4AZ_5daabz62YGsu2DyffRqA-BNezICoYErw_vAs4SvhvzaE_b4_N5IQ5cwlGeNAVTPGabkYCu2D6oMGF2QDaebm06Fpr79kFni35sz3CERWzHt6sGSQT3sxCm0e7ORtM11d0O34oLmkbUdrBZaB047jv4aACK2MCdOWwvwNUSc5jE3rInSGzcKGistq_mmIBTWe0pZqo6k3cNJ_NTz6WL_r48f9p9rAe5wAR9ZfY_o3AlwHhKfl-Mhio5lfANKk5PylQcsOqWxBKrdbXznzQZ9eyluDpxNxdp62JU0jYvkUW2rjfmku-yh5A5qcgnMzqyDcunS-My8tbnyJ8XcEiEbq89cMrbe-4BGy1bha.djjhS_qNCs4dICnfw7676A"; + public static string JWEDirectEcryptionWithAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmciLCJjdHkiOiJKV1QifQ..mp-RpKTVAT1emM_7g74Mdw.cIOK6Y1HtKyIvpnY4USHej6jwdcjkr77ydqnhnMTEAWW5Xy4o15A-0m0u5lRrkuRu-OHRts7IpFMZNeabSNrLZ1NTnG-EPMXmjJts7ZRIGH0GMgdCOp7YUE0hgxmtxJYVLDI-raJUGSO4ZEPBB3UDWDc9QxeQXQj6CnYDkd52YkW6bURXjWst848qc_AttNVRPQl6AcCYGM51YDFWIPZ2L2GcyKVVbVFwyfz9pij2qUS150OZx4DtwhySY3MyiYLWFhRqR9o1DKUW2mTsOylxfJ72R4MdOp0AhVUOqGiI_gNzLDpsLtKuuUFAnTiUkYgPEEf_yz53Iwj06x-oSWTJaUkW7e9Wwj2RczpW2qOTVKT7Fasg8aYZzdetAoMU1FY5FvHRzzIBI0-k7rZ53CWqgOsd3P7jMP-n0GrRiw0D3dvHlRjpDgyiqhDU5LC-4gU56ZBxxzvL_dsA-H7rHrwl7q0fAR3ISEBxKoOuJP7HQhJvm-qXt8DuRCYgRIwdKKqwrgLX8khkBtBSrdHqx9Aj2G6tTpNG4lHX8J7ZQZd0U2n-jv_xoH7cpF4sB5HYLZGpiMNZNYKpqNvR8MgXEJWYQ.blFLIZ076tHgYpIFyLEKYQ"; // This token was signed with Default.SymmetricSigningCredentials and encryted with Default.SymmetricEncryptingCredentials. // The value for the 'typ' header claim in this token has been changed to "TEST". - public static string JWEDirectEcryptionWithDifferentTyp = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IlRFU1QifQ..xJEJ2ClTfUIvjzh5wtS-ow.N-0XvCnAcjp5C59LiB7G3EO0XL7S9ILsDMgwqRWQ0bXoBgKM9GUhCRByft24iORAD5NOKBvs7gt3kbN6Yek5q7I3fRYUZBJlcA7tLhmdl3-XU-N3H9Z0du7NwOZPlytqNJByF0sTsz5MJDUo2BvBZ7tb0g7G8240eGnPBBOTyA0q6uEYQJSoxUKL-_-rkwEudyikTgVrlFOCTtaZZDUXPrT4406enNlXwel6PpYnZq_hxoAzS1tfZZpaW0lpg-55o4d6OiXu143EUq-JXMUDieln1AoKOtsBXM25JtXzo2QphP5PzM-knebR4PusOVupm44MTGZRjz0Upmj2DKR6wblO-Hc3U3LJIRR9FFSmAHew4QFKbC7I1KKvbZ0UMuHulnZR9DiE340_N2RKn0Odo4goXyUDLGcUolF5JthXE8ljIixbmbDgfalbFTlqbsLtbvzbZRRjspYACuB7kVkh-K6fyNLuawvkC_IgTKjFxAWDnFVaPirf8kYm-ckbyZmEhDyvMtVeREjTXV6jWW_mynwCHy78-cst8PAVSnsdQkLCYntAf-ZNhxLSTC9WlE2k.prYwXw1nKcjTCUrko6HF7Q"; + public static string JWEDirectEcryptionWithDifferentTyp = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IlRFU1QiLCJjdHkiOiJKV1QifQ..eA39_r3taevB_cX_S3qN2g.z_HTNrt92jP1eHPj8GRQAiAhOUVr9CXeMoFrePV7Cey9CGbSZZlGxzHcAmYKTG_-3B9JzR_ogswr7-RcfZmmQ6fwPymNd7pyPz62Dle7mAAiysLopxbGHm1bEys6VMq07wYaQbhMHYaC1MPor11uOQA7vLATA8FzaBmpZoKAJ1SyNqxMcwlHNQB1b8GR20isP9zyhH8IaFecTdfWduyzH_cGRaK3o43XKZTKBqRHReVI-80KjcIT7b2vz26qEwQ8r5DE4b4phVwGbBvNDfjzcJKHwj8yLGWKIh5QhljJL1Tq65ELgCssCGRmr2bvbYYVWwUNemHKj_5Q2FqiNIyBZjiWXIdCSE1zLuh7JKr1KXD_thuf0LMoHb-a7J2JHaIKokciHpTRTGr_G82_mY1iq0h9Z7Nd_AwIZcMDGUUmf6UUcn731ZJPmQeEDodxA96kwrrKsmM4zYFH-S4FmCp619LDiSmtcCwN0Pr3ZEYUCk0fdAvGJltRErVObGBk55jJpLBw8G3s_r0PdOOx1wJChk9LJNP6p-Iwe6wwrnjxJoaK2lFQtOQLFUzCDrYltP-hZeSeyhrVxmh-Ykg9bzRetw.4ulSVNaIUkcJQR5toJ2K_Q"; // This token was signed with Default.SymmetricSigningCredentials and encryted with Default.SymmetricEncryptingCredentials. // The inner JWT is unsigned and this token includes two additional header claims: // { "int", 123 } and { "string", "string" }. - public static string JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmcifQ..Z1hcrcpQf5jXayinT3rF-w.znWveolvSaum8K_PywIM52ByvxW0qbXd2cOMxqb-aHEiawZoCeXais4AnIAN3C4uVRdBQ38HfHq2QeMsnGSO1N1BS5baIXB_5bs3-WHo2CMCf30pjDCSNt9wDMCSUuwf4oYZ8yk55ibl0rnebWJ1K8NkpXRrIpALnkixsyG3tLAVM5tI8d3zP52IslmvSL-LpReX0MXp96odi5NmQm9G7C_iiEE7QMbmX6DcCJMPCVEhpBeC7SfrPW6r2GMjaRfp451lE0uTEY7g70nJO44uOMg79DNN6RgPcj57tF_UH-G_RudS94Mk7x-ojMlZk3KyOqTIkoF8QxmavapKNcuEBEJlLIw_B0ljTDff5fMdLys.5dLQqOaiOSkVOO2BwqLShw"; + public static string JWEDirectEncryptionUnsignedInnerJWTWithAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmciLCJjdHkiOiJKV1QifQ..PPytZx4wiznlp-vK0K_tMA.twU1OKQpq0Ahh00JWKF7JW-OhsLg0fNJJsIP3kO8ZkYxqFeotqJhRfXSn0sVXTiG7urQOfV9RFF4N_o9s38CBnMqUTY41Qu2fBimD2BRz0OFVidXvaRVp9WPQ-2MI2UlOYAIC4UXqoYmb-OsfvMtqXNZfz5A-xqKXN2WbuVYngwXVTff-yw7utsqYlgos6j0Sb7OCZ9u768ubnIzLyDiexU3ty4nQ8yLuDkHZ_nX5OFuuPrTQXTvPhX3A4gFWCeksjno_Mf75i9F9YgWpAwJR99ZT0PhCwtk4v810y3CNZLc7Uh099K-evyMJxYsGMeH4FNrj751Uslf9_G8lFsEBI0cXA9s6jERsBCiWDrox_SG7XcE8879DgBPHxyLeraC.0sIhTcQGCJpyPyfiRRgO0A"; // This token was signed with Default.SymmetricSigningCredentials and encrypted with EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256). // This token includes two additional header claims: // { "int", 123 } and { "string", "string" }. - public static string JWEKeyWrappingWithAdditionalHeaderClaims = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmcifQ.sw1A_ikOQlAroR_AAHGoe2vUA7ps7vQo8ZcNkprBaLSf0YES6hnzml0XeJ0gYjOumNAqZNxpWE7Jre1BqiK-o1uINjRdoOlgF62S-2Y2PSPlU202jzJhBo9OQMJzgpKewMFtR0poLtXvzgWpwI-scETh0IrrQ81zhwx2ZmaFaeJVTesZTNT_cE-MFE_xTDIv_sklpbBtcqdNdOUOSgPxSrsd1p2TG55w2FP-EvqoESF-F7agyK_szFaRCICIgz82VUUkp0Z1t6QDgNUw-lGpDV25w1REmT0y_I8j2a-9WlycVUwvtNXQxTZ5LeGyug9xAG_stomneYwqOwQXIOS04w.-OEPy1izi1LaiDQ6UmWzPA.phUKIJBqBdjTplTwZk3xI8GP9qN5TqKSPz5p5ix-GnAG4RaZg_XvsLlX6yFh_PBjp15Cd9Myzo9QnGUHd1fIGUkuCqS3fV1oLFHePEMx2b4HfHeQOXGNNnBg9PM0UEjTOmxhe3fq-c4o45nvmHz9jmC3sWJHK4Ev4GXSIrmyxC0_2vs81gspfeFg72s-azBFzhKNcUwhtniTFixregY722ezhEE5XLhB861G1hWqkphiMN-MAW0jAHtyWfyy4S4PV7ysZihQqU4DyjtYiSE36i_vV9lofjeXmPQp4yl529cawEFE2i3rQc6uP5vfBRkU08TokFdNA3O01DgsUhjzJ4VmWD8Z8zAYmu7YQwcLGGNZlYvRdVXnkxQkvITo4lRi7on05wo14VT46rNhmRDQ8r56b8-qgpODC60qLK9YhhsSTKhhsRUU-W4HsrkOopUjrHdzmSevepm81ISev7ZoJp1-9wSUomsxFaz8liux7wugEBkwqKhJh_tvclyUEq8Ta9pKDwvTExhycAlRKz-L3TwbHa4MIVaAXIR0IDeRV-0Q3DDE0TEdtLMK31N1pVmU.hsQ5f1Sz8lBnQPWnjnrX4A"; + public static string JWEKeyWrappingWithAdditionalHeaderClaims = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmciLCJjdHkiOiJKV1QifQ.oZRuDGHRSu6ouGlWu21lm6wmN_sx18oz9v5gXCp19DZPrv6fIweO4be0_rESFr9PPF5_nP7BPZDxNef0TbYhsuoF021k8c_L1dB_tQSbWifAt3AsBGFFVnKy3_ZBsEk957JyhS6l3nBLUQ9pKxxmVzXGILpLC5OrTRiWu502xrfzNUWiro7lmwtlXm-sYqI3nHqeGjoRWYU8vWztz8w5IQUntcV4Oocr_lgXOA0k4lbgcdgc57iTME4nw4HT1s1PvsRYoZ1QviE5f-G6G3Ke7j766tJHRLSBlCjVztR2EFW9zAbQ6MP8LTPpdv6V8KNaRa4_SMCYqK9PajeVnlf--g.AQUxe_kk8JSJ7_Nn_QdwYQ.1eZBZl5hP12ix_vxNhX5OyKUSt_sE5uc-Dk-1zVpVSDOspNLOfMhGRNZe0xnprCX7x-8RmTHoHcLVS-0_A38osifgIujN8g9-eg1dPNn_tUeed-JCSzGnjtpYBWp0wuydU_t2JQN-VJrA-mKM-ECOgvT0MzfNoKs7cvulVUfaUl4i6VD5vKYwijq1lQoH9YOSkxNCfA7TFfB7upQ-7RXcABw6ONOMkq6FzTFtlg81b1ggPPAYiq-50xPccmo_nk0JstfUXe6LFBKClhJoyBwLjruxr2abtsD2OWCI2WcahvrrLZZR-CBXEUSIr67L80B8vvstSVTKnLq2epROXz5BgArbZvy2uVR02584CX3L6dyLxJw_dOeUvPY0FgdZsFAd1dSrIP802rZtKpG07X9SxmJqsFYy_NIS67-umQdVsbqvX70sDRK7qGPN-iThvjrgj7eqEtiU36cW8pDKUP1V7zHHFsDMyhKVBbF3xOVmKLaJwH2M7YQaYrNYjQKLs6UCgPvEySmf4Dv3M7PEWRpgSGVHc0__P0xKsHxMx7d_o836VE9OIwR_7FbPvdPI7MfV3OwcPdRl72wZRGadltd8g.VHeeRVUns5K4I2Jk-fIXGQ"; // This token was signed with Default.SymmetricSigningCredentials and encrypted with EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256). // The value for the 'typ' header claim in this token has been changed to "TEST". - public static string JWEKeyWrappingWithDifferentTyp = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IlRFU1QifQ.4O-5-yN67fG2iEW3dvViUN9mj3hIHXhEXkf3_8YbvDqQz91hR6A2vrNQMAx5H8SBJvcKUj9xtMvs5yT4bSXJijN60xXJeP6SUyxxC9IlMgeNIlguJSeBGDxD_Oka1DKYdWjWO50LrvuD2FR99vpLA8merOmOXTkePrYb0Cgz4lPwHVrEuOsRa_YSg6FwHZS_nTKwHH6A5Y8Jo9m8IAIxWMxTeUksOip9kzZetURWIMrYToQIcwCESv5szAXV3uXMUa2veESDtwSTXOJc3GkzTt22vcxk5plRnX9vWT2cwKb33ZeX7SMJwd8OweEb47d93L0YJklwPIFuUrjPztyNCg.bI0Xb6prW9z6rQhhF4hDUg.n6gBVp7MVKU71lzYv0yJQsIIh05veO9Ee26C44Venp2KkqDrsHkUxAOGuwCEzrJG6e2H4trPa5mv8Nwi9voTf_YAtQyrZVojMD3N3Apqsl9vDdxV7yyOz1Y1LsUhkriWV3YgkRtlW8-oqKXDlkDlWv_o47aFOv4zqwn8xx58ohg6des0PjyKb5dORtSKmvAoIxBUlDN2vp9gEquZiRQSFjFKw9bK28rklf345wFB3MnLxPk2JVLXHk3IjggHNV8BR6fi38ViFzKD8uFO1pvNc_balCJWGIPUtOGU0D7paJWbzCHnjKK_SohG7-dBIPPoynSNRz5bubRuuIAvhUC2cjVCwp2KNV1_V3Tdaw9qo_x32TNIL-wO0-RNyKJB5SxTiEjO2xi-I5CGPNNf-yzRrwqfjvsF_3_yccdbNT_VleCwnBp403A4rnmq8Pa2nLB3hHL_Uv2qvkSa5GuJYyLtMb93mr_fyCL21KS6TlL5uD_x91oQsAeeT87bKnmP3RI3kgmR2MhXIQVabkI83rNpHqF-YyzpOIFMCtLN0ZtilbKWlc-0cxuoh2zJy-Dek20_.nk6ecxkk77wf-qSG59QwEw"; + public static string JWEKeyWrappingWithDifferentTyp = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IlRFU1QiLCJjdHkiOiJKV1QifQ.kYcTGDV2aeHmLLgNVBcTEodNDGeG23x0B3roYeOxwR9KNDNCwddpdCTVKq0kvntL_kfVwTacHJjL-_-RcsFAqyNn7WvESj3xg4QGimwEFQoUNa3SfRDx97nIvsDMqg9akZ54-J8jzQIG6CsqkpUzSMcgupPSij98zg-d9wDwn-RK7QqaLNVwXVEqdXHKKkhNFxU9lVnGVcx00O8WEp3qK6EI1G3PWd-n7h5tpUX4DU8nPigehPlNY97TfpUFsuKzzliNqdbuwARVsiA7H4GKSXp-M_EeYKfG22UKmkpa_5Xt1kvYtdzVlGvu0cVN4ucANiO-vWeReX0aWsFdt8oelA.tV5yh3vZMRwaCIkSp0m7GA.RPUNqprUt0b9GkfMkDzYlhTEtnYXp7owlkd0RnRXooZYK582T1E1jFjuYJjZp18re7_P9i0KhEHvqNGBes-apMta7IGzNh6Ry62LUTSHnYQ3wdQvuxi-ov1IqW75v3P-TTHk6-SmGG2OIBQS18qRoUEsPu_HDA03btp1ezEt9GhDArwmLapsu_PAl26sjyc53UXigeb4x0agHMeSylNR97IvWj7DvDlX0m1-MD4LT7A0vXKIii6grir9k_b0af0gB5s9-pA-Dd9R-CN1p-_-KfoxI7cu0U0abFPsoJvagLRPNFJsSMlzjQIK86l6CPZvAlMRKGieM5CSgcZaR_nZ7ZgyzogloV19Rcn17apUzKV70Y_piM-jenYzumZ9Rln82YD3rsppJBg5bUOpuAU4oVYAR3BvWjgcqAfuWa51rAZMDyNXjKa_p0dQhHNxYu6oNKaNwRnVAxfTpsJVEwwitYBjk0u-4u5CwSrk0_-fnzRIA6KWMX7MDZzhyWyEl_zk5086PAwN5yI1FpuCEBZQqtX8rBVokYsiYE7PEvnPIzoY8ycrGl4s_0DmGUHZ1gshha19pdIMd91ZzpB4iO9AXQ.hNCUOKB72epKrl94MzhJaA"; // This token was signed with Default.SymmetricSigningCredentials and encrypted with EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256). // The inner JWT is unsigned and yhis token includes two additional header claims: // { "int", 123 } and { "string", "string" }. - public static string JWEKeyWrappingUnsignedInnerJWTWithAdditionalHeaderClaims = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmcifQ.UuIMP7ipqfWl0fjWpgUtDQtn9YVMy2_r7oQ-3moQOjQP_sfuxTPQ8LwlgSxGqZ42ihfzyZJ2RjuyQPETi_CN5t1T7EJzdLfjmLAExanc4XjtveKxcKgUxPfq1izIRzn4ceEswfbFqFRSw-MG5MiyxwZN7EMZFtHQZPbJFLZuOdtMUcNXCIbaJTRdyWHrOqd_3PoGJG29puuhbhTdWbK3qtrpaLULf0wUcEE5GL5K8Ob8f4XqKTlaF5t_QZOD1P9rtEiFvnF0Iw74yrs-cv6mvG1AgEG5AcfPH9mIO7Xul6o5KiPqaMsJ2PZSJ2CApeajWgdtgfxeUn8HUecss20P3g.XSpcWjCuqG2VBKhloR8DHw.LbUjGDCLvWxR1nxf_YKpTr_9Bx4OtivIgileWVFA8gwYqV73OSrt1TeuGOEdNrIFwf0uZomNSL7WWOQqrwGqpXL4I43Mua8SkG5vEACoXid_aTyo0ANil4ujnBBNxwDHopF0a-sH0AZGNsiaSLPwq2DjLq34Dg7YsWK3Y4BJtpGuXeJJmSxdn17uZFuJ3ptPdh29YRlWPl6wke6fWfotS95O40ydg-eGqcrdOVpm4Ccy88QEzT7zOW0NRFTGAh3dI8SrzRlKPh-f-uCyRWYkL4gHLty07g23PnsCj6w2xccI5iqcCQelFBouJhOqDNSrga0mdwfZ6dOFle0zTBrHDsdFNgEpT2ruhZS1-BD05ds.9s3x86R6cDfY8q-IvQLIRw"; + public static string JWEKeyWrappingUnsignedInnerJWTWithAdditionalHeaderClaims = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiUnNhU2VjdXJpdHlLZXlfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmciLCJjdHkiOiJKV1QifQ.V6F0hmafOWK6eukXTf2wPgT7PKmL2EcQ91VGQAkZGpCbtj_soc2beY39mnmhRvhKyupb16i4cgCWbQokNPPRwbPZRbtEMGHguAICY4PNclPRd3Sehf3p-q5MmNktd4IryVvCiB3_ZjMBPk7lea2vUxx56_j6dJhSGGLA4XtuVVsACICALa5yG-e5yQSFH-gEAQwnhxt8iLrL-_BWI7Z-tf9kuB3CrBaO_y6cqWY-d6tIm2-gYqgO290OHjyLgFHsFJx2piyTXBrGwirRd4zSY6YKEkpWnhDiKfRdPR_BgQiJBlBbxX6INQFKsIgfxEarKI4yYUOZBArHsbePWIYtgA.0_fs7xERR3lI1u-1fQhKcw.yazlqwo_HJ-t2flnaX06qXGHZIxGB2Nj872jVvKy4x4NUVOXXStjRFaheCGueO2I9VkpqKOKuXHX96QTzPCRU9EeuxerGbTZPRpuzvIosfnhIWX4oFJHN4B4gdwxOcRVORrU8sY_AL1EDwFG__PE9F5tD7ctzeyyOKNJRyrJuS6N16JrrOc0GhjASv5gOWN1rs6toXLCXueDiBvvsw6BE23vfUuD-hnXBZjxTBu_NCEkagCj80YQb50NCgw6-PghaOU7IZ5dGo-so_6VSoIv05dO4oE19MEqNrEb8QsxJ_4YKPmcftcDLvVJl5S23i_I27QtKccqryvu1nF0d7nOnilYMHg7n-NRyigNxo_s5BxONttY3IiRnCLKU-hAGYhO.DwVlelyPWHvIU-RaIzgRrg"; // This token was signed with Default.SymmetricSigningCredentials and encryted with Default.SymmetricEncryptingCredentials. // This token includes one additional header claim: // { "cty", "JWT"}. - public static string JWEDirectEcryptionWithCtyInAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.._ZJWQnRHlma3GH2zlxmsbw.lWnl-nsdegs6v-nbvZXL5mP9fWI83aY2RzNjp3e8pJ_356Ai83RZni1ZL2pjrnfRdY92bQi-NfEnHO9udO1A7gWNoqHu9A-AXeOsDFvNhDPCgqH1cozpSPQ_G8urRV5G2rOaR842ZG_1A8XferrEpp1mib0nH2AmvEhRYPJm3xvEg4zjlqFK9_taTQeX8A46Fs4I85Ekjd_q1qQYrVXcYZ_1IOLEZXoCwN31M7PbwD-tXqGEWSbhwCm2e6y2ZQ_v-fYGSvMq_-61cIELKwlkYcQZzh3j2OZjYVnP7E8SJV63mYDmz4pPr5TOXXa0F2LXNdp_it3y2L4DfHDcXw9vwIpXfOOdO2RuJozocTSSWihYnxDqV93ONLQaSzg6kk3ZXSo9gFp7gRBZQaPlxme40Ue3IDgsOgtSYU7r8Fjs3Ln4-98ABAEWaRdAaSsIQD9Vf4XoA2hgl393BvIcp_1ODSXSu623WRNGfCEaPqRNBCqq7R0ArXcy8muD6vW85MhmGMOvSOWBbh0E2C5VcZ1iVdN2Yw8gtHos6LN5322p6aF9z81JHMB4_AFs1cLzLY5aMYMYnouy0lQW8G-3RCO3wQ.uJcdhgoE2u3dA5GDQwHGOQ"; + public static string JWEDirectEcryptionWithCtyInAdditionalHeaderClaims = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoiRGVmYXVsdFN5bW1ldHJpY1NlY3VyaXR5S2V5XzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9..FAp-a7aBBDcPqyPxNSd7lw.U40cYyp9sQdC_dJUi4tXULOn3haYWy3Y2hupuO_23ck_jjEGTrXGBd1QVWId9AFeDYDiuUkD1kIjj2dDp3Ct7VNBOMCe0rBW_A6tfWR92XmkMfykdOvod9D7BJYMlhVd_J_oSS_7a4KbyL9f2vX3rAI721dJLs55woRFIfcJhvUkFH14MhJ7XtA4JSKJaNjKRdthPfKyw2hV-5mYcYzvNPB4qeJMnCU9TQ8FgBRXZRS5L3ea7_hJvZFjJg1QYt6NOsPV4p5FEg8lqxbo1OZ1fgtiNewTcFUMi7_GE59YAs0tFaEYmq2ZMjSUAscVclO6M1CED8MNfuz7aWxlDz9fPAHtKA_Yv7OkQLYKbhD1ZJTB6w9FZaLV9UpVrDgqJOoZ8uT77RrOulC1Nvtg8bSTlpJmjmL8yfwJAjdxpq8eyzVwElsmehNVcucMDobCcktpSSwmUNnpVBK0N6gPUQtIehpHyumUAM3fl2wmzpyOwqEBZJpxl9k8WXrsRbbHJqO7X33x9MylVq3YWwjzhiZAemMFAzY18tSs31pHIlPUUDSjsOR4M73Z3TxyQSXRnMZw0dPzUCnJ-dmMz4WPtoks0g.pFKtbLR_yuxVCjhfy3V7DQ"; #endregion #region JWS tokens @@ -380,20 +380,20 @@ public static string SamlToken_Formated **/ // This token has the default 'typ' header claim value replaced with "TEST". - public static string JWSWithDifferentTyp = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IlRFU1QifQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.GWbkNNMRmd-58J38op7Pl3Y4HyM-jUUgCt_3jmMkHplRE7O8mw8eXtpUnHJPZkoCUQhb4nKxz-LUp8mcNDU4Pmr8Y9RcgvM9kUOdqjqElUldSULOb3QWyjVcbDIi_baWiSxaFOlp10A99iEQUCs-yb0-Cd1fvxINd58A7_a8lCTikHZPhrSS_RRZyL5V_PrBSMFSVFilYv858ghC9PZ34yQd71Bq6IDfhFLAZvx_Qv9MMfQ53mOEg0HXpUHONRRMxhFtUfHtwbhwmSmbQOcodEUN5PmTKaIh1SXLZIOT53jdn2ahKv1NhIiGDgUBeRsNPna8GQvyCyTX5TdBASOU_Q"; + public static string JWSWithDifferentTyp = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IlRFU1QiLCJjdHkiOiJKV1QifQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.iBts9C885qebjet7hUpEEHEaLft90sQ3nlu28G7hAS15KjjZkJIZ85CsM-6MKLDeC4OXpE7QRr6IJ2QG1hF-UYJY4oamcRNgIjX_Qwm4kbFdvTEL-Eg3HsijNCcPaYI_FfzR-cxL10dv7ncPa8dgxQs550EbuKH5O1gr_p2dJvsVRU5wk45T8FEArRF0zK6pGm3DJCklv29v5sWdDlY9tHst2jGvGq8Rzid-VC6ptcliUdcjWxi7tUT7QHG2p-UhT-sSku61UoFCD5EkpWlzR2A2IaR_Vzdh47Yg8pt5z_7h0aR4ljxN1O14CVyBJL45vZLGnCr1Oq8BCrEtY7Vvow"; // This token includes two additional header claims: // { "int", 123 } and { "string", "string" }. - public static string JWSWithMultipleAdditionalHeaderClaims = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmcifQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.wW0j9ddiisdKVQJISIRcAQLwjL0U6xGRmGva6ospVhiE96aldAMcKlzKzBMWBZkQV-3ViAA31zF4MFVFdjplsppD8wjzLyFQtbCpiSq9XZ_9DEh5oABSpOSESiTK1x69ag1iJwztxGtSAvtMwqme8GmMC4ahIhzjpjEBndqXYVNA19523d06SJh6vAlQGTEt1gmBVojKA_Fjf2G-pOH5_fgoOSBLcpf96GbvmmUGCX4UUqqxhCXd-0LKQdq7M6hIkDu8BY1RKHxK5FDLB2Bbh5Ir7oGInjUbe7xfNoCYifWWs1bVyJ14w2V4XnwJfBUj2lyMsF5WBhL81HH950Rp3A"; + public static string JWSWithMultipleAdditionalHeaderClaims = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJzdHJpbmciOiJzdHJpbmciLCJjdHkiOiJKV1QifQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.iQ81lXGKaCUGp4H6J9-9nHWlmSbtJRKFi-Y6FH0LfVN4lVau10l1sFEkFtMiEfUMc9VTh83ODpHjoV4OJUdniILhmDp6Skaafj0TASYWq1CEWOxTHbmYTPJvTK0eWWYKvcIghrX0CiqnKdxl0vtXSF7zE03g1KGJa7NwVUqvGTEU4CAl2NAdDKBPq8zH7lkknRMXcjrFsLV-U6Iqi561tIRWOCGd3qQh0YdY13SzXkPFY1FnZ4QFEuuqLARK5ngUGlXLSlD53UDwxouYD6MDh0GbV26ayfYmITWdhuAWk5G-1NA2RBDNOukeMtNuI8_tP6K6KCFTFPQCitmpIDvFQA"; // This token includes one additional header claim: // { "int", 123 }. - public static string JWSWithSingleAdditionalHeaderClaim = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzfQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.DhPiCtD9HWTjG5LDCW8YxSaBXffmPosGnnKINuey6ec50Yf72SzBnMDVZ4Cw9S_SyqSRIxVs0x87g0ZUP8fytUxr_D7ksf0cBI9tqh2MgoAZ2lY8T8oflfIBaTLraZHRmjRCMZGdOLmGj__xqM7mmD0Y1grwAkQgMCLlze2qgCXmym_8jAWfSLQcNc-XNUaDZBlbgebic7TZ0INa93QcJvm_ov6t_rg90Y0l4xCxL_VOdXctdbc5D87bEaaAdqThfVMA1325JZdS_CBWVelLf5zZYPldVDxnD9l93Fy0gqWTWJ0QxMP-BDMgXbQQdUDoSC5HrxXU2JRXnF8V_V4G2g"; + public static string JWSWithSingleAdditionalHeaderClaim = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikpzb25XZWJLZXlSc2FfMjA0OCIsInR5cCI6IkpXVCIsImludCI6MTIzLCJjdHkiOiJKV1QifQ.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ.fWO-KSojdz7iMjdP3z9XNE3qQO71lrltuvuLqHrN60U86b_gHOHX-dFs_W0GVbn1o8sfKOUbbmi12-CSyMC4Xk3WFdGx9W8qWrzD06-ZbXVNF3sOOjKDRmEU_xEp2wfTLY7vhrmtVCHWdrfExYv6pkgpt5e_eaog6t1O_c3qsbq_7wtWTbvhvuSr9giqPrclZm0iLlBV3MQ6x8ZLD8IFrOeO3CmGdpkKx6rjcV_73iRM8ErnpLUHW-UqkkdF4rwG-NEMqGz9gXPQHXGDJ1J4hUF1RidRsnjcfvUCaReeHktwv6fMteQ-WJwGWppTNdZ9qFMxhbOefOIMKBcNfoy15w"; // This token is unsigned and includes one additional header claim: // { "int", 123 }. - public static string UnsignedJWSWithSingleAdditionalHeaderClaim = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwiaW50IjoxMjN9.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ."; + public static string UnsignedJWSWithSingleAdditionalHeaderClaim = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwiaW50IjoxMjMsImN0eSI6IkpXVCJ9.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTQ4OTc3NTYxNyIsIm5iZiI6IjE0ODk3NzU2MTciLCJleHAiOiIyNTM0MDIzMDA3OTkifQ."; #endregion } } diff --git a/test/Microsoft.IdentityModel.TestUtils/References.cs b/test/Microsoft.IdentityModel.TestUtils/References.cs index e30cfd4749..6eb7e53f36 100644 --- a/test/Microsoft.IdentityModel.TestUtils/References.cs +++ b/test/Microsoft.IdentityModel.TestUtils/References.cs @@ -25,6 +25,7 @@ // //------------------------------------------------------------------------------ +using System.IO.Compression; using Microsoft.IdentityModel.Tokens; namespace Microsoft.IdentityModel.TestUtils @@ -490,6 +491,132 @@ public static string EncodedEncryptedKey } } + // https://datatracker.ietf.org/doc/html/rfc7518#appendix-C + public static class ECDH_ES + { + public static byte[] AlgorithmID = new byte[] { 0, 0, 0, 7, 65, 49, 50, 56, 71, 67, 77 }; + + public static JsonWebKey AliceEphereralPrivateKey => + new JsonWebKey + { + Crv = "P-256", + D = "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo", + Kty = "EC", + X = "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", + Y = "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" + }; + + public static string AliceEphereralPrivateKeyString => + @"{ + ""kty"":""EC"", + ""crv"":""P-256"", + ""x"":""gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0"", + ""y"":""SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps"", + ""d"":""0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"" + }"; + + public static JsonWebKey AliceEphereralPublicKey => + new JsonWebKey + { + Crv = "P-256", + Kty = "EC", + X = "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0", + Y = "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" + }; + + public static string AliceEphereralPublicKeyString => + @"{ + ""kty"":""EC"", + ""crv"":""P-256"", + ""x"":""gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0"", + ""y"":""SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps"" + }"; + + public static JsonWebKey BobEphereralPrivateKey => + new JsonWebKey + { + Crv = "P-256", + D = "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw", + Kty = "EC", + X = "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", + Y = "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck" + }; + + public static string BobEphereralPrivateKeyString => + @"{ + ""kty"":""EC"", + ""crv"":""P-256"", + ""x"":""weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ"", + ""y"":""e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"", + ""d"":""VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"" + }"; + + public static JsonWebKey BobEphereralPublicKey => + new JsonWebKey + { + Crv = "P-256", + Kty = "EC", + X = "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", + Y = "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck" + }; + + public static string BobEphereralPublicString => + @"{ + ""kty"":""EC"", + ""crv"":""P-256"", + ""x"":""weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ"", + ""y"":""e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"" + }"; + + public static byte[] ConcatKDF = + new byte[] { 0, 0, 0, 1, + 158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132, 38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121, 140, 254, 144, 196, + 0, 0, 0, 7, 65, 49, 50, 56, 71, 67, 77, + 0, 0, 0, 5, 65, 108, 105, 99, 101, + 0, 0, 0, 3, 66, 111, 98, 0, 0, 0, 128}; + + public static byte[] DerivedKeyBytes = new byte[] { 86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26 }; + + public static string DerivedKeyEncoded = "VqqN6vgjbSBcIijNcacQGg"; + + public static string EPKString => + @"{ + ""alg"":""ECDH-ES"", + ""enc"":""A128GCM"", + ""apu"":""QWxpY2U"", + ""apv"":""Qm9i"", + ""epk"": + { + ""kty"":""EC"", + ""crv"":""P-256"", + ""x"":""gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0"", + ""y"":""SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps"" + } + }"; + + public static string Alg = "ECDH-ES"; + + public static string Enc = "A128GCM"; + + public static string Apu = "QWxpY2U"; + + public static string Apv = "Qm9i"; + + public static int KeyDataLen = 128; + + public static byte[] PartyUInfo = new byte[] { 0, 0, 0, 5, 65, 108, 105, 99, 101 }; + + public static byte[] PartyVInfo = new byte[] { 0, 0, 0, 3, 66, 11, 98 }; + + public static byte[] OtherInfo = new byte[] { 0, 0, 0, 7, 65, 49, 50, 56, 71, 67, 77, 0, 0, 0, 5, 65, 108, 105, 99, 101, 0, 0, 0, 3, 66, 111, 98, 0, 0, 0, 128 }; + + public static byte[] SuppPubInfo = new byte[] { 0, 0, 0, 128 }; + + public static byte[] SuppPrivInfo = new byte[] { }; + + public static byte[] Z => new byte[] { 158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132, 38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121, 140, 254, 144, 196 }; + } + // https://datatracker.ietf.org/doc/html/rfc7516#appendix-A.1.3 // A.1.3 Key wrap: RSAES-OAEP + JsonWebKey public static class RSAES_OAEP_KeyWrap diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs index 09b8167d98..f9ed3b50ce 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs @@ -48,7 +48,7 @@ public void AsymmetricAdapterUsageTests(AsymmetricAdapterTheoryData theoryData) try { -#if NET461 || NET472 || NETCOREAPP2_1 +#if NET461 || NET472 || NETCOREAPP2_1 || NETCOREAPP3_1 AsymmetricAdapter asymmetricdapter = new AsymmetricAdapter(theoryData.SecurityKey, theoryData.Algorithm, hashAlgorithm, SupportedAlgorithms.GetHashAlgorithmName(theoryData.Algorithm), true); #else AsymmetricAdapter asymmetricdapter = new AsymmetricAdapter(theoryData.SecurityKey, theoryData.Algorithm, hashAlgorithm, true); @@ -82,7 +82,7 @@ public static TheoryData AsymmetricAdapterUsageTest // RSA // RSACertificateExtensions.GetRSAPrivateKey - this results in - #if NET461 || NET472 || NETCOREAPP2_1 + #if NET461 || NET472 || NETCOREAPP2_1 || NETCOREAPP3_1 new AsymmetricAdapterTheoryData { Algorithm = SecurityAlgorithms.RsaSha256, @@ -102,7 +102,7 @@ public static TheoryData AsymmetricAdapterUsageTest }, // RSA.Create - #if NET472 || NETCOREAPP2_1 + #if NET472 || NETCOREAPP2_1 || NETCOREAPP3_1 new AsymmetricAdapterTheoryData { Algorithm = SecurityAlgorithms.RsaSha256, @@ -140,7 +140,7 @@ public static TheoryData AsymmetricAdapterUsageTest TestId = "KeyingMaterial_Ecdsa256Key" }, - #if NET472 || NETCOREAPP2_1 + #if NET472 || NETCOREAPP2_1 || NETCOREAPP3_1 new AsymmetricAdapterTheoryData { Algorithm = SecurityAlgorithms.EcdsaSha256, diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs index 6ce6e1c049..30342acbee 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs @@ -291,7 +291,7 @@ public static TheoryData TryAddTheoryData SignatureProvider = new SymmetricSignatureProvider(KeyingMaterial.DefaultSymmetricSecurityKey_384, ALG.HmacSha384, true), TestId = nameof(KeyingMaterial.DefaultSymmetricSecurityKey_256) }, -#if NET472 || NET_CORE +#if NET472 || NET_CORE // ecdsa signature provider should be added to the cache on NET472 and NET_CORE. new CryptoProviderCacheTheoryData { diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/EcdhEsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/EcdhEsTests.cs new file mode 100644 index 0000000000..ec7dc3f833 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/EcdhEsTests.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +using System; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +using KEY = Microsoft.IdentityModel.TestUtils.KeyingMaterial; + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant +#if NET472 || NETCOREAPP3_1 +namespace Microsoft.IdentityModel.Tokens.Tests +{ + public class EcdhEsTests + { + [Theory, MemberData(nameof(CreateEcdhEsTestCases))] + public void EcdhEsKeyExchangeProviderTests(EcdhEsTheoryData theoryData) + { + var context = new CompareContext(); + // arrange + string alg = theoryData.Algorithm; + string enc = theoryData.Encryption; + string apuProducer = theoryData.ApuProducer; + string apvProducer = theoryData.ApvProducer; + string apuConsumer = theoryData.ApuConsumer; + string apvConsumer = theoryData.ApvConsumer; + + var aliceKeyExchangeProvider = new EcdhKeyExchangeProvider(theoryData.PrivateKeySender, theoryData.PublicKeyReceiver, alg, enc); + var bobKeyExchangeProvider = new EcdhKeyExchangeProvider(theoryData.PrivateKeyReceiver, theoryData.PublicKeySender, alg, enc); + + // act + SecurityKey aliceCek = aliceKeyExchangeProvider.GenerateKdf(apuProducer, apvProducer); + SecurityKey bobCek = bobKeyExchangeProvider.GenerateKdf(apuConsumer, apvConsumer); + + // assert + // compare KDFs are the same + if (theoryData.MatchingKdfs && !Utility.AreEqual(((SymmetricSecurityKey)aliceCek).Key, ((SymmetricSecurityKey)bobCek).Key)) + context.AddDiff($"theoryData.MatchingKdfs && !Utility.AreEqual(aliceCek, bobCek)"); + if (!theoryData.MatchingKdfs && Utility.AreEqual(((SymmetricSecurityKey)aliceCek).Key, ((SymmetricSecurityKey)bobCek).Key)) + context.AddDiff($"!theoryData.MatchingKdfs && Utility.AreEqual(aliceCek, bobCek)"); + + TestUtilities.AssertFailIfErrors(context); + } + + + [Theory, MemberData(nameof(CreateEcdhEsTestCases))] + public void CreateEcdhEsTests(EcdhEsTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.CreateEcdhEsTests", theoryData); + try + { + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData CreateEcdhEsTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + theoryData.Add(KeyExchangeEcdhEsA256kwTestPass()); + theoryData.Add(KeyExchangeEcdhEsA256kwTestPassNullApuApv()); + theoryData.Add(KeyExchangeEcdhEsA256kwTestFailDifferentApu()); + theoryData.Add(KeyExchangeEcdhEsA256kwTestFailDifferentApv()); + theoryData.Add(KeyExchangeEcdhEsA256kwTestFailDifferentApuApv()); + return theoryData; + } + } + + private static EcdhEsTheoryData KeyExchangeEcdhEsA256kwTestPass() => new EcdhEsTheoryData("KeyExchangeEcdhEsA256kwTestPass") + { + First = true, + Algorithm = SecurityAlgorithms.EcdhEsA256kw, + Encryption = SecurityAlgorithms.Aes128CbcHmacSha256, + ApuProducer = KEY.ApuExample1, + ApvProducer = KEY.ApvExample1, + ApuConsumer = KEY.ApuExample1, + ApvConsumer = KEY.ApvExample1, + PrivateKeySender = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + PublicKeyReceiver = KEY.JsonWebKeyP256_Public, + PublicKeySender = KEY.JsonWebKeyP256_Public, + PrivateKeyReceiver = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + MatchingKdfs = true + }; + + private static EcdhEsTheoryData KeyExchangeEcdhEsA256kwTestPassNullApuApv() => new EcdhEsTheoryData("KeyExchangeEcdhEsA256kwTestPassNullApuApv") + { + Algorithm = SecurityAlgorithms.EcdhEsA256kw, + Encryption = SecurityAlgorithms.Aes128CbcHmacSha256, + ApuProducer = null, + ApvProducer = null, + ApuConsumer = null, + ApvConsumer = null, + PrivateKeySender = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + PublicKeyReceiver = KEY.JsonWebKeyP256_Public, + PublicKeySender = KEY.JsonWebKeyP256_Public, + PrivateKeyReceiver = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + MatchingKdfs = true + }; + + private static EcdhEsTheoryData KeyExchangeEcdhEsA256kwTestFailDifferentApu() => new EcdhEsTheoryData("KeyExchangeEcdhEsA256kwTestFailDifferentApu") + { + Algorithm = SecurityAlgorithms.EcdhEsA256kw, + Encryption = SecurityAlgorithms.Aes128CbcHmacSha256, + ApuProducer = KEY.ApuExample1, + ApvProducer = KEY.ApvExample1, + ApuConsumer = KEY.ApuExample2, + ApvConsumer = KEY.ApvExample1, + PrivateKeySender = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + PublicKeyReceiver = KEY.JsonWebKeyP256_Public, + PublicKeySender = KEY.JsonWebKeyP256_Public, + PrivateKeyReceiver = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + MatchingKdfs = false + }; + + private static EcdhEsTheoryData KeyExchangeEcdhEsA256kwTestFailDifferentApv() => new EcdhEsTheoryData("KeyExchangeEcdhEsA256kwTestFailDifferentApv") + { + Algorithm = SecurityAlgorithms.EcdhEsA256kw, + Encryption = SecurityAlgorithms.Aes128CbcHmacSha256, + ApuProducer = KEY.ApuExample1, + ApvProducer = KEY.ApvExample1, + ApuConsumer = KEY.ApuExample1, + ApvConsumer = KEY.ApvExample2, + PrivateKeySender = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + PublicKeyReceiver = KEY.JsonWebKeyP256_Public, + PublicKeySender = KEY.JsonWebKeyP256_Public, + PrivateKeyReceiver = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + MatchingKdfs = false + }; + + private static EcdhEsTheoryData KeyExchangeEcdhEsA256kwTestFailDifferentApuApv() => new EcdhEsTheoryData("KeyExchangeEcdhEsA256kwTestFailDifferentApuApv") + { + Algorithm = SecurityAlgorithms.EcdhEsA256kw, + Encryption = SecurityAlgorithms.Aes128CbcHmacSha256, + ApuProducer = KEY.ApuExample1, + ApvProducer = KEY.ApvExample1, + ApuConsumer = KEY.ApuExample2, + ApvConsumer = KEY.ApvExample2, + PrivateKeySender = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + PublicKeyReceiver = KEY.JsonWebKeyP256_Public, + PublicKeySender = KEY.JsonWebKeyP256_Public, + PrivateKeyReceiver = new ECDsaSecurityKey(KEY.JsonWebKeyP256, true), + MatchingKdfs = false + }; + + public class EcdhEsTheoryData : TheoryDataBase + { + public EcdhEsTheoryData(string testId) + { + TestId = testId; + } + public ECDsaSecurityKey PrivateKeySender { get; set; } + public ECDsaSecurityKey PrivateKeyReceiver { get; set; } + public JsonWebKey PublicKeyReceiver { get; set; } + public JsonWebKey PublicKeySender { get; set; } + public string Algorithm { get; set; } + public string Encryption { get; set; } + public string ApuProducer { get; set; } + public string ApvProducer { get; set; } + public string ApuConsumer { get; set; } + public string ApvConsumer { get; set; } + public bool MatchingKdfs { get; set; } + } + } +} +#endif +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/JweUsingEchdTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/JweUsingEchdTests.cs new file mode 100644 index 0000000000..46f1c164c1 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/JweUsingEchdTests.cs @@ -0,0 +1,351 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//------------------------------------------------------------------------------ + +#if NET472 || NETCOREAPP3_1 + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.IdentityModel.Json.Linq; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; +using Xunit; + + +using KEY = Microsoft.IdentityModel.TestUtils.KeyingMaterial; + +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + +namespace Microsoft.IdentityModel.Tokens.Tests +{ + public class JweUsingEcdhEsTests + { + [Theory, MemberData(nameof(CreateEcdhEsTestcases))] + public void CreateJweEcdhEsTests(CreateEcdhEsTheoryData theoryData) + { + var context = TestUtilities.WriteHeader($"{this}.CreateJweEcdhEsTests", theoryData); + try + { + JsonWebTokenHandler jsonWebTokenHandler = new JsonWebTokenHandler(); + + // Do we need an extension to EncryptingCredentials for: ApuSender, ApvSender + string jwe = jsonWebTokenHandler.CreateToken( + Default.PayloadString, + Default.AsymmetricSigningCredentials, + theoryData.EncryptingCredentials, + theoryData.AdditionalHeaderParams); + + JsonWebToken jsonWebToken = new JsonWebToken(jwe); + // we need the ECDSASecurityKey for the receiver to validate, use TokenValidationParameters.TokenDecryptionKey + TokenValidationResult tokenValidationResult = jsonWebTokenHandler.ValidateToken(jwe, theoryData.TokenValidationParameters); + + // adjusted for theoryData.ExpectedException == tokenValidationResult.Exception + if (tokenValidationResult.IsValid != theoryData.ExpectedIsValid) + context.AddDiff($"tokenValidationResult.IsValid != theoryData.ExpectedIsValid"); + + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData CreateEcdhEsTestcases + { + get + { + TheoryData theoryData = new TheoryData(); + + theoryData.Add(EcdhEsCurveP256AEnc256KW()); + theoryData.Add(EcdhEsCurveP256AEnc256KWNullApuApv()); + theoryData.Add(EcdhEsCurveP384EncA256KW()); + theoryData.Add(EcdhEsCurveP512EncA256KW()); + theoryData.Add(EcdhEsCurveP256EncA192KW()); + theoryData.Add(EcdhEsCurveP256EncA128KW()); + + return theoryData; + } + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP256AEnc256KW() + { + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP256AEnc256KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP256_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP256_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP256, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = "SenderInfo", + ApvSender = "ReceivererInfo" + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + + return testData; + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP256AEnc256KWNullApuApv() + { + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP256AEnc256KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP256_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP256_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP256, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = null, + ApvSender = null + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + + return testData; + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP384EncA256KW() + { + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP384EncA256KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP384, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP384_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP384_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP384_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP384, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP384, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = "SenderInfo", + ApvSender = "ReceivererInfo" + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + //testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, testData.PublicKeySender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + // APU, APV different + return testData; + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP512EncA256KW() + { + // use of 521 is actually 512 + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP512EncA256KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true), + SecurityAlgorithms.EcdhEsA256kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP521_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP521_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP521_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP521, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP521, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = "SenderInfo", + ApvSender = "ReceivererInfo" + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + + return testData; + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP256EncA192KW() + { + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP256EncA192KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + SecurityAlgorithms.EcdhEsA192kw, + SecurityAlgorithms.Aes192CbcHmacSha384) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP256_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP256_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP256, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = "SenderInfo", + ApvSender = "ReceivererInfo" + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + + return testData; + } + + private static CreateEcdhEsTheoryData EcdhEsCurveP256EncA128KW() + { + CreateEcdhEsTheoryData testData = new CreateEcdhEsTheoryData("EcdhEsCurveP256EncA128KW") + { + EncryptingCredentials = new EncryptingCredentials( + new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + SecurityAlgorithms.EcdhEsA128kw, + SecurityAlgorithms.Aes128CbcHmacSha256) + { + KeyExchangePublicKey = KeyingMaterial.JsonWebKeyP256_Public + }, + PublicKeyReceiver = KeyingMaterial.JsonWebKeyP256_Public, + PublicKeySender = KeyingMaterial.JsonWebKeyP256_Public, + PrivateKeyReceiver = KeyingMaterial.JsonWebKeyP256, + TokenValidationParameters = new TokenValidationParameters() + { + TokenDecryptionKey = new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, true), + ValidAudience = Default.Audience, + ValidIssuer = Default.Issuer, + IssuerSigningKey = Default.AsymmetricSigningKey + }, + ApuSender = "SenderInfo", + ApvSender = "ReceivererInfo" + }; + + var epkJObject = new JObject(); + epkJObject.Add(JsonWebKeyParameterNames.Kty, testData.PublicKeySender.Kty); + epkJObject.Add(JsonWebKeyParameterNames.Crv, testData.PublicKeySender.Crv); + epkJObject.Add(JsonWebKeyParameterNames.X, testData.PublicKeySender.X); + epkJObject.Add(JsonWebKeyParameterNames.Y, testData.PublicKeySender.Y); + testData.AdditionalHeaderParams = new Dictionary(); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apu, testData.ApuSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Apv, testData.ApvSender); + testData.AdditionalHeaderParams.Add(JwtHeaderParameterNames.Epk, epkJObject); + + return testData; + } + } + + public class CreateEcdhEsTheoryData : TheoryDataBase + { + public CreateEcdhEsTheoryData(string testId) + { + TestId = testId; + } + + public string ApuReceiver { get; set; } + public string ApvReceiver { get; set; } + public string ApuSender { get; set; } + public string ApvSender { get; set; } + public EncryptingCredentials EncryptingCredentials { get; set; } + public JsonWebKey PrivateKeyReceiver { get; set; } + public JsonWebKey PublicKeyReceiver { get; set; } + public JsonWebKey PublicKeySender { get; set; } + public TokenValidationParameters TokenValidationParameters { get; set; } + public bool ExpectedIsValid { get; set; } = true; + public IDictionary AdditionalHeaderParams { get; set; } + } +} + +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant +#endif // !NET45 diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs index 6b009a4367..5ea4ba88e8 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs @@ -26,7 +26,12 @@ //------------------------------------------------------------------------------ using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens; using Xunit; #pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant @@ -42,6 +47,38 @@ public class ReferenceTests #if NET_CORE [PlatformSpecific(TestPlatforms.Windows)] #endif +#if NET472 || NETCOREAPP3_1 + [Fact] + public void ECDH_ESReferenceTest() + { + var context = new CompareContext(); + // arrange + string alg = ECDH_ES.Alg; + string enc = ECDH_ES.Enc; + string apu = ECDH_ES.Apu; + string apv = ECDH_ES.Apv; + + var aliceEcdsaSecurityKey = new ECDsaSecurityKey(ECDH_ES.AliceEphereralPrivateKey, true); + var aliceKeyExchangeProvider = new EcdhKeyExchangeProvider(aliceEcdsaSecurityKey, ECDH_ES.BobEphereralPublicKey, alg, enc); + + var bobEcdsaSecurityKey = new ECDsaSecurityKey(ECDH_ES.BobEphereralPrivateKey, true); + var bobKeyExchangeProvider = new EcdhKeyExchangeProvider(bobEcdsaSecurityKey, ECDH_ES.AliceEphereralPublicKey, alg, enc); + + // act + SecurityKey aliceCek = aliceKeyExchangeProvider.GenerateKdf(apu, apv); + SecurityKey bobCek = bobKeyExchangeProvider.GenerateKdf(apu, apv); + + // assert + // compare KDFs are the same and they're matching with expected + if (!Utility.AreEqual(((SymmetricSecurityKey)aliceCek).Key, ((SymmetricSecurityKey)bobCek).Key)) + context.AddDiff($"!Utility.AreEqual(aliceCek, bobCek)"); + if (!Utility.AreEqual(((SymmetricSecurityKey)aliceCek).Key, ECDH_ES.DerivedKeyBytes)) + context.AddDiff($"!Utility.AreEqual(aliceCek, ECDH_ES.DerivedKeyBytes)"); + + TestUtilities.AssertFailIfErrors(context); + } +#endif + [Fact] public void AesGcmReferenceTest() {