From df77be658cc10db000e7e15cf7f9f28188bf84c0 Mon Sep 17 00:00:00 2001 From: Brent Schmaltz Date: Fri, 28 Jul 2023 06:12:19 -0700 Subject: [PATCH] First pass at JsonWebKey and JsonWebKeySet using system.text.json --- .../OpenIdConnectConfigurationRetriever.cs | 2 +- .../Json/EncodedJsonWebKeyParameterNames.cs | 271 +++++++ .../Json/JsonSerializerPrimitives.cs | 243 ++++-- .../Json/JsonWebKeySerializer.cs | 440 +++++++++++ .../Json/JsonWebKeySetSerializer.cs | 140 ++++ .../JsonWebKey.cs | 286 ++++--- .../JsonWebKeyParameterNames.cs | 40 +- .../JsonWebKeySet.cs | 25 +- .../LogMessages.cs | 1 + .../SecurityKey.cs | 43 +- .../OpenIdConfigData.cs | 4 - .../SignedHttpRequestUtilityTests.cs | 4 +- .../CompareContext.cs | 4 +- .../DataSets.cs | 703 ++++++++--------- .../IdentityComparer.cs | 719 +++++++++--------- .../ECDsaAdapterTests.cs | 14 +- .../IdentityComparerTests.cs | 6 +- .../Json/JsonTestClassSerializer.cs | 23 +- .../Json/JsonUtilities.cs | 167 ++++ .../Json/JsonWebKey6x.cs | 482 ++++++++++++ .../Json/JsonWebKeySerializationTests.cs | 442 +++++++++++ .../Json/JsonWebKeySet6x.cs | 95 +++ .../Json/JsonWebKeySetSerializationTests.cs | 94 +++ .../Json/JsonWebKeySetTheoryData.cs | 23 + .../Json/JsonWebKeyTheoryData.cs | 34 + .../Json/SecurityKey6x.cs | 126 +++ .../JsonWebKeySetTests.cs | 160 ++-- .../JsonWebKeyTests.cs | 74 +- ...icrosoft.IdentityModel.Tokens.Tests.csproj | 12 +- .../JwtHeaderTests.cs | 2 +- 30 files changed, 3631 insertions(+), 1048 deletions(-) create mode 100644 src/Microsoft.IdentityModel.Tokens/Json/EncodedJsonWebKeyParameterNames.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs create mode 100644 src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySetSerializer.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonUtilities.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKey6x.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySerializationTests.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySet6x.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetSerializationTests.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetTheoryData.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeyTheoryData.cs create mode 100644 test/Microsoft.IdentityModel.Tokens.Tests/Json/SecurityKey6x.cs diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs index bb17d074ec..22f7dc5421 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs @@ -79,7 +79,7 @@ public static async Task GetAsync(string address, ID if (LogHelper.IsEnabled(EventLogLevel.Verbose)) LogHelper.LogVerbose(LogMessages.IDX21813, openIdConnectConfiguration.JwksUri); - openIdConnectConfiguration.JsonWebKeySet = JsonConvert.DeserializeObject(keys); + openIdConnectConfiguration.JsonWebKeySet = new JsonWebKeySet(keys); foreach (SecurityKey key in openIdConnectConfiguration.JsonWebKeySet.GetSigningKeys()) { openIdConnectConfiguration.SigningKeys.Add(key); diff --git a/src/Microsoft.IdentityModel.Tokens/Json/EncodedJsonWebKeyParameterNames.cs b/src/Microsoft.IdentityModel.Tokens/Json/EncodedJsonWebKeyParameterNames.cs new file mode 100644 index 0000000000..9a5828d148 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Json/EncodedJsonWebKeyParameterNames.cs @@ -0,0 +1,271 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Text.Encodings.Web; +using System.Text.Json; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// JsonEncodedText used with utf8Writer when writing property names for JsonWebKey. + /// Common names are initialized on startup, lazy for others. + /// + internal static class EncodedJsonWebKeyParameterNames + { + internal static bool _algSet; + internal static JsonEncodedText _alg; + internal static bool _crvSet; + internal static JsonEncodedText _crv; + internal static bool _dSet; + internal static JsonEncodedText _d; + internal static bool _dpSet; + internal static JsonEncodedText _dp; + internal static bool _dqSet; + internal static JsonEncodedText _dq; + internal static bool _kSet; + internal static JsonEncodedText _k; + internal static bool _keyopsSet; + internal static JsonEncodedText _keyOps; + internal static bool _othSet; + internal static JsonEncodedText _oth; + internal static bool _pSet; + internal static JsonEncodedText _p; + internal static bool _qSet; + internal static JsonEncodedText _q; + internal static bool _qiSet; + internal static JsonEncodedText _qi; + internal static bool _x5tS256Set; + internal static JsonEncodedText _x5tS256; + internal static bool _x5uSet; + internal static JsonEncodedText _x5u; + internal static bool _xSet; + internal static JsonEncodedText _x; + internal static bool _ySet; + internal static JsonEncodedText _y; + + public static JsonEncodedText Alg + { + get + { + if (!_algSet) + { + _alg = JsonEncodedText.Encode(JsonWebKeyParameterNames.Alg, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _algSet = true; + } + + return _alg; + } + } + + public static JsonEncodedText Crv + { + get + { + if (!_crvSet) + { + _crv = JsonEncodedText.Encode(JsonWebKeyParameterNames.Crv, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _crvSet = true; + } + + return _crv; + } + } + + public static JsonEncodedText D + { + get + { + if (!_dSet) + { + _d = JsonEncodedText.Encode(JsonWebKeyParameterNames.D, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _dSet = true; + } + + return _d; + } + } + + public static JsonEncodedText DP + { + get + { + if (!_dpSet) + { + _dp = JsonEncodedText.Encode(JsonWebKeyParameterNames.DP, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _dpSet = true; + } + + return _dp; + } + } + + public static JsonEncodedText DQ + { + get + { + if (!_dqSet) + { + _dq = JsonEncodedText.Encode(JsonWebKeyParameterNames.DQ, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _dqSet = true; + } + + return _dq; + } + } + + public static readonly JsonEncodedText E = JsonEncodedText.Encode(JsonWebKeyParameterNames.E, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static JsonEncodedText K + { + get + { + if (!_kSet) + { + _k = JsonEncodedText.Encode(JsonWebKeyParameterNames.K, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _kSet = true; + } + + return _k; + } + } + + public static JsonEncodedText KeyOps + { + get + { + if (!_keyopsSet) + { + _keyOps = JsonEncodedText.Encode(JsonWebKeyParameterNames.KeyOps, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _keyopsSet = true; + } + + return _keyOps; + } + } + + public static readonly JsonEncodedText Keys = JsonEncodedText.Encode(JsonWebKeyParameterNames.Keys, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static readonly JsonEncodedText Kid = JsonEncodedText.Encode(JsonWebKeyParameterNames.Kid, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static readonly JsonEncodedText Kty = JsonEncodedText.Encode(JsonWebKeyParameterNames.Kty, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static readonly JsonEncodedText N = JsonEncodedText.Encode(JsonWebKeyParameterNames.N, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static JsonEncodedText Oth + { + get + { + if (!_othSet) + { + _oth = JsonEncodedText.Encode(JsonWebKeyParameterNames.Oth, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _othSet = true; + } + + return _oth; + } + } + + public static JsonEncodedText P + { + get + { + if (!_pSet) + { + _p = JsonEncodedText.Encode(JsonWebKeyParameterNames.P, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _pSet = true; + } + + return _p; + } + } + public static JsonEncodedText Q + { + get + { + if (!_qSet) + { + _q = JsonEncodedText.Encode(JsonWebKeyParameterNames.Q, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _qSet = true; + } + + return _q; + } + } + + public static JsonEncodedText QI + { + get + { + if (!_qiSet) + { + _qi = JsonEncodedText.Encode(JsonWebKeyParameterNames.QI, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _qiSet = true; + } + + return _qi; + } + } + + public static readonly JsonEncodedText Use = JsonEncodedText.Encode(JsonWebKeyParameterNames.Use, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static readonly JsonEncodedText X5c = JsonEncodedText.Encode(JsonWebKeyParameterNames.X5c, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static readonly JsonEncodedText X5t = JsonEncodedText.Encode(JsonWebKeyParameterNames.X5t, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + + public static JsonEncodedText X5tS256 + { + get + { + if (!_x5tS256Set) + { + _x5tS256 = JsonEncodedText.Encode(JsonWebKeyParameterNames.X5tS256, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _x5tS256Set = true; + } + + return _x5tS256; + } + } + + public static JsonEncodedText X5u + { + get + { + if (!_x5uSet) + { + _x5u = JsonEncodedText.Encode(JsonWebKeyParameterNames.X5u, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _x5uSet = true; + } + + return _x5u; + } + } + + public static JsonEncodedText X + { + get + { + if (!_xSet) + { + _x = JsonEncodedText.Encode(JsonWebKeyParameterNames.X, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _xSet = true; + } + + return _x; + } + } + + public static JsonEncodedText Y + { + get + { + if (!_ySet) + { + _y = JsonEncodedText.Encode(JsonWebKeyParameterNames.Y, JavaScriptEncoder.UnsafeRelaxedJsonEscaping); + _ySet = true; + } + + return _y; + } + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs index 24586fe3b6..94606b1a15 100644 --- a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text.Json; using Microsoft.IdentityModel.Logging; @@ -60,7 +61,7 @@ internal static Exception CreateJsonReaderExceptionInvalidType(ref Utf8JsonReade internal static string GetPropertyName(ref Utf8JsonReader reader, string className, bool advanceReader) { if (reader.TokenType == JsonTokenType.None) - reader.Read(); + ReaderRead(ref reader); if (reader.TokenType != JsonTokenType.PropertyName) throw LogHelper.LogExceptionMessage(CreateJsonReaderExceptionInvalidType(ref reader, "JsonTokenType.PropertyName", string.Empty, className)); @@ -68,186 +69,259 @@ internal static string GetPropertyName(ref Utf8JsonReader reader, string classNa if (advanceReader) { string propertyName = reader.GetString(); - reader.Read(); + ReaderRead(ref reader); return propertyName; } return reader.GetString(); } + /// + /// This method is called when deserializing a known type where the JSON property does not map to a type property. + /// We put the object into a Dictionary[string, object]. + /// + /// + /// + internal static object GetUnknownProperty(ref Utf8JsonReader reader) + { + switch (reader.TokenType) + { + case JsonTokenType.False: + return false; + case JsonTokenType.Number: + return ReadNumber(ref reader); + case JsonTokenType.True: + return true; + case JsonTokenType.Null: + return null; + case JsonTokenType.String: + return reader.GetString(); + case JsonTokenType.StartObject: + case JsonTokenType.StartArray: + return ReadJsonElement(ref reader); + default: + // There is something broken here as this was called when the reader is pointing at a property. + // It must be a known Json type. + Debug.Assert(false, $"Utf8JsonReader.TokenType is not one of the expected types: False, Number, True, Null, String, StartArray, StartObject. Is: '{reader.TokenType}'."); + return null; + } + } + internal static bool IsReaderAtTokenType(ref Utf8JsonReader reader, JsonTokenType tokenType, bool advanceReader) { if (reader.TokenType == JsonTokenType.None) - reader.Read(); + ReaderRead(ref reader); if (reader.TokenType != tokenType) return false; if (advanceReader) - reader.Read(); + ReaderRead(ref reader); return true; } - internal static bool ReadBoolean(ref Utf8JsonReader reader, string propertyName, string className, bool advanceReader) + internal static bool ReaderRead(ref Utf8JsonReader reader) { - if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False) + try { - bool retVal = reader.GetBoolean(); - if (advanceReader) - reader.Read(); - - return retVal; + return reader.Read(); } + catch (JsonException ex) + { + throw new JsonException(ex.Message, ex); + } + } + + internal static bool ReadBoolean(ref Utf8JsonReader reader, string propertyName, string className) + { + if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False) + return reader.GetBoolean(); throw LogHelper.LogExceptionMessage( CreateJsonReaderException(ref reader, "JsonTokenType.False or JsonTokenType.True", className, propertyName)); } - internal static double ReadDouble(ref Utf8JsonReader reader, string propertyName, string className, bool advanceReader) + internal static double ReadDouble(ref Utf8JsonReader reader, string propertyName, string className) { if (reader.TokenType == JsonTokenType.Number) { - double retVal; try { - retVal = reader.GetDouble(); + return reader.GetDouble(); } catch (Exception ex) { throw LogHelper.LogExceptionMessage( CreateJsonReaderException(ref reader, typeof(double).ToString(), className, propertyName, ex)); } - - if (advanceReader) - reader.Read(); - - return retVal; } throw LogHelper.LogExceptionMessage( - CreateJsonReaderException(ref reader, typeof(double).ToString(), className, propertyName)); + CreateJsonReaderException(ref reader, "JsonTokenType.Number", className, propertyName)); } - internal static int ReadInt(ref Utf8JsonReader reader, string propertyName, string className, bool advanceReader) + internal static int ReadInt(ref Utf8JsonReader reader, string propertyName, string className) { if (reader.TokenType == JsonTokenType.Number) { - int retVal; try { - retVal = reader.GetInt32(); + return reader.GetInt32(); } - catch(Exception ex) + catch (Exception ex) { throw LogHelper.LogExceptionMessage( CreateJsonReaderException(ref reader, typeof(int).ToString(), className, propertyName, ex)); } - - if (advanceReader) - reader.Read(); - - return retVal; } throw LogHelper.LogExceptionMessage( CreateJsonReaderException(ref reader, "JsonTokenType.Number", className, propertyName)); } - internal static object ReadObject(ref Utf8JsonReader reader, bool advanceReader) + internal static JsonElement ReadJsonElement(ref Utf8JsonReader reader) { - object retVal = null; +#if NET6_0_OR_GREATER + JsonElement? jsonElement; + bool ret = JsonElement.TryParseValue(ref reader, out jsonElement); + if (ret) + return jsonElement.Value; + + return default; +#else using (JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader)) - { - if (jsonDocument.RootElement.ValueKind == JsonValueKind.Null) - return null; - - retVal = jsonDocument.RootElement.Clone(); - } - - if (advanceReader) - reader.Read(); - - return retVal; + return jsonDocument.RootElement.Clone(); +#endif } - internal static IList ReadObjects(ref Utf8JsonReader reader, IList objects, string propertyName, string className, bool advanceReader) + /// + /// Currently used by test code only + /// + /// + /// + /// + /// + /// + internal static IList ReadObjects(ref Utf8JsonReader reader, IList objects, string propertyName, string className) { + _ = objects ?? throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(objects))); + // returning null keeps the same logic as JsonSerialization.ReadObject if (reader.TokenType == JsonTokenType.Null) - { - if (advanceReader) - reader.Read(); - return null; - } - if (!IsReaderAtTokenType(ref reader, JsonTokenType.StartArray, true)) - { + if (!IsReaderAtTokenType(ref reader, JsonTokenType.StartArray, false)) throw LogHelper.LogExceptionMessage( CreateJsonReaderExceptionInvalidType(ref reader, "JsonTokenType.StartArray", className, propertyName)); - } - do + while (ReaderRead(ref reader)) { if (IsReaderAtTokenType(ref reader, JsonTokenType.EndArray, true)) break; - objects.Add(ReadObject(ref reader, false)); - - } while (reader.Read()); + objects.Add(ReadJsonElement(ref reader)); + } return objects; } - internal static string ReadString(ref Utf8JsonReader reader, string propertyName, string className, bool advanceReader) + internal static string ReadString(ref Utf8JsonReader reader, string propertyName, string className) { + // returning null keeps the same logic as JsonSerialization.ReadObject if (reader.TokenType == JsonTokenType.Null) - { - if (advanceReader) - reader.Read(); - return null; - } if (reader.TokenType != JsonTokenType.String) throw LogHelper.LogExceptionMessage( CreateJsonReaderException(ref reader, "JsonTokenType.String", className, propertyName)); - string retVal = reader.GetString(); - if (advanceReader) - reader.Read(); - - return retVal; + return reader.GetString(); } - internal static IList ReadStrings(ref Utf8JsonReader reader, IList strings, string propertyName, string className, bool advanceReader) + internal static IList ReadStrings(ref Utf8JsonReader reader, IList strings, string propertyName, string className) { + // returning null keeps the same logic as JsonSerialization.ReadObject if (reader.TokenType == JsonTokenType.Null) - { - if (advanceReader) - reader.Read(); - return null; - } - if (!IsReaderAtTokenType(ref reader, JsonTokenType.StartArray, true)) + if (!IsReaderAtTokenType(ref reader, JsonTokenType.StartArray, false)) throw LogHelper.LogExceptionMessage( CreateJsonReaderExceptionInvalidType(ref reader, "JsonTokenType.StartArray", className, propertyName)); - do + while (ReaderRead(ref reader)) { - if (IsReaderAtTokenType(ref reader, JsonTokenType.EndArray, true)) + if (IsReaderAtTokenType(ref reader, JsonTokenType.EndArray, false)) break; - strings.Add(ReadString(ref reader, propertyName, className, false)); - - } while (reader.Read()); + strings.Add(ReadString(ref reader, propertyName, className)); + } return strings; } - // used by JsonWebKey which will be in this release + /// + /// This method is only called when we are on a JsonTokenType.Number AND reading into AdditionalData which is an IDictionary[string, object]. + /// We have to make a choice of the type to return. + /// + /// + /// If possible a .net numerical type, otherwise a JsonElement. + internal static object ReadNumber(ref Utf8JsonReader reader) + { + // Assume reader is a Utf8JsonReader positioned at a JsonTokenType.Number + if (reader.TryGetInt32(out int i)) + return i; + else if (reader.TryGetInt64(out long l)) + return l; + else if (reader.TryGetUInt32(out uint u)) + return u; + else if (reader.TryGetSingle(out float f)) + return f; + else if (reader.TryGetDouble(out double d)) + return d; + else if (reader.TryGetDecimal(out decimal m)) + return m; + + Debug.Assert(false, "expected to read a number, but none of the Utf8JsonReader.TryGet... methods returned true."); + + return ReadJsonElement(ref reader); + } + + internal static void WriteAdditionalData(ref Utf8JsonWriter writer, IDictionary additionalData) + { + if (additionalData.Count > 0) + { + foreach (KeyValuePair kvp in additionalData) + { + if (kvp.Value is string) + writer.WriteString(kvp.Key, kvp.Value as string); + else if (kvp.Value is int) + writer.WriteNumber(kvp.Key, (int)kvp.Value); + else if (kvp.Value is bool) + writer.WriteBoolean(kvp.Key, (bool)kvp.Value); + else if (kvp.Value is decimal) + writer.WriteNumber(kvp.Key, (decimal)kvp.Value); + else if (kvp.Value is double) + writer.WriteNumber(kvp.Key, (double)kvp.Value); + else if (kvp.Value is float) + writer.WriteNumber(kvp.Key, (float)kvp.Value); + else if (kvp.Value is long) + writer.WriteNumber(kvp.Key, (long)kvp.Value); + else if (kvp.Value is null) + writer.WriteNull(kvp.Key); + else if (kvp.Value is JsonElement element) + { + writer.WritePropertyName(kvp.Key); + element.WriteTo(writer); + } + else + { + writer.WriteString(kvp.Key, kvp.Value.ToString()); + } + } + } + } + internal static void WriteStrings(ref Utf8JsonWriter writer, string propertyName, IList strings) { writer.WritePropertyName(propertyName); @@ -257,6 +331,15 @@ internal static void WriteStrings(ref Utf8JsonWriter writer, string propertyName writer.WriteEndArray(); } + + internal static void WriteStrings(ref Utf8JsonWriter writer, JsonEncodedText propertyName, IList strings) + { + writer.WritePropertyName(propertyName); + writer.WriteStartArray(); + foreach (string str in strings) + writer.WriteStringValue(str); + + writer.WriteEndArray(); + } } } - diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs new file mode 100644 index 0000000000..1e029e6170 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs @@ -0,0 +1,440 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens.Json +{ + internal static class JsonWebKeySerializer + { + public static HashSet JsonWebKeyParameterNamesUpperCase = new HashSet + { + "ALG", + "CRV", + "D", + "DP", + "DQ", + "E", + "K", + "KEY_OPS", + "KEYS", + "KID", + "KTY", + "N", + "OTH", + "P", + "Q", + "QI", + "USE", + "X", + "X5C", + "X5T", + "X5T#S256", + "X5U", + "Y" + }; + + #region Read + public static JsonWebKey Read(string json) + { + return Read(json, new JsonWebKey()); + } + + public static JsonWebKey Read(string json, JsonWebKey jsonWebKey) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json).AsSpan()); + return Read(ref reader, jsonWebKey); + } + + /// + /// Reads a JsonWebKey. see: https://datatracker.ietf.org/doc/html/rfc7517 + /// + /// a pointing at a StartObject. + /// + /// A . + public static JsonWebKey Read(ref Utf8JsonReader reader, JsonWebKey jsonWebKey) + { + if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, false)) + throw LogHelper.LogExceptionMessage( + new JsonException( + LogHelper.FormatInvariant( + LogMessages.IDX11023, + LogHelper.MarkAsNonPII("JsonTokenType.StartObject"), + LogHelper.MarkAsNonPII(reader.TokenType), + LogHelper.MarkAsNonPII(JsonWebKey.ClassName), + LogHelper.MarkAsNonPII(reader.TokenStartIndex), + LogHelper.MarkAsNonPII(reader.CurrentDepth), + LogHelper.MarkAsNonPII(reader.BytesConsumed)))); + + while(JsonSerializerPrimitives.ReaderRead(ref reader)) + { + #region Check property name using ValueTextEquals + // common names are tried first + // the JsonWebKey spec, https://datatracker.ietf.org/doc/html/rfc7517#section-4, does not require that we reject JSON with + // duplicate member names, in strict mode, we could add logic to try a property once and throw if a duplicate shows up. + // 6x uses the last value. + if (reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.K)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.K = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.K, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.E)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.E = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.E, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Kid)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Kid = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Kid, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Kty)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Kty = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Kty, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.N)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.N = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.N, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.X5c)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.X5c, JsonWebKeyParameterNames.X5c, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Alg)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Alg = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Alg, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Crv)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Crv = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Crv, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.D)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.D = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.D, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.DP)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.DP = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.DP, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.DQ)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.DQ = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.DQ, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.KeyOps)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + // the value can be null if the value is 'nill' + if (JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.KeyOps, JsonWebKeyParameterNames.KeyOps, JsonWebKey.ClassName) == null) + { + throw LogHelper.LogExceptionMessage( + new ArgumentNullException( + JsonWebKeyParameterNames.KeyOps, + new JsonException( + LogHelper.FormatInvariant( + LogMessages.IDX11022, + LogHelper.MarkAsNonPII("JsonTokenType.StartArray"), + LogHelper.MarkAsNonPII(reader.TokenType), + LogHelper.MarkAsNonPII(JsonWebKey.ClassName), + LogHelper.MarkAsNonPII(JsonWebKeyParameterNames.KeyOps), + LogHelper.MarkAsNonPII(reader.TokenStartIndex), + LogHelper.MarkAsNonPII(reader.CurrentDepth), + LogHelper.MarkAsNonPII(reader.BytesConsumed))))); + } + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Oth)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.Oth, JsonWebKeyParameterNames.Oth, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.P)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.P = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.P, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Q)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Q = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Q, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.QI)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.QI = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.QI, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Use)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Use = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Use, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.X)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.X = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.X5t)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.X5t = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5t, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.X5tS256)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.X5tS256 = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5tS256, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.X5u)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.X5u = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5u, JsonWebKey.ClassName); + } + else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Y)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + jsonWebKey.Y = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Y, JsonWebKey.ClassName); + } + #endregion + else + { + #region case-insensitive + // fallback to checking property names as case insensitive + // first check to see if the upper case property value is a valid property name if not add to AdditionalData, to avoid unnecessary string compares. + string propertyName = JsonSerializerPrimitives.GetPropertyName(ref reader, JsonWebKey.ClassName, true); + if (!JsonWebKeyParameterNamesUpperCase.Contains(propertyName.ToUpperInvariant())) + { + jsonWebKey.AdditionalData[propertyName] = JsonSerializerPrimitives.GetUnknownProperty(ref reader); + } + else + { + if (propertyName.Equals(JsonWebKeyParameterNames.E, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.E = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.E, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Kid, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Kid = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Kid, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Kty, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Kty = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Kty, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.N, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.N = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.N, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Use, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Use = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Use, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Alg, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Alg = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Alg, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Crv, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Crv = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Crv, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.D, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.D = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.D, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.DP, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.DP = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.DP, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.DQ, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.DQ = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.DQ, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.K, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.K = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.K, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.KeyOps, StringComparison.OrdinalIgnoreCase)) + { + // the value can be null if the value is 'nill' + if (JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.KeyOps, JsonWebKeyParameterNames.KeyOps, JsonWebKey.ClassName) == null) + { + throw LogHelper.LogExceptionMessage( + new ArgumentNullException( + JsonWebKeyParameterNames.KeyOps, + new JsonException( + LogHelper.FormatInvariant( + LogMessages.IDX11022, + LogHelper.MarkAsNonPII("JsonTokenType.StartArray"), + LogHelper.MarkAsNonPII(reader.TokenType), + LogHelper.MarkAsNonPII(JsonWebKey.ClassName), + LogHelper.MarkAsNonPII(JsonWebKeyParameterNames.KeyOps), + LogHelper.MarkAsNonPII(reader.TokenStartIndex), + LogHelper.MarkAsNonPII(reader.CurrentDepth), + LogHelper.MarkAsNonPII(reader.BytesConsumed))))); + } + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Oth, StringComparison.OrdinalIgnoreCase)) + { + JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.Oth, JsonWebKeyParameterNames.Oth, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.P, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.P = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.P, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Q, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Q = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Q, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.QI, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.QI = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.QI, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.X, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.X = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.X5c, StringComparison.OrdinalIgnoreCase)) + { + JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.X5c, JsonWebKeyParameterNames.X5c, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.X5t, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.X5t = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5t, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.X5tS256, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.X5tS256 = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5tS256, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.X5u, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.X5u = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.X5u, JsonWebKey.ClassName); + } + else if (propertyName.Equals(JsonWebKeyParameterNames.Y, StringComparison.OrdinalIgnoreCase)) + { + jsonWebKey.Y = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Y, JsonWebKey.ClassName); + } + } + #endregion case-insensitive + } + } + else if (JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.EndObject, false)) + break; + } + + return jsonWebKey; + } + #endregion + + #region Write + public static string Write(JsonWebKey jsonWebKey) + { + using (MemoryStream memoryStream = new MemoryStream()) + { + Utf8JsonWriter writer = null; + try + { + writer = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); + Write(ref writer, jsonWebKey); + writer.Flush(); + return Encoding.UTF8.GetString(memoryStream.ToArray()); + } + finally + { + writer?.Dispose(); + } + } + } + + public static void Write(ref Utf8JsonWriter writer, JsonWebKey jsonWebKey) + { + _ = jsonWebKey ?? throw new ArgumentNullException(nameof(jsonWebKey)); + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + + writer.WriteStartObject(); + + if (!string.IsNullOrEmpty(jsonWebKey.Alg)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Alg, jsonWebKey.Alg); + + if (!string.IsNullOrEmpty(jsonWebKey.Crv)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Crv, jsonWebKey.Crv); + + if (!string.IsNullOrEmpty(jsonWebKey.D)) + writer.WriteString(EncodedJsonWebKeyParameterNames.D, jsonWebKey.D); + + if (!string.IsNullOrEmpty(jsonWebKey.DP)) + writer.WriteString(EncodedJsonWebKeyParameterNames.DP, jsonWebKey.DP); + + if (!string.IsNullOrEmpty(jsonWebKey.DQ)) + writer.WriteString(EncodedJsonWebKeyParameterNames.DQ, jsonWebKey.DQ); + + if (!string.IsNullOrEmpty(jsonWebKey.E)) + writer.WriteString(EncodedJsonWebKeyParameterNames.E, jsonWebKey.E); + + if (!string.IsNullOrEmpty(jsonWebKey.K)) + writer.WriteString(EncodedJsonWebKeyParameterNames.K, jsonWebKey.K); + + if (jsonWebKey.KeyOps.Count > 0) + JsonSerializerPrimitives.WriteStrings(ref writer, EncodedJsonWebKeyParameterNames.KeyOps, jsonWebKey.KeyOps); + + if (!string.IsNullOrEmpty(jsonWebKey.Kid)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Kid, jsonWebKey.Kid); + + if (!string.IsNullOrEmpty(jsonWebKey.Kty)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Kty, jsonWebKey.Kty); + + if (!string.IsNullOrEmpty(jsonWebKey.N)) + writer.WriteString(EncodedJsonWebKeyParameterNames.N, jsonWebKey.N); + + if (jsonWebKey.Oth.Count > 0) + JsonSerializerPrimitives.WriteStrings(ref writer, EncodedJsonWebKeyParameterNames.Oth, jsonWebKey.Oth); + + if (!string.IsNullOrEmpty(jsonWebKey.P)) + writer.WriteString(EncodedJsonWebKeyParameterNames.P, jsonWebKey.P); + + if (!string.IsNullOrEmpty(jsonWebKey.Q)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Q, jsonWebKey.Q); + + if (!string.IsNullOrEmpty(jsonWebKey.QI)) + writer.WriteString(EncodedJsonWebKeyParameterNames.QI, jsonWebKey.QI); + + if (!string.IsNullOrEmpty(jsonWebKey.Use)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Use, jsonWebKey.Use); + + if (!string.IsNullOrEmpty(jsonWebKey.X)) + writer.WriteString(EncodedJsonWebKeyParameterNames.X, jsonWebKey.X); + + if (jsonWebKey.X5c.Count > 0) + JsonSerializerPrimitives.WriteStrings(ref writer, EncodedJsonWebKeyParameterNames.X5c, jsonWebKey.X5c); + + if (!string.IsNullOrEmpty(jsonWebKey.X5t)) + writer.WriteString(EncodedJsonWebKeyParameterNames.X5t, jsonWebKey.X5t); + + if (!string.IsNullOrEmpty(jsonWebKey.X5tS256)) + writer.WriteString(EncodedJsonWebKeyParameterNames.X5tS256, jsonWebKey.X5tS256); + + if (!string.IsNullOrEmpty(jsonWebKey.X5u)) + writer.WriteString(EncodedJsonWebKeyParameterNames.X5u, jsonWebKey.X5u); + + if (!string.IsNullOrEmpty(jsonWebKey.Y)) + writer.WriteString(EncodedJsonWebKeyParameterNames.Y, jsonWebKey.Y); + + JsonSerializerPrimitives.WriteAdditionalData(ref writer, jsonWebKey.AdditionalData); + + writer.WriteEndObject(); + } + #endregion + } +} + diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySetSerializer.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySetSerializer.cs new file mode 100644 index 0000000000..0ab209b31f --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySetSerializer.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens.Json +{ + internal static class JsonWebKeySetSerializer + { + private static readonly byte[] _keysUtf8 = Encoding.UTF8.GetBytes(JsonWebKeySetParameterNames.Keys); + + #region Read + public static JsonWebKeySet Read(string json, JsonWebKeySet jsonWebKeySet) + { + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json).AsSpan()); + return Read(ref reader, jsonWebKeySet); + } + + /// + /// Reads a JsonWebKey. see: https://datatracker.ietf.org/doc/html/rfc7517 + /// + /// a pointing at a StartObject. + /// + /// A . + public static JsonWebKeySet Read(ref Utf8JsonReader reader, JsonWebKeySet jsonWebKeySet) + { + if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, false)) + throw LogHelper.LogExceptionMessage( + new JsonException( + LogHelper.FormatInvariant( + LogMessages.IDX11022, + LogHelper.MarkAsNonPII("JsonTokenType.StartObject"), + LogHelper.MarkAsNonPII(reader.TokenType), + LogHelper.MarkAsNonPII(JsonWebKeySet.ClassName), + LogHelper.MarkAsNonPII(reader.TokenStartIndex), + LogHelper.MarkAsNonPII(reader.CurrentDepth), + LogHelper.MarkAsNonPII(reader.BytesConsumed)))); + + while (JsonSerializerPrimitives.ReaderRead(ref reader)) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(_keysUtf8)) + { + JsonSerializerPrimitives.ReaderRead(ref reader); + ReadKeys(ref reader, jsonWebKeySet); + } + else + { + string propertyName = JsonSerializerPrimitives.GetPropertyName(ref reader, JsonWebKey.ClassName, true); + if (propertyName.Equals(JsonWebKeyParameterNames.Keys, StringComparison.OrdinalIgnoreCase)) + ReadKeys(ref reader, jsonWebKeySet); + else + jsonWebKeySet.AdditionalData[propertyName] = JsonSerializerPrimitives.GetUnknownProperty(ref reader); + } + } + else if (JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.EndObject, true)) + { + break; + } + } + + return jsonWebKeySet; + } + + public static void ReadKeys(ref Utf8JsonReader reader, JsonWebKeySet jsonWebKeySet) + { + if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartArray, false)) + throw LogHelper.LogExceptionMessage( + JsonSerializerPrimitives.CreateJsonReaderExceptionInvalidType( + ref reader, + "JsonTokenType.StartArray", + JsonWebKeyParameterNames.KeyOps, + JsonWebKeySet.ClassName)); + + while (JsonSerializerPrimitives.ReaderRead(ref reader)) + { + if (reader.TokenType == JsonTokenType.StartObject) + jsonWebKeySet.Keys.Add(JsonWebKeySerializer.Read(ref reader, new JsonWebKey())); + else if (JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.EndArray, false)) + break; + } + } + + #endregion + + #region Write + public static string Write(JsonWebKeySet jsonWebKeySet) + { + using (MemoryStream memoryStream = new MemoryStream()) + { + Utf8JsonWriter writer = null; + try + { + // writing strings without escaping is as we know this is a utf8 encoding + writer = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); + Write(ref writer, jsonWebKeySet); + writer.Flush(); + return Encoding.UTF8.GetString(memoryStream.ToArray()); + } + finally + { + writer?.Dispose(); + } + } + } + + /// + /// This method will be used when reading OIDC metadata + /// + /// + /// + public static void Write(ref Utf8JsonWriter writer, JsonWebKeySet jsonWebKeySet) + { + writer.WriteStartObject(); + + writer.WritePropertyName(EncodedJsonWebKeyParameterNames.Keys); + writer.WriteStartArray(); + + foreach (JsonWebKey jsonWebKey in jsonWebKeySet.Keys) + JsonWebKeySerializer.Write(ref writer, jsonWebKey); + + writer.WriteEndArray(); + + if (jsonWebKeySet.AdditionalData.Count > 0) + JsonSerializerPrimitives.WriteAdditionalData(ref writer, jsonWebKeySet.AdditionalData); + + writer.WriteEndObject(); + } + + #endregion + } +} + diff --git a/src/Microsoft.IdentityModel.Tokens/JsonWebKey.cs b/src/Microsoft.IdentityModel.Tokens/JsonWebKey.cs index 754ed570d3..b9504122a1 100644 --- a/src/Microsoft.IdentityModel.Tokens/JsonWebKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/JsonWebKey.cs @@ -4,21 +4,32 @@ using System; using System.Collections.Generic; using System.Security.Cryptography; +using System.Text.Json.Serialization; +using System.Threading; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Microsoft.IdentityModel.Tokens.Json; namespace Microsoft.IdentityModel.Tokens { /// /// Represents a JSON Web Key as defined in https://datatracker.ietf.org/doc/html/rfc7517. /// - [JsonObject] public class JsonWebKey : SecurityKey { + internal const string ClassName = "Microsoft.IdentityModel.Tokens.JsonWebKey"; + private Dictionary _additionalData; + private IList _keyOps; + private IList _oth; + private IList _x5c; private string _kid; - private const string _className = "Microsoft.IdentityModel.Tokens.JsonWebKey"; + + /// + /// Initializes an new instance of . + /// + public JsonWebKey() + { + } /// /// Returns a new instance of . @@ -27,7 +38,7 @@ public class JsonWebKey : SecurityKey /// /// If 'json' is null or empty. /// If 'json' fails to deserialize. - static public JsonWebKey Create(string json) + public static JsonWebKey Create(string json) { if (string.IsNullOrEmpty(json)) throw LogHelper.LogArgumentNullException(nameof(json)); @@ -35,13 +46,6 @@ static public JsonWebKey Create(string json) return new JsonWebKey(json); } - /// - /// Initializes an new instance of . - /// - public JsonWebKey() - { - } - /// /// Initializes an new instance of from a json string. /// @@ -56,13 +60,13 @@ public JsonWebKey(string json) try { if (LogHelper.IsEnabled(EventLogLevel.Verbose)) - LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(_className)); + LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(ClassName)); - JsonConvert.PopulateObject(json, this); + JsonWebKeySerializer.Read(json, this); } catch (Exception ex) { - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(_className)), ex)); + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(ClassName)), ex)); } } @@ -82,52 +86,75 @@ public JsonWebKey(string json) /// When deserializing from JSON any properties that are not defined will be placed here. /// [JsonExtensionData] - public virtual IDictionary AdditionalData { get; } = new Dictionary(); + public IDictionary AdditionalData => _additionalData ?? + Interlocked.CompareExchange(ref _additionalData, new Dictionary(StringComparer.Ordinal), null) ?? + _additionalData; /// - /// Gets or sets the 'alg' (KeyType).. + /// Gets or sets the 'alg' (KeyType). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Alg, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.Alg)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Alg { get; set; } /// - /// Gets or sets the 'crv' (ECC - Curve).. + /// Gets or sets the 'crv' (ECC - Curve). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Crv, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.Crv)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Crv { get; set; } /// - /// Gets or sets the 'd' (ECC - Private Key OR RSA - Private Exponent).. + /// Gets or sets the 'd' (ECC - Private Key OR RSA - Private Exponent). /// /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.D, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.D)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string D { get; set; } /// - /// Gets or sets the 'dp' (RSA - First Factor CRT Exponent).. + /// Gets or sets the 'dp' (RSA - First Factor CRT Exponent). /// /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.DP, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.DP)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string DP { get; set; } /// - /// Gets or sets the 'dq' (RSA - Second Factor CRT Exponent).. + /// Gets or sets the 'dq' (RSA - Second Factor CRT Exponent). /// /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.DQ, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.DQ)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string DQ { get; set; } /// - /// Gets or sets the 'e' (RSA - Exponent).. + /// Gets or sets the 'e' (RSA - Exponent). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.E, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.E)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string E { get; set; } /// - /// Gets or sets the 'k' (Symmetric - Key Value).. + /// Gets or sets the 'k' (Symmetric - Key Value). /// /// Base64urlEncoding - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.K, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.K)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string K { get; set; } /// @@ -141,15 +168,23 @@ public override string KeyId } /// - /// Gets the 'key_ops' (Key Operations).. + /// Gets the 'key_ops' (Key Operations). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.KeyOps, Required = Required.Default)] - public IList KeyOps { get; private set; } = new List(); + [JsonPropertyName(JsonWebKeyParameterNames.KeyOps)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public IList KeyOps => _keyOps ?? + Interlocked.CompareExchange(ref _keyOps, new List(), null) ?? + _keyOps; /// /// Gets or sets the 'kid' (Key ID).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Kid, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.Kid)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Kid { get { return _kid; } @@ -157,87 +192,130 @@ public string Kid } /// - /// Gets or sets the 'kty' (Key Type).. + /// Gets or sets the 'kty' (Key Type). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Kty, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.Kty)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Kty { get; set; } /// - /// Gets or sets the 'n' (RSA - Modulus).. + /// Gets or sets the 'n' (RSA - Modulus). /// - /// Value is formated as: Base64urlEncoding - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.N, Required = Required.Default)] + /// Value is formatted as: Base64urlEncoding + [JsonPropertyName(JsonWebKeyParameterNames.N)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string N { get; set; } /// - /// Gets or sets the 'oth' (RSA - Other Primes Info).. + /// Gets or sets the 'oth' (RSA - Other Primes Info). /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Oth, Required = Required.Default)] - public IList Oth { get; set; } + [JsonPropertyName(JsonWebKeyParameterNames.Oth)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public IList Oth => _oth ?? + Interlocked.CompareExchange(ref _oth, new List(), null) ?? + _oth; /// /// Gets or sets the 'p' (RSA - First Prime Factor).. /// - /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.P, Required = Required.Default)] + /// Value is formatted as: Base64urlUInt + [JsonPropertyName(JsonWebKeyParameterNames.P)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string P { get; set; } /// /// Gets or sets the 'q' (RSA - Second Prime Factor).. /// - /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Q, Required = Required.Default)] + /// Value is formatted as: Base64urlUInt + [JsonPropertyName(JsonWebKeyParameterNames.Q)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Q { get; set; } /// /// Gets or sets the 'qi' (RSA - First CRT Coefficient).. /// - /// Value is formated as: Base64urlUInt - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.QI, Required = Required.Default)] + /// Value is formatted as: Base64urlUInt + [JsonPropertyName(JsonWebKeyParameterNames.QI)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string QI { get; set; } /// /// Gets or sets the 'use' (Public Key Use).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Use, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.Use)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Use { get; set; } /// /// Gets or sets the 'x' (ECC - X Coordinate).. /// - /// Value is formated as: Base64urlEncoding - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X, Required = Required.Default)] + /// Value is formatted as: Base64urlEncoding + [JsonPropertyName(JsonWebKeyParameterNames.X)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string X { get; set; } /// /// Gets the 'x5c' collection (X.509 Certificate Chain).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5c, Required = Required.Default)] - public IList X5c { get; private set; } = new List(); + [JsonPropertyName(JsonWebKeyParameterNames.X5c)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public IList X5c => _x5c ?? + Interlocked.CompareExchange(ref _x5c, new List(), null) ?? + _x5c; /// /// Gets or sets the 'x5t' (X.509 Certificate SHA-1 thumbprint).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5t, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.X5t)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string X5t { get; set; } /// /// Gets or sets the 'x5t#S256' (X.509 Certificate SHA-1 thumbprint).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5tS256, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.X5tS256)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string X5tS256 { get; set; } /// /// Gets or sets the 'x5u' (X.509 URL).. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5u, Required = Required.Default)] + [JsonPropertyName(JsonWebKeyParameterNames.X5u)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string X5u { get; set; } /// /// Gets or sets the 'y' (ECC - Y Coordinate).. /// - /// Value is formated as: Base64urlEncoding - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Y, Required = Required.Default)] + /// Value is formatted as: Base64urlEncoding + [JsonPropertyName(JsonWebKeyParameterNames.Y)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif public string Y { get; set; } /// @@ -277,33 +355,13 @@ public bool HasPrivateKey } } - /// - /// Gets a bool that determines if the 'key_ops' (Key Operations) property should be serialized. - /// This is used by Json.NET in order to conditionally serialize properties. - /// - /// true if 'key_ops' (Key Operations) is not empty; otherwise, false. - public bool ShouldSerializeKeyOps() - { - return KeyOps.Count > 0; - } - - /// - /// Gets a bool that determines if the 'x5c' collection (X.509 Certificate Chain) property should be serialized. - /// This is used by Json.NET in order to conditionally serialize properties. - /// - /// true if 'x5c' collection (X.509 Certificate Chain) is not empty; otherwise, false. - public bool ShouldSerializeX5c() - { - return X5c.Count > 0; - } - internal RSAParameters CreateRsaParameters() { if (string.IsNullOrEmpty(N)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("Modulus")))); + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(ClassName), LogHelper.MarkAsNonPII("Modulus")))); if (string.IsNullOrEmpty(E)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("Exponent")))); + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(ClassName), LogHelper.MarkAsNonPII("Exponent")))); return new RSAParameters { @@ -339,10 +397,9 @@ public override bool CanComputeJwkThumbprint() } /// - /// Computes a sha256 hash over the . + /// Computes the JWK thumprint per spec: https://datatracker.ietf.org/doc/html/rfc7638 />. /// - /// A JWK thumbprint. - /// https://datatracker.ietf.org/doc/html/rfc7638 + /// A the JWK thumbprint. public override byte[] ComputeJwkThumbprint() { if (string.IsNullOrEmpty(Kty)) @@ -416,49 +473,41 @@ private byte[] ComputeECThumbprint() /// https://datatracker.ietf.org/doc/html/rfc7800#section-3.2 internal string RepresentAsAsymmetricPublicJwk() { - JObject jwk = new JObject(); - - if (!string.IsNullOrEmpty(Kid)) - jwk.Add(JsonWebKeyParameterNames.Kid, Kid); + string kid = string.IsNullOrEmpty(Kid) ? "{" : $@"{{""{JsonWebKeyParameterNames.Kid}"":""{Kid}"","; if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) - PopulateWithPublicEcParams(jwk); - else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.RSA)) - PopulateWithPublicRsaParams(jwk); - else - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10707, LogHelper.MarkAsNonPII(nameof(Kty)), LogHelper.MarkAsNonPII(string.Join(", ", JsonWebAlgorithmsKeyTypes.EllipticCurve, JsonWebAlgorithmsKeyTypes.RSA)), LogHelper.MarkAsNonPII(nameof(Kty))))); - - return jwk.ToString(Formatting.None); - } + { + if (string.IsNullOrEmpty(Crv)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Crv))))); - private void PopulateWithPublicEcParams(JObject jwk) - { - if (string.IsNullOrEmpty(Crv)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Crv))))); + if (string.IsNullOrEmpty(X)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(X))))); - if (string.IsNullOrEmpty(X)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(X))))); + if (string.IsNullOrEmpty(Y)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Y))))); - if (string.IsNullOrEmpty(Y)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Y))))); - - jwk.Add(JsonWebKeyParameterNames.Crv, Crv); - jwk.Add(JsonWebKeyParameterNames.Kty, Kty); - jwk.Add(JsonWebKeyParameterNames.X, X); - jwk.Add(JsonWebKeyParameterNames.Y, Y); - } + return $@"{kid}" + + $@"""{JsonWebKeyParameterNames.Crv}"":""{Crv}""," + + $@"""{JsonWebKeyParameterNames.Kty}"":""{Kty}""," + + $@"""{JsonWebKeyParameterNames.X}"":""{X}""," + + $@"""{JsonWebKeyParameterNames.Y}"":""{Y}""}}"; + } + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.RSA)) + { + if (string.IsNullOrEmpty(E)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(E))))); - private void PopulateWithPublicRsaParams(JObject jwk) - { - if (string.IsNullOrEmpty(E)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(E))))); + if (string.IsNullOrEmpty(N)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(N))))); - if (string.IsNullOrEmpty(N)) - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(N))))); + return $@"{kid}" + + $@"""{JsonWebKeyParameterNames.E}"":""{E}""," + + $@"""{JsonWebKeyParameterNames.Kty}"":""{Kty}""," + + $@"""{JsonWebKeyParameterNames.N}"":""{N}""}}"; + } + else + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10707, LogHelper.MarkAsNonPII(nameof(Kty)), LogHelper.MarkAsNonPII(string.Join(", ", JsonWebAlgorithmsKeyTypes.EllipticCurve, JsonWebAlgorithmsKeyTypes.RSA)), LogHelper.MarkAsNonPII(nameof(Kty))))); - jwk.Add(JsonWebKeyParameterNames.E, E); - jwk.Add(JsonWebKeyParameterNames.Kty, Kty); - jwk.Add(JsonWebKeyParameterNames.N, N); } /// @@ -471,3 +520,4 @@ public override string ToString() } } } + diff --git a/src/Microsoft.IdentityModel.Tokens/JsonWebKeyParameterNames.cs b/src/Microsoft.IdentityModel.Tokens/JsonWebKeyParameterNames.cs index c2cd3320a5..0aecd691c6 100644 --- a/src/Microsoft.IdentityModel.Tokens/JsonWebKeyParameterNames.cs +++ b/src/Microsoft.IdentityModel.Tokens/JsonWebKeyParameterNames.cs @@ -1,14 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Text; + namespace Microsoft.IdentityModel.Tokens { +#pragma warning disable 1591 + /// - /// Names for Json Web Key Values + /// JsonWebKey parameter names /// public static class JsonWebKeyParameterNames { -#pragma warning disable 1591 public const string Alg = "alg"; public const string Crv = "crv"; public const string D = "d"; @@ -24,16 +27,41 @@ public static class JsonWebKeyParameterNames public const string Oth = "oth"; public const string P = "p"; public const string Q = "q"; - public const string R = "r"; - public const string T = "t"; public const string QI = "qi"; public const string Use = "use"; + public const string X = "x"; public const string X5c = "x5c"; public const string X5t = "x5t"; public const string X5tS256 = "x5t#S256"; public const string X5u = "x5u"; - public const string X = "x"; public const string Y = "y"; -#pragma warning restore 1591 } + + internal static class JsonWebKeyParameterUtf8Bytes + { + public static readonly byte[] Alg = Encoding.UTF8.GetBytes("alg"); + public static readonly byte[] Crv = Encoding.UTF8.GetBytes("crv"); + public static readonly byte[] D = Encoding.UTF8.GetBytes("d"); + public static readonly byte[] DP = Encoding.UTF8.GetBytes("dp"); + public static readonly byte[] DQ = Encoding.UTF8.GetBytes("dq"); + public static readonly byte[] E = Encoding.UTF8.GetBytes("e"); + public static readonly byte[] K = Encoding.UTF8.GetBytes("k"); + public static readonly byte[] KeyOps = Encoding.UTF8.GetBytes("key_ops"); + public static readonly byte[] Keys = Encoding.UTF8.GetBytes("keys"); + public static readonly byte[] Kid = Encoding.UTF8.GetBytes("kid"); + public static readonly byte[] Kty = Encoding.UTF8.GetBytes("kty"); + public static readonly byte[] N = Encoding.UTF8.GetBytes("n"); + public static readonly byte[] Oth = Encoding.UTF8.GetBytes("oth"); + public static readonly byte[] P = Encoding.UTF8.GetBytes("p"); + public static readonly byte[] Q = Encoding.UTF8.GetBytes("q"); + public static readonly byte[] QI = Encoding.UTF8.GetBytes("qi"); + public static readonly byte[] Use = Encoding.UTF8.GetBytes("use"); + public static readonly byte[] X5c = Encoding.UTF8.GetBytes("x5c"); + public static readonly byte[] X5t = Encoding.UTF8.GetBytes("x5t"); + public static readonly byte[] X5tS256 = Encoding.UTF8.GetBytes("x5t#S256"); + public static readonly byte[] X5u = Encoding.UTF8.GetBytes("x5u"); + public static readonly byte[] X = Encoding.UTF8.GetBytes("x"); + public static readonly byte[] Y = Encoding.UTF8.GetBytes("y"); + } +#pragma warning restore 1591 } diff --git a/src/Microsoft.IdentityModel.Tokens/JsonWebKeySet.cs b/src/Microsoft.IdentityModel.Tokens/JsonWebKeySet.cs index a414f8c8ac..fa46dc58c0 100644 --- a/src/Microsoft.IdentityModel.Tokens/JsonWebKeySet.cs +++ b/src/Microsoft.IdentityModel.Tokens/JsonWebKeySet.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Text.Json.Serialization; +using System.Threading; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; -using Newtonsoft.Json; +using Microsoft.IdentityModel.Tokens.Json; namespace Microsoft.IdentityModel.Tokens { @@ -14,10 +16,10 @@ namespace Microsoft.IdentityModel.Tokens /// Contains a collection of that can be populated from a json string. /// /// provides support for https://datatracker.ietf.org/doc/html/rfc7517. - [JsonObject] public class JsonWebKeySet { - private const string _className = "Microsoft.IdentityModel.Tokens.JsonWebKeySet"; + internal const string ClassName = "Microsoft.IdentityModel.Tokens.JsonWebKeySet"; + private IDictionary _additionalData; /// /// Returns a new instance of . @@ -55,13 +57,13 @@ public JsonWebKeySet(string json) try { if (LogHelper.IsEnabled(EventLogLevel.Verbose)) - LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(_className)); + LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(ClassName)); - JsonConvert.PopulateObject(json, this); + JsonWebKeySetSerializer.Read(json, this); } catch (Exception ex) { - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(_className)), ex)); + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(ClassName)), ex)); } } @@ -69,13 +71,15 @@ public JsonWebKeySet(string json) /// When deserializing from JSON any properties that are not defined will be placed here. /// [JsonExtensionData] - public virtual IDictionary AdditionalData { get; } = new Dictionary(); + public IDictionary AdditionalData => _additionalData ?? + Interlocked.CompareExchange(ref _additionalData, new Dictionary(StringComparer.Ordinal), null) ?? + _additionalData; /// /// Gets the . - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeySetParameterNames.Keys, Required = Required.Default)] - public IList Keys { get; private set; } = new List(); + /// + [JsonPropertyName(JsonWebKeySetParameterNames.Keys)] + public IList Keys { get; } = new List(); /// /// Default value for the flag that controls whether unresolved JsonWebKeys will be included in the resulting collection of method. @@ -87,6 +91,7 @@ public JsonWebKeySet(string json) /// Flag that controls whether unresolved JsonWebKeys will be included in the resulting collection of method. /// [DefaultValue(true)] + [JsonIgnore] public bool SkipUnresolvedJsonWebKeys { get; set; } = DefaultSkipUnresolvedJsonWebKeys; /// diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index f6e7e6af50..42fa8eb726 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -256,6 +256,7 @@ internal static class LogMessages // Json parsing errors public const string IDX11020 = "IDX11020: The JSON value of type: '{0}', could not be converted to '{1}'. Reading: '{2}.{3}', Position: '{4}', CurrentDepth: '{5}', BytesConsumed: '{6}'."; public const string IDX11022 = "IDX11022: Expecting json reader to be positioned on '{0}', reader was positioned at: '{1}', Reading: '{2}.{3}', Position: '{4}', CurrentDepth: '{5}', BytesConsumed: '{6}'."; + public const string IDX11023 = "IDX11023: Expecting json reader to be positioned on '{0}', reader was positioned at: '{1}', Reading: '{2}', Position: '{3}', CurrentDepth: '{4}', BytesConsumed: '{5}'."; #pragma warning restore 1591 } } diff --git a/src/Microsoft.IdentityModel.Tokens/SecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/SecurityKey.cs index 08f486f4db..93d3f0bd04 100644 --- a/src/Microsoft.IdentityModel.Tokens/SecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/SecurityKey.cs @@ -3,7 +3,7 @@ using System; using Microsoft.IdentityModel.Logging; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.IdentityModel.Tokens { @@ -13,13 +13,13 @@ namespace Microsoft.IdentityModel.Tokens public abstract class SecurityKey { private CryptoProviderFactory _cryptoProviderFactory; - private Lazy _internalId; + private object _internalIdLock = new object(); + private string _internalId; internal SecurityKey(SecurityKey key) { _cryptoProviderFactory = key._cryptoProviderFactory; KeyId = key.KeyId; - SetInternalId(); } /// @@ -28,11 +28,30 @@ internal SecurityKey(SecurityKey key) public SecurityKey() { _cryptoProviderFactory = CryptoProviderFactory.Default; - SetInternalId(); } [JsonIgnore] - internal virtual string InternalId { get => _internalId.Value; } + internal virtual string InternalId + { + get + { + if (_internalId == null) + { + lock (_internalIdLock) + { + if (_internalId == null) + { + if (CanComputeJwkThumbprint()) + _internalId = Base64UrlEncoder.Encode(ComputeJwkThumbprint()); + else + _internalId = string.Empty; + } + } + } + + return _internalId; + } + } /// /// This must be overridden to get the size of this . @@ -100,19 +119,5 @@ public virtual bool IsSupportedAlgorithm(string algorithm) // do not throw if algorithm is null or empty to stay in sync with CryptoProviderFactory.IsSupportedAlgorithm. return CryptoProviderFactory.IsSupportedAlgorithm(algorithm, this); } - - /// - /// Sets the to value of 's JWK thumbprint if it can be computed, otherwise sets the to . - /// - private void SetInternalId() - { - _internalId = new Lazy(() => - { - if (CanComputeJwkThumbprint()) - return Base64UrlEncoder.Encode(ComputeJwkThumbprint()); - else - return string.Empty; - }); - } } } diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs index 8f79a9ddf7..bc6dc849be 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs @@ -145,10 +145,6 @@ public static OpenIdConnectConfiguration FullyPopulatedWithKeys public static string OpenIdConnectMetadataPingLabsJWKSString = @"{""jwks_uri"":""PingLabsJWKS.json""}"; public static string OpenIdConnectMetatadataBadJson = @"{..."; - // interop - public static string GoogleCertsFile = "google-certs.json"; - public static JsonWebKeySet GoogleCertsExpected; - static OpenIdConfigData() { PingLabs = new OpenIdConnectConfiguration() diff --git a/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestUtilityTests.cs b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestUtilityTests.cs index 4a6f50b602..a1896c6995 100644 --- a/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestUtilityTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestUtilityTests.cs @@ -39,7 +39,7 @@ public void AppendHeaders(SignedHttpRequestUtilityTheoryData theoryData) }; httpRequestData.AppendHeaders(theoryData.HttpHeaders); - IdentityComparer.AreStingEnumDictionariesEqual(httpRequestData.Headers, theoryData.ExpectedHttpRequestHeaders, context); + IdentityComparer.AreStringEnumDictionariesEqual(httpRequestData.Headers, theoryData.ExpectedHttpRequestHeaders, context); theoryData.ExpectedException.ProcessNoException(context); } catch (Exception ex) @@ -259,7 +259,7 @@ public async Task ToHttpRequestDataAsync(SignedHttpRequestUtilityTheoryData theo IdentityComparer.AreStringsEqual(httpRequestData.Method, theoryData.ExpectedHttpRequestData.Method, context); IdentityComparer.AreUrisEqual(httpRequestData.Uri, theoryData.ExpectedHttpRequestData.Uri, context); IdentityComparer.AreBytesEqual(httpRequestData.Body, theoryData.ExpectedHttpRequestData.Body, context); - IdentityComparer.AreStingEnumDictionariesEqual(httpRequestData.Headers, theoryData.ExpectedHttpRequestData.Headers, context); + IdentityComparer.AreStringEnumDictionariesEqual(httpRequestData.Headers, theoryData.ExpectedHttpRequestData.Headers, context); theoryData.ExpectedException.ProcessNoException(context); } diff --git a/test/Microsoft.IdentityModel.TestUtils/CompareContext.cs b/test/Microsoft.IdentityModel.TestUtils/CompareContext.cs index 2fae47d5fd..78dd04e7c8 100644 --- a/test/Microsoft.IdentityModel.TestUtils/CompareContext.cs +++ b/test/Microsoft.IdentityModel.TestUtils/CompareContext.cs @@ -21,14 +21,14 @@ public CompareContext(string title) public CompareContext(TheoryDataBase theoryData) { - PropertiesToIgnoreWhenComparing = theoryData.PropertiesToIgnoreWhenComparing; + PropertiesToIgnoreWhenComparing = new Dictionary>(theoryData.PropertiesToIgnoreWhenComparing); Title = theoryData.TestId; } public CompareContext(string testName, TheoryDataBase theoryData) { Title = testName; - PropertiesToIgnoreWhenComparing = theoryData.PropertiesToIgnoreWhenComparing; + PropertiesToIgnoreWhenComparing = new Dictionary>(theoryData.PropertiesToIgnoreWhenComparing); } public CompareContext(CompareContext other) diff --git a/test/Microsoft.IdentityModel.TestUtils/DataSets.cs b/test/Microsoft.IdentityModel.TestUtils/DataSets.cs index a3390fbe7b..501e5ba404 100644 --- a/test/Microsoft.IdentityModel.TestUtils/DataSets.cs +++ b/test/Microsoft.IdentityModel.TestUtils/DataSets.cs @@ -10,274 +10,113 @@ namespace Microsoft.IdentityModel.TestUtils { public class DataSets { - public static JsonWebKey JsonWebKeyFromPing1; - public static JsonWebKey JsonWebKeyFromPing2; - public static JsonWebKey JsonWebKeyFromPing3; - public static JsonWebKey JsonWebKey1; - public static JsonWebKey JsonWebKey2; - public static JsonWebKey JsonWebKeyAdditionalData1; - public static JsonWebKey JsonWebKeyBadX509Data; - public static JsonWebKey JsonWebKeyES256; - public static JsonWebKey JsonWebKeyES384; - public static JsonWebKey JsonWebKeyES512; - - public static JsonWebKeySet JsonWebKeySet1; - public static JsonWebKeySet JsonWebKeySet2; - public static JsonWebKeySet JsonWebKeySetX509Data; - public static JsonWebKeySet JsonWebKeySetAdditionalData1; - public static JsonWebKeySet JsonWebKeySetEC; - public static JsonWebKeySet JsonWebKeySetOnlyX5t; - - // interop - public static string GoogleCertsFile = "google-certs.json"; - public static JsonWebKeySet GoogleCertsExpected; - - public static string JsonWebKey_X5c_1 = "MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"; - public static string JsonWebKey_X5c_2 = "MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"; - - public static string JsonWebKeyFromPingString1 = - @"{ ""e"":""AQAB"", - ""kid"":""20am7"", - ""kty"":""RSA"", - ""n"":""mhupHfUtg_gHIqwu2wm8CprXY-gKqbPMV6tEYVqkyYrHugzQ_YDYAHr7vWo5Pe_3gIujSFwpqIfXaP8-Fl3O5fQhMo1lMv4DdRabyDLEpv7YO9qoVKTmDOZqYZx-AYBr5x1Zh2xWByI6_0dsPtCjD1pFZfg_SxNEcLPyH1aY6dT8CWYu32qG4O0WF4EihZzMkzSn8fyh8RXbMf5U9Wm2kgb0g8jK62S7MoF4IlhFaJreq898wgUohhPwR8P3X-gk0XQJAFcogEf04Fw4UmKo3z1B6mcNbPRfImhWw4wtLkhp_KIqKNOkMsSpYGSLrCvqQpgK56EJZExrmb7WozjwHw"", - ""use"":""sig"" - }"; - - public static string JsonWebKeyString1 = - @"{ ""alg"":""SHA256"", - ""e"":""AQAB"", - ""key_ops"":[""signing""], - ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""kty"":""RSA"", - ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng""], - ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""x5u"":""https://jsonkeyurl"", - ""use"":""sig"" - }"; - - public static string JsonWebKeyString2 = - @"{ ""alg"":""SHA256"", - ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], - ""use"":""sig"" - }"; - - public static string JsonWebKeyAdditionalDataString1 = - @"{ ""alg"":""SHA256"", - ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], - ""use"":""sig"", - ""additionalProperty"":""additionalValue"" - }"; + public static string X5C_1 = "MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"; + public static string X5C_2 = "MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"; + + #region JsonWebKeys + public static string JsonWebKeyWithOneRsaKey = + @"{ ""e"":""AQAB"", + ""kid"":""20am7"", + ""kty"":""RSA"", + ""n"":""mhupHfUtg_gHIqwu2wm8CprXY-gKqbPMV6tEYVqkyYrHugzQ_YDYAHr7vWo5Pe_3gIujSFwpqIfXaP8-Fl3O5fQhMo1lMv4DdRabyDLEpv7YO9qoVKTmDOZqYZx-AYBr5x1Zh2xWByI6_0dsPtCjD1pFZfg_SxNEcLPyH1aY6dT8CWYu32qG4O0WF4EihZzMkzSn8fyh8RXbMf5U9Wm2kgb0g8jK62S7MoF4IlhFaJreq898wgUohhPwR8P3X-gk0XQJAFcogEf04Fw4UmKo3z1B6mcNbPRfImhWw4wtLkhp_KIqKNOkMsSpYGSLrCvqQpgK56EJZExrmb7WozjwHw"", + ""use"":""sig"" + }"; public static string JsonWebKeyBadFormatString1 = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], - ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""use""::""sig"" - }"; + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""x5c"":[""MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], + ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""use""::""sig"" + }"; public static string JsonWebKeyBadFormatString2 = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""x5c"":""M""IIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], - ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""use""::""sig"" - }"; + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""x5c"":""M""IIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], + ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""use""::""sig"" + }"; public static string JsonWebKeyBadRsaExponentString = - @"{ ""e"":""AQABC"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""use"":""sig"" - }"; + @"{ ""e"":""AQABC"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sig"" + }"; public static string JsonWebKeyBadRsaModulusString = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""use"":""sig"" - }"; + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sig"" + }"; public static string JsonWebKeyRsaNoKidString = - @"{ ""e"":""AQAB"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""use"":""sig"" - }"; + @"{ ""e"":""AQAB"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sig"" + }"; public static string JsonWebKeyKtyNotRsaString = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSAA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""use"":""sig"" - }"; + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSAA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sig"" + }"; public static string JsonWebKeyUseNotSigString = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""use"":""sigg"" - }"; + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sigg"" + }"; public static string JsonWebKeyX509DataString = - @"{ ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""kty"":""RSA"", - ""use"":""sig"", - ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng""] - }"; - - public static string JsonWebKeyBadX509String = - @"{ ""e"":""AQAB"", - ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""kty"":""RSA"", - ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", - ""x5c"":[""==MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], - ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", - ""use"":""sig"" - }"; + @"{ ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""kty"":""RSA"", + ""use"":""sig"", + ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng""] + }"; public static string JsonWebKeyBadECCurveString = - @"{ - ""kty"": ""EC"", - ""alg"": ""ES521"", - ""use"": ""sig"", - ""crv"": ""P-999"", - ""kid"": ""unknownCrv"", - ""x"": ""AX0BXx6mpDjvGk-NLTwobKNjfAP4QCRjtKi8UQsuPqQ2sRKITAcSti3UMn0COcrG_FVgEDNPyPVlSi5LnUl0dREr"", - ""y"": ""AZ8DlNxsA6eCj_JL9Rz8uU4eacd-XX--ek8-VCOgv3YNRPeN_2PJauJL7q9Pg1MSe8zEaLIRhM4SGWJ4SI1rMhlW"" - }"; - - public static string JsonWebKeyES256String = - @"{ - ""kty"": ""EC"", - ""alg"": ""ES256"", - ""use"": ""sig"", - ""crv"": ""P-256"", - ""kid"": ""JsonWebKeyEcdsa256"", - ""x"": ""luR290c8sXxbOGhNquQ3J3rh763Os4D609cHK-L_5fA"", - ""y"": ""tUqUwtaVHwc7_CXnuBrCpMQTF5BJKdFnw9_JkSIXWpQ"" - }"; - - public static string JsonWebKeyES384String = - @"{ - ""kty"": ""EC"", - ""alg"": ""ES384"", - ""use"": ""sig"", - ""crv"": ""P-384"", - ""kid"": ""JsonWebKeyEcdsa384"", - ""x"": ""5mn3HaDoUgdNTFCACaWIvrpriQTloEbMbx4eUu_XvB4pyExig45VIozMnj7FedJg"", - ""y"": ""Vh872HVKNHrzlVu0Ko-3dN-eHoDYBeZgdGLAqenyZ0_X_TctwT6MVLxcAvwbJG5l"" - }"; - - public static string JsonWebKeyES512String = - @"{ - ""kty"": ""EC"", - ""alg"": ""ES512"", - ""use"": ""sig"", - ""crv"": ""P-521"", - ""kid"": ""JsonWebKeyEcdsa521"", - ""x"": ""AX0BXx6mpDjvGk-NLTwobKNjfAP4QCRjtKi8UQsuPqQ2sRKITAcSti3UMn0COcrG_FVgEDNPyPVlSi5LnUl0dREr"", - ""y"": ""AZ8DlNxsA6eCj_JL9Rz8uU4eacd-XX--ek8-VCOgv3YNRPeN_2PJauJL7q9Pg1MSe8zEaLIRhM4SGWJ4SI1rMhlW"" - }"; + @"{ + ""kty"": ""EC"", + ""alg"": ""ES521"", + ""use"": ""sig"", + ""crv"": ""P-999"", + ""kid"": ""unknownCrv"", + ""x"": ""AX0BXx6mpDjvGk-NLTwobKNjfAP4QCRjtKi8UQsuPqQ2sRKITAcSti3UMn0COcrG_FVgEDNPyPVlSi5LnUl0dREr"", + ""y"": ""AZ8DlNxsA6eCj_JL9Rz8uU4eacd-XX--ek8-VCOgv3YNRPeN_2PJauJL7q9Pg1MSe8zEaLIRhM4SGWJ4SI1rMhlW"" + }"; public static string JsonWebKeyOnlyX5tString = - @"{ - ""kty"": ""RSA"", - ""use"": ""sig"", - ""kid"":""pqoeamb2e5YVzR6_rqFpiCrFZgw"", - ""x5t"":""pqoeamb2e5YVzR6_rqFpiCrFZgw"" - }"; + @"{ + ""kty"": ""RSA"", + ""use"": ""sig"", + ""kid"":""pqoeamb2e5YVzR6_rqFpiCrFZgw"", + ""x5t"":""pqoeamb2e5YVzR6_rqFpiCrFZgw"" + }"; public static string JsonWebKeyNoKtyString = - @"{ ""e"":""AQAB"", - ""kid"":""20am7"", - ""n"":""mhupHfUtg_gHIqwu2wm8CprXY-gKqbPMV6tEYVqkyYrHugzQ_YDYAHr7vWo5Pe_3gIujSFwpqIfXaP8-Fl3O5fQhMo1lMv4DdRabyDLEpv7YO9qoVKTmDOZqYZx-AYBr5x1Zh2xWByI6_0dsPtCjD1pFZfg_SxNEcLPyH1aY6dT8CWYu32qG4O0WF4EihZzMkzSn8fyh8RXbMf5U9Wm2kgb0g8jK62S7MoF4IlhFaJreq898wgUohhPwR8P3X-gk0XQJAFcogEf04Fw4UmKo3z1B6mcNbPRfImhWw4wtLkhp_KIqKNOkMsSpYGSLrCvqQpgK56EJZExrmb7WozjwHw"", - ""use"":""sig"" - }"; - - public static string JsonWebKeySetBadRsaExponentString = @"{ ""keys"":[" + JsonWebKeyBadRsaExponentString + "]}"; - public static string JsonWebKeySetBadRsaModulusString = @"{ ""keys"":[" + JsonWebKeyBadRsaModulusString + "]}"; - public static string JsonWebKeySetUseNoKidString = @"{ ""keys"":[" + JsonWebKeyRsaNoKidString + "]}"; - public static string JsonWebKeySetKtyNotRsaString = @"{ ""keys"":[" + JsonWebKeyKtyNotRsaString + "]}"; - public static string JsonWebKeySetUseNotSigString = @"{ ""keys"":[" + JsonWebKeyUseNotSigString + "]}"; - public static string JsonWebKeySetX509DataString = @"{ ""keys"":[" + JsonWebKeyX509DataString + "]}"; - public static string JsonWebKeySetBadX509String = @"{ ""keys"":[" + JsonWebKeyBadX509String + "]}"; - public static string JsonWebKeySetBadECCurveString = @"{ ""keys"":[" + JsonWebKeyBadECCurveString + "]}"; - public static string JsonWebKeySetOnlyX5tString = @"{ ""keys"":[" + JsonWebKeyOnlyX5tString + "]}"; - public static string JsonWebKeySetUseNoKtyString = @"{ ""keys"":[" + JsonWebKeyNoKtyString + "]}"; - - // Key Sets - public static string JsonWebKeySet = "JsonWebKeySet.json"; - public static string JsonWebKeySetString1 = @"{ ""keys"":[" + JsonWebKeyString1 + "," + JsonWebKeyString2 + "]}"; - public static string JsonWebKeySetString2 = @"{ ""keys"":[" + JsonWebKeyString2 + "]}"; - public static string JsonWebKeySetECCString = @"{ ""keys"":[" + JsonWebKeyES256String + "," + JsonWebKeyES384String + "," + JsonWebKeyES512String + "]}"; - public static string JsonWebKeySetAdditionalDataString1 = @"{ ""keys"":[" + JsonWebKeyAdditionalDataString1 + "]" + @", ""additionalProperty"":""additionalValue""}"; - public static string JsonWebKeySetOneValidRsaOneInvalidRsaString = @"{ ""keys"":[" + JsonWebKeyFromPingString1 + "," + JsonWebKeyBadRsaExponentString + "]}"; - public static string JsonWebKeySetOneInvalidEcOneValidEcString = @"{ ""keys"":[" + JsonWebKeyBadECCurveString + "," + JsonWebKeyES256String + "]}"; - public static string JsonWebKeySetOneValidRsaOneInvalidEcString = @"{ ""keys"":[" + JsonWebKeyFromPingString1 + "," + JsonWebKeyBadECCurveString + "]}"; - public static string JsonWebKeySetBadFormatingString = - @"{ ""keys"":[ - { ""e"":""AQAB"", - ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""kty"":""RSA"", - ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", - ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"" - ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""use"":""sig"" - } - ]}"; - - public static string JsonWebKeySetSingleX509DataString = - @"{ ""keys"":[ - { ""e"":""AQAB"", - ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""kty"":""RSA"", - ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", - ""x5c"":""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"", - ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", - ""use"":""sig"" - } - ]}"; - - public static string JsonWebKeySetEvoString = - @"{ ""keys"":[ - { - ""kty"":""RSA"", - ""use"":""sig"", - ""kid"":""HBxl9mAe6gxavCkcoOU2THsDNa0"", - ""x5t"":""HBxl9mAe6gxavCkcoOU2THsDNa0"", - ""n"":""0afCaiPd_xl_ewZGfOkxKwYPfI4Efu0COfzajK_gnviWk7w3R-88Dmb0j24DSn1qVR3ptCnA1-QUfUMyhvl8pT5-t7oRkLNPzp0hVV-dAG3ZoMaSEMW0wapshA6LVGROpBncDmc66hx5-t3eOFA24fiKfQiv2TJth3Y9jhHnLe7GBOoomWYx_pJiEG3mhYFIt7shaEwNcEjo34vr1WWzRm8D8gogjrJWd1moyeGftWLzvfp9e79QwHYJv907vQbFrT7LYuy8g7-Rpxujgumw2mx7CewcCZXwPiZ-raM3Ap1FhINiGpd5mbbYrFDDFIWAjWPUY6KNvXtc24yUfZr4MQ"", - ""e"":""AQAB"", - ""x5c"":[ - ""MIIDBTCCAe2gAwIBAgIQWcq84CdVhKVEcKbZdMOMGjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE5MDMxNDAwMDAwMFoXDTIxMDMxNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGnwmoj3f8Zf3sGRnzpMSsGD3yOBH7tAjn82oyv4J74lpO8N0fvPA5m9I9uA0p9alUd6bQpwNfkFH1DMob5fKU+fre6EZCzT86dIVVfnQBt2aDGkhDFtMGqbIQOi1RkTqQZ3A5nOuocefrd3jhQNuH4in0Ir9kybYd2PY4R5y3uxgTqKJlmMf6SYhBt5oWBSLe7IWhMDXBI6N+L69Vls0ZvA/IKII6yVndZqMnhn7Vi8736fXu/UMB2Cb/dO70Gxa0+y2LsvIO/kacbo4LpsNpsewnsHAmV8D4mfq2jNwKdRYSDYhqXeZm22KxQwxSFgI1j1GOijb17XNuMlH2a+DECAwEAAaMhMB8wHQYDVR0OBBYEFIkZ5wrSV8lohIsreOmig7h5wQDkMA0GCSqGSIb3DQEBCwUAA4IBAQAd8sKZLwZBocM4pMIRKarK60907jQCOi1m449WyToUcYPXmU7wrjy9fkYwJdC5sniItVBJ3RIQbF/hyjwnRoIaEcWYMAftBnH+c19WIuiWjR3EHnIdxmSopezl/9FaTNghbKjZtrKK+jL/RdkMY9uWxwUFLjTAtMm24QOt2+CGntBA9ohQUgiML/mlUpf4qEqa2/Lh+bjiHl3smg4TwuIl0i/TMN9Rg7UgQ6BnqfgiuMl6BtBiatNollwgGNI2zJEi47MjdeMf8+C3tXs//asqqlqJCyVLwN7AN47ynYmkl89MleOfKIojhrGRxryZG2nRjD9u/kZbPJ8e3JE9px67"" - ], - ""issuer"":""https://login.microsoftonline.com/{tenantid}/v2.0"" - } - ]}"; - - static DataSets() - { - JsonWebKeyFromPing1 = new JsonWebKey + @"{ ""e"":""AQAB"", + ""kid"":""20am7"", + ""n"":""mhupHfUtg_gHIqwu2wm8CprXY-gKqbPMV6tEYVqkyYrHugzQ_YDYAHr7vWo5Pe_3gIujSFwpqIfXaP8-Fl3O5fQhMo1lMv4DdRabyDLEpv7YO9qoVKTmDOZqYZx-AYBr5x1Zh2xWByI6_0dsPtCjD1pFZfg_SxNEcLPyH1aY6dT8CWYu32qG4O0WF4EihZzMkzSn8fyh8RXbMf5U9Wm2kgb0g8jK62S7MoF4IlhFaJreq898wgUohhPwR8P3X-gk0XQJAFcogEf04Fw4UmKo3z1B6mcNbPRfImhWw4wtLkhp_KIqKNOkMsSpYGSLrCvqQpgK56EJZExrmb7WozjwHw"", + ""use"":""sig"" + }"; + + public static JsonWebKey JsonWebKeyFromPing1 => + new JsonWebKey { E = "AQAB", Kid = "20am7", @@ -286,7 +125,8 @@ static DataSets() Use = "sig" }; - JsonWebKeyFromPing2 = new JsonWebKey + public static JsonWebKey JsonWebKeyFromPing2 => + new JsonWebKey { E = "AQAB", Kid = "20am3", @@ -295,7 +135,8 @@ static DataSets() Use = "sig" }; - JsonWebKeyFromPing3 = new JsonWebKey + public static JsonWebKey JsonWebKeyFromPing3 => + new JsonWebKey { E = "AQAB", Kid = "20alz", @@ -304,34 +145,90 @@ static DataSets() Use = "sig" }; - JsonWebKey1 = new JsonWebKey + public static JsonWebKey JsonWebKey1 + { + get { - Alg = "SHA256", - E = "AQAB", - Kid = "NGTFvdK-fythEuLwjpwAJOM9n-A", - Kty = "RSA", - N = "rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw==", - X5t = "NGTFvdK-fythEuLwjpwAJOM9n-A", - X5u = "https://jsonkeyurl", - Use = "sig", - }; + JsonWebKey jsonWebKey = new JsonWebKey + { + Alg = "SHA256", + E = "AQAB", + Kid = "NGTFvdK-fythEuLwjpwAJOM9n-A", + Kty = "RSA", + N = "rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw==", + Use = "sig", + X5t = "NGTFvdK-fythEuLwjpwAJOM9n-A", + X5u = "https://jsonkeyurl", + }; + + jsonWebKey.X5c.Add(X5C_1); + jsonWebKey.KeyOps.Add("signing"); + + return jsonWebKey; + } + } - JsonWebKey1.X5c.Add(JsonWebKey_X5c_1); - JsonWebKey1.KeyOps.Add("signing"); - JsonWebKey2 = new JsonWebKey + public static JsonWebKey JsonWebKey2 + { + get { - Alg = "SHA256", - E = "AQAB", - Kid = "kriMPdmBvx68skT8-mPAB3BseeA", - Kty = "RSA", - N = "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==", - X5t = "kriMPdmBvx68skT8-mPAB3BseeA", - Use = "sig", - }; + JsonWebKey jsonWebKey = new JsonWebKey + { + Alg = "SHA256", + E = "AQAB", + Kid = "kriMPdmBvx68skT8-mPAB3BseeA", + Kty = "RSA", + N = "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==", + X5t = "kriMPdmBvx68skT8-mPAB3BseeA", + Use = "sig" + }; - JsonWebKey2.X5c.Add(JsonWebKey_X5c_2); + jsonWebKey.X5c.Add(X5C_2); + + return jsonWebKey; + } + } - JsonWebKeyES256 = new JsonWebKey + public static string JsonWebKeyString => + @"{ ""alg"":""SHA256"", + ""e"":""AQAB"", + ""key_ops"":[""signing""], + ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""kty"":""RSA"", + ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", + ""use"":""sig"", + ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng""], + ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""x5u"":""https://jsonkeyurl"" + }"; + + public static string JsonWebKeyString2 = + @"{ ""alg"":""SHA256"", + ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""use"":""sig"", + ""x5c"":[""MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], + ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"" + }"; + + public static string JsonWebKeyMixedCaseString => + @"{ ""Alg"":""SHA256"", + ""E"":""AQAB"", + ""key_Ops"":[""signing""], + ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""kty"":""RSA"", + ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", + ""use"":""sig"", + ""x5C"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng""], + ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""x5T#S256"":""NGTFvdK-fythEuLwjpwAJOM9n-A256"", + ""x5u"":""https://jsonkeyurl"" + }"; + + public static JsonWebKey JsonWebKeyES256 => + new JsonWebKey { Alg = "ES256", Crv = "P-256", @@ -342,7 +239,19 @@ static DataSets() Y = "tUqUwtaVHwc7_CXnuBrCpMQTF5BJKdFnw9_JkSIXWpQ" }; - JsonWebKeyES384 = new JsonWebKey + public static string JsonWebKeyES256String => + @"{ + ""kty"": ""EC"", + ""alg"": ""ES256"", + ""use"": ""sig"", + ""crv"": ""P-256"", + ""kid"": ""JsonWebKeyEcdsa256"", + ""x"": ""luR290c8sXxbOGhNquQ3J3rh763Os4D609cHK-L_5fA"", + ""y"": ""tUqUwtaVHwc7_CXnuBrCpMQTF5BJKdFnw9_JkSIXWpQ"" + }"; + + public static JsonWebKey JsonWebKeyES384 => + new JsonWebKey { Alg = "ES384", Crv = "P-384", @@ -353,7 +262,19 @@ static DataSets() Y = "Vh872HVKNHrzlVu0Ko-3dN-eHoDYBeZgdGLAqenyZ0_X_TctwT6MVLxcAvwbJG5l" }; - JsonWebKeyES512 = new JsonWebKey + public static string JsonWebKeyES384String = + @"{ + ""kty"": ""EC"", + ""alg"": ""ES384"", + ""use"": ""sig"", + ""crv"": ""P-384"", + ""kid"": ""JsonWebKeyEcdsa384"", + ""x"": ""5mn3HaDoUgdNTFCACaWIvrpriQTloEbMbx4eUu_XvB4pyExig45VIozMnj7FedJg"", + ""y"": ""Vh872HVKNHrzlVu0Ko-3dN-eHoDYBeZgdGLAqenyZ0_X_TctwT6MVLxcAvwbJG5l"" + }"; + + public static JsonWebKey JsonWebKeyES512 => + new JsonWebKey { Alg = "ES512", Crv = "P-521", @@ -364,89 +285,189 @@ static DataSets() Y = "AZ8DlNxsA6eCj_JL9Rz8uU4eacd-XX--ek8-VCOgv3YNRPeN_2PJauJL7q9Pg1MSe8zEaLIRhM4SGWJ4SI1rMhlW" }; - JsonWebKeySet1 = new JsonWebKeySet(); - JsonWebKeySet1.Keys.Add(JsonWebKey1); - JsonWebKeySet1.Keys.Add(JsonWebKey2); + public static string JsonWebKeyES512String => + @"{ + ""kty"": ""EC"", + ""alg"": ""ES512"", + ""use"": ""sig"", + ""crv"": ""P-521"", + ""kid"": ""JsonWebKeyEcdsa521"", + ""x"": ""AX0BXx6mpDjvGk-NLTwobKNjfAP4QCRjtKi8UQsuPqQ2sRKITAcSti3UMn0COcrG_FVgEDNPyPVlSi5LnUl0dREr"", + ""y"": ""AZ8DlNxsA6eCj_JL9Rz8uU4eacd-XX--ek8-VCOgv3YNRPeN_2PJauJL7q9Pg1MSe8zEaLIRhM4SGWJ4SI1rMhlW"" + }"; + + public static string JsonWebKeyBadX509DataString => + @"{ ""e"":""AQAB"", + ""kid"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""kty"":""RSA"", + ""n"":""kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw=="", + ""x5c"":[""==MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ""], + ""x5t"":""kriMPdmBvx68skT8-mPAB3BseeA"", + ""use"":""sig"" + }"; + + public static JsonWebKey JsonWebKeyBadX509Data + { + get + { + JsonWebKey jsonWebKey = new JsonWebKey + { + E = "AQAB", + Kid = "kriMPdmBvx68skT8-mPAB3BseeA", + Kty = "RSA", + N = "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==", + X5t = "kriMPdmBvx68skT8-mPAB3BseeA", + Use = "sig" + }; - JsonWebKeySetEC = new JsonWebKeySet(); - JsonWebKeySetEC.Keys.Add(JsonWebKeyES256); - JsonWebKeySetEC.Keys.Add(JsonWebKeyES384); - JsonWebKeySetEC.Keys.Add(JsonWebKeyES512); + jsonWebKey.X5c.Add("==MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"); - var jwk = new JsonWebKey + return jsonWebKey; + } + } + #endregion + + #region JsonWebKeySets + public static string GoogleCertsFile = "google-certs.json"; + public static JsonWebKeySet GoogleCertsExpected + { + get { - Kid = "NGTFvdK-fythEuLwjpwAJOM9n-A", - Kty = "RSA", - Use = "sig", - X5t = "NGTFvdK-fythEuLwjpwAJOM9n-A" - }; + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add( + new JsonWebKey + { + Alg = "RS256", + E = "AQAB", + Kty = "RSA", + Kid = "ab844f3d4c69feee0de2501b04e1a4c8d78eead1", + N = "AKrMiv5vhYehVKXnSpZZN6lYymUIi+NS97ceYKYClMlNyj2Ln4ErWiOwjwdivG2kZnN0kKCC/XL9E+uEgsZO3ECvvDtgtFhPOR0MiqL7pp/K7d58dbKUWX/cWy8E4bm/Zmwa/g0HDcW6o19+Q85IPYXbY/6Z2oOgA9qDAoGHkjIv", + Use = "sig", + }); + + jsonWebKeySet.Keys.Add( + new JsonWebKey + { + Alg = "RS256", + E = "AQAB", + Kty = "RSA", + Kid = "550326e0aacb4674d22905a1a51a808cfa7463b0", + N = "ANLFuJO6EoKczde+YP3b1yuz2b46D7Rd7CjrbvKrzbjkH29iRFLBagT7nojwdMOPrsV+WLp/C8lfkRT7UJ38lnQh3m4oEy98HdRRMZh5Vtpbotgt4S/ugh5ansJdHSXSBTxk+X1ZnTzMOUH7ZROpxw3NcX/IFl0sshFlTbebPrDj", + Use = "sig", + }); + + return jsonWebKeySet; + } + } - jwk.X5c.Add("MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"); + public static string JsonWebKeySetString1 = @"{ ""keys"":[" + JsonWebKeyString + "," + JsonWebKeyString2 + "]}"; + public static string JsonWebKeySetECCString = @"{ ""keys"":[" + JsonWebKeyES256String + "," + JsonWebKeyES384String + "," + JsonWebKeyES512String + "]}"; + public static string JsonWebKeySetOneValidRsaOneInvalidRsaString = @"{ ""keys"":[" + JsonWebKeyWithOneRsaKey + "," + JsonWebKeyBadRsaExponentString + "]}"; + public static string JsonWebKeySetOneInvalidEcOneValidEcString = @"{ ""keys"":[" + JsonWebKeyBadECCurveString + "," + JsonWebKeyES256String + "]}"; + public static string JsonWebKeySetOneValidRsaOneInvalidEcString = @"{ ""keys"":[" + JsonWebKeyWithOneRsaKey + "," + JsonWebKeyBadECCurveString + "]}"; + public static string JsonWebKeySetBadFormatingString = + @"{ ""keys"":[ + { ""e"":""AQAB"", + ""kid"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""kty"":""RSA"", + ""n"":""rCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVw=="", + ""x5c"":[""MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"" + ""x5t"":""NGTFvdK-fythEuLwjpwAJOM9n-A"", + ""use"":""sig"" + } + ]}"; - JsonWebKeySetX509Data = new JsonWebKeySet(); - JsonWebKeySetX509Data.Keys.Add(jwk); + public static string JsonWebKeySetEvoString = + @"{ ""keys"":[ + { + ""kty"":""RSA"", + ""use"":""sig"", + ""kid"":""HBxl9mAe6gxavCkcoOU2THsDNa0"", + ""x5t"":""HBxl9mAe6gxavCkcoOU2THsDNa0"", + ""n"":""0afCaiPd_xl_ewZGfOkxKwYPfI4Efu0COfzajK_gnviWk7w3R-88Dmb0j24DSn1qVR3ptCnA1-QUfUMyhvl8pT5-t7oRkLNPzp0hVV-dAG3ZoMaSEMW0wapshA6LVGROpBncDmc66hx5-t3eOFA24fiKfQiv2TJth3Y9jhHnLe7GBOoomWYx_pJiEG3mhYFIt7shaEwNcEjo34vr1WWzRm8D8gogjrJWd1moyeGftWLzvfp9e79QwHYJv907vQbFrT7LYuy8g7-Rpxujgumw2mx7CewcCZXwPiZ-raM3Ap1FhINiGpd5mbbYrFDDFIWAjWPUY6KNvXtc24yUfZr4MQ"", + ""e"":""AQAB"", + ""x5c"":[ + ""MIIDBTCCAe2gAwIBAgIQWcq84CdVhKVEcKbZdMOMGjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE5MDMxNDAwMDAwMFoXDTIxMDMxNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGnwmoj3f8Zf3sGRnzpMSsGD3yOBH7tAjn82oyv4J74lpO8N0fvPA5m9I9uA0p9alUd6bQpwNfkFH1DMob5fKU+fre6EZCzT86dIVVfnQBt2aDGkhDFtMGqbIQOi1RkTqQZ3A5nOuocefrd3jhQNuH4in0Ir9kybYd2PY4R5y3uxgTqKJlmMf6SYhBt5oWBSLe7IWhMDXBI6N+L69Vls0ZvA/IKII6yVndZqMnhn7Vi8736fXu/UMB2Cb/dO70Gxa0+y2LsvIO/kacbo4LpsNpsewnsHAmV8D4mfq2jNwKdRYSDYhqXeZm22KxQwxSFgI1j1GOijb17XNuMlH2a+DECAwEAAaMhMB8wHQYDVR0OBBYEFIkZ5wrSV8lohIsreOmig7h5wQDkMA0GCSqGSIb3DQEBCwUAA4IBAQAd8sKZLwZBocM4pMIRKarK60907jQCOi1m449WyToUcYPXmU7wrjy9fkYwJdC5sniItVBJ3RIQbF/hyjwnRoIaEcWYMAftBnH+c19WIuiWjR3EHnIdxmSopezl/9FaTNghbKjZtrKK+jL/RdkMY9uWxwUFLjTAtMm24QOt2+CGntBA9ohQUgiML/mlUpf4qEqa2/Lh+bjiHl3smg4TwuIl0i/TMN9Rg7UgQ6BnqfgiuMl6BtBiatNollwgGNI2zJEi47MjdeMf8+C3tXs//asqqlqJCyVLwN7AN47ynYmkl89MleOfKIojhrGRxryZG2nRjD9u/kZbPJ8e3JE9px67"" + ], + ""issuer"":""https://login.microsoftonline.com/{tenantid}/v2.0"" + } + ]}"; + + public static string JsonWebKeySetBadRsaExponentString = @"{ ""keys"":[" + JsonWebKeyBadRsaExponentString + "]}"; + public static string JsonWebKeySetBadRsaModulusString = @"{ ""keys"":[" + JsonWebKeyBadRsaModulusString + "]}"; + public static string JsonWebKeySetUseNoKidString = @"{ ""keys"":[" + JsonWebKeyRsaNoKidString + "]}"; + public static string JsonWebKeySetKtyNotRsaString = @"{ ""keys"":[" + JsonWebKeyKtyNotRsaString + "]}"; + public static string JsonWebKeySetUseNotSigString = @"{ ""keys"":[" + JsonWebKeyUseNotSigString + "]}"; + public static string JsonWebKeySetX509DataString = @"{ ""keys"":[" + JsonWebKeyX509DataString + "]}"; + public static string JsonWebKeySetBadX509String = @"{ ""keys"":[" + JsonWebKeyBadX509DataString + "]}"; + public static string JsonWebKeySetBadECCurveString = @"{ ""keys"":[" + JsonWebKeyBadECCurveString + "]}"; + public static string JsonWebKeySetOnlyX5tString = @"{ ""keys"":[" + JsonWebKeyOnlyX5tString + "]}"; + public static string JsonWebKeySetUseNoKtyString = @"{ ""keys"":[" + JsonWebKeyNoKtyString + "]}"; - JsonWebKeyAdditionalData1 = new JsonWebKey + public static JsonWebKeySet JsonWebKeySet1 + { + get { - Alg = "SHA256", - E = "AQAB", - Kid = "kriMPdmBvx68skT8-mPAB3BseeA", - Kty = "RSA", - N = "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==", - X5t = "kriMPdmBvx68skT8-mPAB3BseeA", - Use = "sig", - }; + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add(JsonWebKey1); + jsonWebKeySet.Keys.Add(JsonWebKey2); - JsonWebKeyAdditionalData1.AdditionalData["additionalProperty"] = "additionalValue"; - JsonWebKeyAdditionalData1.X5c.Add(JsonWebKey_X5c_2); - JsonWebKeySetAdditionalData1 = new JsonWebKeySet(); - JsonWebKeySetAdditionalData1.Keys.Add(JsonWebKeyAdditionalData1); - JsonWebKeySetAdditionalData1.AdditionalData["additionalProperty"] = "additionalValue"; + return jsonWebKeySet; + } + } - JsonWebKeyBadX509Data = new JsonWebKey + public static JsonWebKeySet JsonWebKeySetX509Data + { + get { - E = "AQAB", - Kid = "kriMPdmBvx68skT8-mPAB3BseeA", - Kty = "RSA", - N = "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==", - X5t = "kriMPdmBvx68skT8-mPAB3BseeA", - Use = "sig" - }; + var jsonWebKey = new JsonWebKey + { + Kid = "NGTFvdK-fythEuLwjpwAJOM9n-A", + Kty = "RSA", + Use = "sig", + X5t = "NGTFvdK-fythEuLwjpwAJOM9n-A" + }; + + jsonWebKey.X5c.Add("MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"); + + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add(jsonWebKey); + + return jsonWebKeySet; + } + } - JsonWebKeyBadX509Data.X5c.Add("==MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"); + public static JsonWebKeySet JsonWebKeySetEC + { + get + { + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add(JsonWebKeyES256); + jsonWebKeySet.Keys.Add(JsonWebKeyES384); + jsonWebKeySet.Keys.Add(JsonWebKeyES512); + + return jsonWebKeySet; + } + } - GoogleCertsExpected = new JsonWebKeySet(); - GoogleCertsExpected.Keys.Add( - new JsonWebKey + public static JsonWebKeySet JsonWebKeySetOnlyX5t + { + get + { + JsonWebKey jsonWebKey = new JsonWebKey { - Alg = "RS256", - E = "AQAB", + Kid = "pqoeamb2e5YVzR6_rqFpiCrFZgw", Kty = "RSA", - Kid = "ab844f3d4c69feee0de2501b04e1a4c8d78eead1", - N = "AKrMiv5vhYehVKXnSpZZN6lYymUIi+NS97ceYKYClMlNyj2Ln4ErWiOwjwdivG2kZnN0kKCC/XL9E+uEgsZO3ECvvDtgtFhPOR0MiqL7pp/K7d58dbKUWX/cWy8E4bm/Zmwa/g0HDcW6o19+Q85IPYXbY/6Z2oOgA9qDAoGHkjIv", Use = "sig", - }); - - GoogleCertsExpected.Keys.Add( - new JsonWebKey - { - Alg = "RS256", - E = "AQAB", - Kty = "RSA", - Kid = "550326e0aacb4674d22905a1a51a808cfa7463b0", - N = "ANLFuJO6EoKczde+YP3b1yuz2b46D7Rd7CjrbvKrzbjkH29iRFLBagT7nojwdMOPrsV+WLp/C8lfkRT7UJ38lnQh3m4oEy98HdRRMZh5Vtpbotgt4S/ugh5ansJdHSXSBTxk+X1ZnTzMOUH7ZROpxw3NcX/IFl0sshFlTbebPrDj", - Use = "sig", - }); - - var JsonWebKeyOnlyX5t = new JsonWebKey - { - Kid = "pqoeamb2e5YVzR6_rqFpiCrFZgw", - Kty = "RSA", - Use = "sig", - X5t = "pqoeamb2e5YVzR6_rqFpiCrFZgw" - }; - JsonWebKeySetOnlyX5t = new JsonWebKeySet(); - JsonWebKeySetOnlyX5t.Keys.Add(JsonWebKeyOnlyX5t); + X5t = "pqoeamb2e5YVzR6_rqFpiCrFZgw" + }; + + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add(jsonWebKey); + + return jsonWebKeySet; + } } + #endregion } } diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index e96e1d68bf..e41402809f 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -33,47 +33,37 @@ namespace Microsoft.IdentityModel.TestUtils { public class IdentityComparer { + // Dictionary of types and the validation function. + // Keep entries in alphabetical order private static readonly Dictionary> _equalityDict = new Dictionary> { + { typeof(AuthenticationProtocolMessage).ToString(), CompareAllPublicProperties }, { typeof(bool).ToString(), AreBoolsEqual }, + { typeof(byte[]).ToString(), AreBytesEqual }, + { typeof(CanonicalizingTransfrom).ToString(), CompareAllPublicProperties }, + { typeof(Claim).ToString(), CompareAllPublicProperties }, + { typeof(ClaimsIdentity).ToString(), CompareAllPublicProperties }, + { typeof(ClaimsPrincipal).ToString(), CompareAllPublicProperties }, { typeof(Collection).ToString(), ContinueCheckingEquality }, { typeof(DateTime).ToString(), AreDateTimesEqual }, { typeof(Dictionary).ToString(), AreObjectDictionariesEqual }, { typeof(Dictionary.ValueCollection).ToString(), AreValueCollectionsEqual }, + { typeof(ExclusiveCanonicalizationTransform).ToString(), CompareAllPublicProperties }, + { typeof(EnvelopedSignatureTransform).ToString(), CompareAllPublicProperties }, + { typeof(IDictionary).ToString(), AreStringDictionariesEqual}, { typeof(IEnumerable).ToString(), AreClaimsEnumsEqual }, { typeof(IEnumerable).ToString(), AreClaimsIdentitiesEnumsEqual }, { typeof(IEnumerable).ToString(), AreObjectEnumsEqual }, { typeof(IEnumerable).ToString(), AreSecurityKeyEnumsEqual }, { typeof(IEnumerable).ToString(), AreStringEnumsEqual }, { typeof(IEnumerable).ToString(), AreX509DataEnumsEqual }, - { typeof(IDictionary).ToString(), AreStringDictionariesEqual}, - { typeof(List).ToString(), AreJsonWebKeyEnumsEqual }, - { typeof(List).ToString(), AreKeyInfoEnumsEqual }, - { typeof(List).ToString(), AreSamlAssertionEnumsEqual}, - { typeof(List).ToString(), AreSamlAttributeEnumsEqual }, - { typeof(List).ToString(), AreSamlAuthorityBindingEnumsEqual }, - { typeof(List).ToString(), AreSamlActionEnumsEqual }, - { typeof(List).ToString(), AreSamlStatementEnumsEqual }, - { typeof(List).ToString(), AreSamlConditionEnumsEqual }, - { typeof(List).ToString(), AreSecurityKeyEnumsEqual }, - { typeof(List).ToString(), AreReferenceEnumsEqual }, - { typeof(List).ToString(), AreUriEnumsEqual }, - { typeof(X509Certificate2).ToString(), AreX509Certificate2Equal }, - { typeof(AuthenticationProtocolMessage).ToString(), CompareAllPublicProperties }, - { typeof(byte[]).ToString(), AreBytesEqual }, - { typeof(Claim).ToString(), CompareAllPublicProperties }, - { typeof(ClaimsIdentity).ToString(), CompareAllPublicProperties }, - { typeof(ClaimsPrincipal).ToString(), CompareAllPublicProperties }, - { typeof(ExclusiveCanonicalizationTransform).ToString(), CompareAllPublicProperties }, - { typeof(CanonicalizingTransfrom).ToString(), CompareAllPublicProperties }, - { typeof(EnvelopedSignatureTransform).ToString(), CompareAllPublicProperties }, { typeof(IssuerSerial).ToString(), CompareAllPublicProperties }, { typeof(JArray).ToString(), AreJArraysEqual }, { typeof(JObject).ToString(), AreJObjectsEqual }, { typeof(JsonElement).ToString(), AreJsonElementsEqual }, - { typeof(JsonWebKey).ToString(), CompareAllPublicProperties }, - { typeof(JsonWebKeySet).ToString(), CompareAllPublicProperties }, + { typeof(JsonWebKey).ToString(), AreJsonWebKeysEqual }, + { typeof(JsonWebKeySet).ToString(), AreJsonWebKeysEqual }, { typeof(JsonWebToken).ToString(), CompareAllPublicProperties }, { typeof(JsonWebTokenHandler).ToString(), CompareAllPublicProperties }, { typeof(JwtHeader).ToString(), CompareAllPublicProperties }, @@ -81,6 +71,17 @@ public class IdentityComparer { typeof(JwtSecurityToken).ToString(), CompareAllPublicProperties }, { typeof(JwtSecurityTokenHandler).ToString(), CompareAllPublicProperties }, { typeof(KeyInfo).ToString(), CompareAllPublicProperties }, + { typeof(List).ToString(), AreJsonWebKeyEnumsEqual }, + { typeof(List).ToString(), AreKeyInfoEnumsEqual }, + { typeof(List).ToString(), AreSamlAssertionEnumsEqual}, + { typeof(List).ToString(), AreSamlAttributeEnumsEqual }, + { typeof(List).ToString(), AreSamlAuthorityBindingEnumsEqual }, + { typeof(List).ToString(), AreSamlActionEnumsEqual }, + { typeof(List).ToString(), AreSamlStatementEnumsEqual }, + { typeof(List).ToString(), AreSamlConditionEnumsEqual }, + { typeof(List).ToString(), AreSecurityKeyEnumsEqual }, + { typeof(List).ToString(), AreReferenceEnumsEqual }, + { typeof(List).ToString(), AreUriEnumsEqual }, { typeof(OpenIdConnectConfiguration).ToString(), CompareAllPublicProperties }, { typeof(OpenIdConnectMessage).ToString(), CompareAllPublicProperties }, { typeof(Reference).ToString(), CompareAllPublicProperties }, @@ -143,11 +144,13 @@ public class IdentityComparer { typeof(WsFederationConfiguration).ToString(), CompareAllPublicProperties }, { typeof(WsFederationMessage).ToString(), CompareAllPublicProperties }, { typeof(Uri).ToString(), AreUrisEqual }, + { typeof(X509Certificate2).ToString(), AreX509Certificate2Equal }, { typeof(X509Data).ToString(), CompareAllPublicProperties }, { typeof(X509SigningCredentials).ToString(), CompareAllPublicProperties }, { typeof(TokenValidationResult).ToString(), CompareAllPublicProperties }, }; + // Keep methods in alphabetical order public static bool AreBoolsEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); @@ -170,74 +173,63 @@ public static bool AreBoolsEqual(object object1, object object2, CompareContext return context.Merge(localContext); } - public static bool AreJsonWebKeyEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } - - public static bool AreKeyInfoEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } - - public static bool AreObjectEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreObjectsEqual); - } - - public static bool AreReferenceEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } - - public static bool AreUriEnumsEqual(object object1, object object2, CompareContext context) + public static bool AreBytesEqual(object object1, object object2, CompareContext context) { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, localContext)) + return context.Merge(localContext); - public static bool AreSamlAttributeEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } + var bytes1 = (byte[])object1; + var bytes2 = (byte[])object2; - public static bool AreSamlConditionEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } + if (bytes1.Length != bytes2.Length) + { + localContext.Diffs.Add("(bytes1.Length != bytes2.Length)"); + } + else + { + for (int i = 0; i < bytes1.Length; i++) + { + if (bytes1[i] != bytes2[i]) + { + localContext.Diffs.Add($"'{bytes1}'"); + localContext.Diffs.Add("!="); + localContext.Diffs.Add($"'{bytes2}'"); + } + } + } - public static bool AreSamlStatementEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + return context.Merge(localContext); } - public static bool AreSamlActionEnumsEqual(object object1, object object2, CompareContext context) + public static bool AreClaimsEqual(Claim claim1, Claim claim2, CompareContext context) { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(claim1, claim2, localContext)) + return context.Merge(localContext); - public static bool AreSamlAuthorityBindingEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + CompareAllPublicProperties(claim1, claim2, localContext); + return context.Merge(localContext); } - public static bool AreSamlAssertionEnumsEqual(object object1, object object2, CompareContext context) + public static bool AreClaimsIdentitiesEqual(ClaimsIdentity identity1, ClaimsIdentity identity2, CompareContext context) { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); - } + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(identity1, identity2, localContext)) + return context.Merge(localContext); - public static bool AreStringEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreStringsEqual); + CompareAllPublicProperties(identity1, identity2, localContext); + return context.Merge(localContext); } - public static bool AreSecurityKeyEnumsEqual(object object1, object object2, CompareContext context) + public static bool AreClaimsPrincipalsEqual(ClaimsPrincipal principal1, ClaimsPrincipal principal2, CompareContext context) { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreSecurityKeysEqual); - } + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(principal1, principal2, localContext)) + return context.Merge(localContext); - public static bool AreX509DataEnumsEqual(object object1, object object2, CompareContext context) - { - return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + CompareAllPublicProperties(principal1, principal2, localContext); + return context.Merge(localContext); } public static bool AreEnumsEqual(IEnumerable object1, IEnumerable object2, CompareContext context, Func areEqual) @@ -254,8 +246,8 @@ public static bool AreEnumsEqual(IEnumerable object1, IEnumerable objec int numMatched = 0; int numToMatch = toMatch.Count; CompareContext localContext = new CompareContext(context); - List> matchedTs = new List>(); - + List> matchedTs = new List>(); + // helps debugging to see what didn't match List notMatched = new List(); foreach (var t in object1) @@ -465,6 +457,15 @@ public static bool AreClaimsIdentitiesEnumsEqual(object object1, object object2, return context.Merge(localContext); } + public static bool AreConfigurationValidationResultEqual(ConfigurationValidationResult result1, ConfigurationValidationResult result2, CompareContext context) + { + var localContext = new CompareContext(context); + if (ContinueCheckingEquality(result1, result2, localContext)) + CompareAllPublicProperties(result1, result2, localContext); + + return context.Merge(localContext); + } + public static bool AreDateTimesEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); @@ -488,7 +489,7 @@ public static bool AreEqual(object object1, object object2) public static bool AreEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); - + // Check if either t1 or t2 are null or references of each other to see if we can terminate early. if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); @@ -512,14 +513,6 @@ public static bool AreEqual(object object1, object object2, CompareContext conte #endif _equalityDict[inter](object1, object2, localContext); } -// else -// { -// CompareAllPublicProperties(object1, object2, localContext); -//#if CheckIfCompared -// wasCompared = true; -//#endif - -// } #if CheckIfCompared if (!wasCompared) @@ -556,7 +549,7 @@ public static bool AreJObjectsEqual(Object object1, Object object2, CompareConte var a1 = (JObject)object1; var a2 = (JObject)object2; - if (!JToken.DeepEquals(a1,a2)) + if (!JToken.DeepEquals(a1, a2)) { localContext.Diffs.Add($"JObjects are not equal."); } @@ -564,90 +557,166 @@ public static bool AreJObjectsEqual(Object object1, Object object2, CompareConte return context.Merge(localContext); } - private static bool AreObjectsEqual(object object1, object object2, CompareContext context) + public static bool AreJsonElementsEqual(object obj1, object obj2, CompareContext context) { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, localContext)) + var localContext = new CompareContext(context) { IgnoreType = true }; + if (!ContinueCheckingEquality(obj1, obj2, localContext)) return context.Merge(localContext); - AreEqual(object1, object2, localContext); + JsonElement jsonElement1 = (JsonElement)obj1; + JsonElement jsonElement2 = (JsonElement)obj2; + + if (jsonElement1.ValueKind != jsonElement2.ValueKind) + { + localContext.Diffs.Add($"jsonElement1.ValueKind != jsonElement2.ValueKind. '{jsonElement1.ValueKind}' != '{jsonElement2.ValueKind}'."); + return context.Merge(localContext); + } + + string str1 = jsonElement1.GetRawText(); + string str2 = jsonElement2.GetRawText(); + + if (str1 != str2) + { + localContext.Diffs.Add($"jsonElement1.GetRawText() != jsonElement2.GetRawText(). '{jsonElement1.GetRawText()}' != '{jsonElement2.GetRawText()}'."); + return context.Merge(localContext); + } return context.Merge(localContext); } - private static bool AreValueCollectionsEqual(Object object1, Object object2, CompareContext context) + public static bool AreJsonWebKeysEqual(object object1, object object2, CompareContext context) { - Dictionary.ValueCollection vc1 = (Dictionary.ValueCollection)object1; - Dictionary.ValueCollection vc2 = (Dictionary.ValueCollection)object2; - return true; + var localContext = new CompareContext(context) { IgnoreType = true }; + if (!ContinueCheckingEquality(object1, object2, localContext)) + return context.Merge(localContext); + + Type jsonWebKeyType1 = object1.GetType(); + Type jsonWebKeyType2 = object2.GetType(); + + if (jsonWebKeyType1 == jsonWebKeyType2) + CompareAllPublicProperties(object1, object2, localContext); + else + CompareAllPublicPropertiesCrossVersion(object1, object2, localContext); + + return context.Merge(localContext); } - public static bool AreBytesEqual(object object1, object object2, CompareContext context) + public static bool CompareAllPublicPropertiesCrossVersion(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context); + var localContext = new CompareContext(context) { IgnoreType = true }; if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); - var bytes1 = (byte[]) object1; - var bytes2 = (byte[]) object2; + Type objectType1 = object1.GetType(); + Type objectType2 = object2.GetType(); - if (bytes1.Length != bytes2.Length) - { - localContext.Diffs.Add("(bytes1.Length != bytes2.Length)"); - } - else + var propertyInfos1 = objectType1.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var propertyInfos2 = objectType2.GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (PropertyInfo propertyInfo1 in propertyInfos1) { - for (int i = 0; i < bytes1.Length; i++) + bool skipProperty = false; + if (context.PropertiesToIgnoreWhenComparing != null && context.PropertiesToIgnoreWhenComparing.TryGetValue(objectType1, out List propertiesToIgnore)) { - if (bytes1[i] != bytes2[i]) + foreach (var val in propertiesToIgnore) + if (string.Equals(val, propertyInfo1.Name, StringComparison.OrdinalIgnoreCase)) + { + skipProperty = true; + break; + } + } + + if (skipProperty) + continue; + + // find a PropertyInfo in the second object that matches the first + PropertyInfo propertyInfoFound = null; + foreach (PropertyInfo propertyInfo2 in propertyInfos2) + { + if (propertyInfo2.Name == propertyInfo1.Name) { - localContext.Diffs.Add($"'{bytes1}'"); - localContext.Diffs.Add("!="); - localContext.Diffs.Add($"'{bytes2}'"); + propertyInfoFound = propertyInfo2; + break; + } + } + + // log an error if the property info cannot be found + if (propertyInfoFound == null) + { + localContext.AddDiff($"property not found when comparing objects: {propertyInfo1.Name}"); + continue; + } + + // ensure there is a get method + if (propertyInfo1.GetMethod != null) + { + var propertyContext = new CompareContext(context); + + object val1 = propertyInfo1.GetValue(object1, null); + object val2 = propertyInfoFound.GetValue(object2, null); + if ((val1 == null) && (val2 == null)) + continue; + + if ((val1 == null) || (val2 == null)) + { + propertyContext.Diffs.Add($"{propertyInfo1.Name}:"); + propertyContext.Diffs.Add(BuildStringDiff(propertyInfoFound.Name, val1, val2)); + } + else if (val1.GetType().BaseType == typeof(System.ValueType) && !_equalityDict.Keys.Contains(val1.GetType().ToString())) + { + if (!val1.Equals(val2)) + { + propertyContext.Diffs.Add($"{propertyInfo1.Name}:"); + propertyContext.Diffs.Add(BuildStringDiff(propertyInfoFound.Name, val1, val2)); + } + } + else + { + AreEqual(val1, val2, propertyContext); + localContext.Merge($"{propertyInfoFound.Name}:", propertyContext); } } } - + return context.Merge(localContext); } - public static bool AreClaimsEqual(Claim claim1, Claim claim2, CompareContext context) + public static bool AreJsonWebKeyEnumsEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(claim1, claim2, localContext)) - return context.Merge(localContext); - - CompareAllPublicProperties(claim1, claim2, localContext); - return context.Merge(localContext); + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); } - public static bool AreClaimsIdentitiesEqual(ClaimsIdentity identity1, ClaimsIdentity identity2, CompareContext context) + public static bool AreJwtSecurityTokensEqual(JwtSecurityToken jwt1, JwtSecurityToken jwt2, CompareContext context) { var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(identity1, identity2, localContext)) + if (!ContinueCheckingEquality(jwt1, jwt2, localContext)) return context.Merge(localContext); - CompareAllPublicProperties(identity1, identity2, localContext); + CompareAllPublicProperties(jwt1, jwt2, localContext); return context.Merge(localContext); } - public static bool AreClaimsPrincipalsEqual(ClaimsPrincipal principal1, ClaimsPrincipal principal2, CompareContext context) + public static bool AreKeyInfosEqual(KeyInfo keyInfo1, KeyInfo keyInfo2, CompareContext context) { var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(principal1, principal2, localContext)) - return context.Merge(localContext); + if (ContinueCheckingEquality(keyInfo1, keyInfo2, context)) + CompareAllPublicProperties(keyInfo1, keyInfo2, localContext); - CompareAllPublicProperties(principal1, principal2, localContext); return context.Merge(localContext); } + public static bool AreKeyInfoEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + public static bool AreObjectDictionariesEqual(Object object1, Object object2, CompareContext context) { var localContext = new CompareContext(context); if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); - IDictionary dictionary1 = new Dictionary(); + IDictionary dictionary1 = new Dictionary(); foreach (var kv in (IDictionary)object1) if (!context.DictionaryKeysToIgnoreWhenComparing.Contains(kv.Key)) dictionary1.Add(kv); @@ -668,7 +737,7 @@ public static bool AreObjectDictionariesEqual(Object object1, Object object2, Co if (dictionary2.ContainsKey(key)) { - if (dictionary1[key].GetType() != dictionary2[key].GetType()) + if (dictionary1[key].GetType() != dictionary2[key].GetType() && dictionary1[key].GetType() != typeof(JsonWebKey)) { localContext.Diffs.Add($"dictionary1[{key}].GetType() != dictionary2[{key}].GetType(). '{dictionary1[key].GetType()}' : '{dictionary2[key].GetType()}'"); continue; @@ -704,113 +773,25 @@ public static bool AreObjectDictionariesEqual(Object object1, Object object2, Co return context.Merge(localContext); } - public static bool AreStingEnumDictionariesEqual(IDictionary> dictionary1, IDictionary> dictionary2, CompareContext context) - { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(dictionary1, dictionary2, localContext)) - return context.Merge(localContext); - - if (dictionary1.Count != dictionary2.Count) - localContext.Diffs.Add($"(dictionary1.Count != dictionary2.Count: {dictionary1.Count}, {dictionary2.Count})"); - - int numMatched = 0; - foreach (string key in dictionary1.Keys) - { - if (dictionary2.ContainsKey(key)) - { - var obj1 = dictionary1[key]; - var obj2 = dictionary2[key]; - if (obj1.GetType().BaseType == typeof(System.ValueType)) - { - if (!obj1.Equals(obj2)) - localContext.Diffs.Add(BuildStringDiff(key, obj1, obj2)); - } - else - { - if (AreEqual(obj1, obj2, context)) - numMatched++; - } - } - else - { - localContext.Diffs.Add("dictionary1[key] ! found in dictionary2. key: " + key); - } - } - - return context.Merge(localContext); - } - - public static bool AreStringDictionariesEqual(Object object1, Object object2, CompareContext context) + private static bool AreObjectsEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); - IDictionary dictionary1 = (IDictionary)object1; - IDictionary dictionary2 = (IDictionary)object2; - - if (dictionary1.Count != dictionary2.Count) - localContext.Diffs.Add($"(dictionary1.Count != dictionary2.Count: {dictionary1.Count}, {dictionary2.Count})"); - - int numMatched = 0; - foreach (string key in dictionary1.Keys) - { - if (dictionary2.ContainsKey(key)) - { - if (!dictionary1[key].Equals(dictionary2[key])) - { - localContext.Diffs.Add($"dictionary1[key] != dictionary2[key], key: '{key}' value1, value2: '{dictionary1[key]}' + '{dictionary2[key]}'"); - } - else - { - numMatched++; - } - } - else - { - localContext.Diffs.Add("dictionary1[key] ! found in dictionary2. key: " + key); - } - } + AreEqual(object1, object2, localContext); - context.Diffs.AddRange(localContext.Diffs); - return localContext.Diffs.Count == 0; + return context.Merge(localContext); } - public static bool AreJwtSecurityTokensEqual(JwtSecurityToken jwt1, JwtSecurityToken jwt2, CompareContext context) + public static bool AreObjectEnumsEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(jwt1, jwt2, localContext)) - return context.Merge(localContext); - - CompareAllPublicProperties(jwt1, jwt2, localContext); - return context.Merge(localContext); + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreObjectsEqual); } - public static bool AreSecurityKeysEqual(SecurityKey securityKey1, SecurityKey securityKey2, CompareContext context) + public static bool AreReferenceEnumsEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(securityKey1, securityKey2, localContext)) - return context.Merge(localContext); - - // X509SecurityKey doesn't have to use reflection to get cert. - X509SecurityKey x509Key1 = securityKey1 as X509SecurityKey; - X509SecurityKey x509Key2 = securityKey2 as X509SecurityKey; - if (x509Key1 != null && x509Key2 != null) - CompareAllPublicProperties(x509Key1, x509Key2, localContext); - - SymmetricSecurityKey symKey1 = securityKey1 as SymmetricSecurityKey; - SymmetricSecurityKey symKey2 = securityKey2 as SymmetricSecurityKey; - if (symKey1 != null && symKey2 != null) - CompareAllPublicProperties(symKey1, symKey2, localContext); - - RsaSecurityKey rsaKey1 = securityKey1 as RsaSecurityKey; - RsaSecurityKey rsaKey2 = securityKey2 as RsaSecurityKey; - if (rsaKey1 != null && rsaKey2 != null) - { - CompareAllPublicProperties(rsaKey1, rsaKey2, localContext); - } - - return context.Merge(localContext); + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); } public static bool AreRsaParametersEqual(object object1, object object2, CompareContext context) @@ -873,6 +854,113 @@ public static bool AreRsaParametersEqual(object object1, object object2, Compare return context.Merge(localContext); } + public static bool AreSamlAttributeEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSamlConditionEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSamlStatementEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSamlActionEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSamlAuthorityBindingEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSamlAssertionEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } + + public static bool AreSecurityKeysEqual(SecurityKey securityKey1, SecurityKey securityKey2, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(securityKey1, securityKey2, localContext)) + return context.Merge(localContext); + + // X509SecurityKey doesn't have to use reflection to get cert. + X509SecurityKey x509Key1 = securityKey1 as X509SecurityKey; + X509SecurityKey x509Key2 = securityKey2 as X509SecurityKey; + if (x509Key1 != null && x509Key2 != null) + CompareAllPublicProperties(x509Key1, x509Key2, localContext); + + SymmetricSecurityKey symKey1 = securityKey1 as SymmetricSecurityKey; + SymmetricSecurityKey symKey2 = securityKey2 as SymmetricSecurityKey; + if (symKey1 != null && symKey2 != null) + CompareAllPublicProperties(symKey1, symKey2, localContext); + + RsaSecurityKey rsaKey1 = securityKey1 as RsaSecurityKey; + RsaSecurityKey rsaKey2 = securityKey2 as RsaSecurityKey; + if (rsaKey1 != null && rsaKey2 != null) + { + CompareAllPublicProperties(rsaKey1, rsaKey2, localContext); + } + + return context.Merge(localContext); + } + + public static bool AreSecurityKeyEnumsEqual(object object1, object object2, CompareContext context) + { + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreSecurityKeysEqual); + } + + public static bool AreSignedInfosEqual(SignedInfo signedInfo1, SignedInfo signedInfo2, CompareContext context) + { + var localContext = new CompareContext(context); + if (ContinueCheckingEquality(signedInfo1, signedInfo2, localContext)) + CompareAllPublicProperties(signedInfo1, signedInfo2, localContext); + + return context.Merge(localContext); + } + + public static bool AreStringDictionariesEqual(Object object1, Object object2, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, localContext)) + return context.Merge(localContext); + + IDictionary dictionary1 = (IDictionary)object1; + IDictionary dictionary2 = (IDictionary)object2; + + if (dictionary1.Count != dictionary2.Count) + localContext.Diffs.Add($"(dictionary1.Count != dictionary2.Count: {dictionary1.Count}, {dictionary2.Count})"); + + int numMatched = 0; + foreach (string key in dictionary1.Keys) + { + if (dictionary2.ContainsKey(key)) + { + if (!dictionary1[key].Equals(dictionary2[key])) + { + localContext.Diffs.Add($"dictionary1[key] != dictionary2[key], key: '{key}' value1, value2: '{dictionary1[key]}' + '{dictionary2[key]}'"); + } + else + { + numMatched++; + } + } + else + { + localContext.Diffs.Add("dictionary1[key] ! found in dictionary2. key: " + key); + } + } + + context.Diffs.AddRange(localContext.Diffs); + return localContext.Diffs.Count == 0; + } + public static bool AreStringsEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); @@ -893,51 +981,53 @@ public static bool AreStringsEqual(object object1, object object2, CompareContex if (!string.Equals(str1, str2, context.StringComparison)) { - localContext.Diffs.Add($"'{str1}'"); - localContext.Diffs.Add($"!="); - localContext.Diffs.Add($"'{str2}'"); - localContext.Diffs.Add($"'{context.StringComparison}'"); + localContext.Diffs.Add($"str1 != str2, StringComparison: '{context.StringComparison}'"); + localContext.Diffs.Add(str1); + localContext.Diffs.Add(str2); } return context.Merge(localContext); } - public static bool AreUrisEqual(object object1, object object2, CompareContext context) + public static bool AreStringEnumDictionariesEqual(IDictionary> dictionary1, IDictionary> dictionary2, CompareContext context) { var localContext = new CompareContext(context); - if (!ContinueCheckingEquality(object1, object2, localContext)) + if (!ContinueCheckingEquality(dictionary1, dictionary2, localContext)) return context.Merge(localContext); - Uri uri1 = (Uri)object1; - Uri uri2 = (Uri)object2; + if (dictionary1.Count != dictionary2.Count) + localContext.Diffs.Add($"(dictionary1.Count != dictionary2.Count: {dictionary1.Count}, {dictionary2.Count})"); - if (!string.Equals(uri1.OriginalString, uri2.OriginalString, context.StringComparison)) + int numMatched = 0; + foreach (string key in dictionary1.Keys) { - localContext.Diffs.Add($"'{uri1.OriginalString}'"); - localContext.Diffs.Add($"!="); - localContext.Diffs.Add($"'{uri2.OriginalString}'"); - localContext.Diffs.Add($"'{context.StringComparison}'"); + if (dictionary2.ContainsKey(key)) + { + var obj1 = dictionary1[key]; + var obj2 = dictionary2[key]; + if (obj1.GetType().BaseType == typeof(System.ValueType)) + { + if (!obj1.Equals(obj2)) + localContext.Diffs.Add(BuildStringDiff(key, obj1, obj2)); + } + else + { + if (AreEqual(obj1, obj2, context)) + numMatched++; + } + } + else + { + localContext.Diffs.Add("dictionary1[key] ! found in dictionary2. key: " + key); + } } return context.Merge(localContext); } - public static bool AreKeyInfosEqual(KeyInfo keyInfo1, KeyInfo keyInfo2, CompareContext context) - { - var localContext = new CompareContext(context); - if (ContinueCheckingEquality(keyInfo1, keyInfo2, context)) - CompareAllPublicProperties(keyInfo1, keyInfo2, localContext); - - return context.Merge(localContext); - } - - public static bool AreSignedInfosEqual(SignedInfo signedInfo1, SignedInfo signedInfo2, CompareContext context) + public static bool AreStringEnumsEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context); - if (ContinueCheckingEquality(signedInfo1, signedInfo2, localContext)) - CompareAllPublicProperties(signedInfo1, signedInfo2, localContext); - - return context.Merge(localContext); + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreStringsEqual); } public static bool AreTimeSpansEqual(object object1, object object2, CompareContext context) @@ -955,6 +1045,13 @@ public static bool AreTimeSpansEqual(object object1, object object2, CompareCont return context.Merge(localContext); } + private static bool AreValueCollectionsEqual(Object object1, Object object2, CompareContext context) + { + Dictionary.ValueCollection vc1 = (Dictionary.ValueCollection)object1; + Dictionary.ValueCollection vc2 = (Dictionary.ValueCollection)object2; + return true; + } + public static bool AreWsFederationConfigurationsEqual(WsFederationConfiguration configuration1, WsFederationConfiguration configuration2, CompareContext context) { var localContext = new CompareContext(context); @@ -1002,119 +1099,39 @@ public static bool AreX509Certificate2Equal(object object1, object object2, Comp return context.Merge(localContext); } - public static bool AreConfigurationValidationResultEqual(ConfigurationValidationResult result1, ConfigurationValidationResult result2, CompareContext context) - { - var localContext = new CompareContext(context); - if (ContinueCheckingEquality(result1, result2, localContext)) - CompareAllPublicProperties(result1, result2, localContext); - - return context.Merge(localContext); - } - - public static string BuildStringDiff(string label, object str1, object str2) + public static bool AreX509DataEnumsEqual(object object1, object object2, CompareContext context) { - return (label ?? "label") + ": '" + GetString(str1) + "', '" + GetString(str2) + "'"; + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); } - public static bool AreJsonElementsEqual(object obj1, object obj2, CompareContext context) + public static bool AreUrisEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context) { IgnoreType = true }; - if (!ContinueCheckingEquality(obj1, obj2, localContext)) - return context.Merge(localContext); - - JsonElement jsonElement1 = (JsonElement)obj1; - JsonElement jsonElement2 = (JsonElement)obj2; - - if (jsonElement1.ValueKind != jsonElement2.ValueKind) - { - localContext.Diffs.Add($"jsonElement1.ValueKind != jsonElement2.ValueKind. '{jsonElement1.ValueKind}' != '{jsonElement2.ValueKind}'."); + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, localContext)) return context.Merge(localContext); - } - string str1 = jsonElement1.GetRawText(); - string str2 = jsonElement2.GetRawText(); + Uri uri1 = (Uri)object1; + Uri uri2 = (Uri)object2; - if (str1 != str2) + if (!string.Equals(uri1.OriginalString, uri2.OriginalString, context.StringComparison)) { - localContext.Diffs.Add($"jsonElement1.GetRawText() != jsonElement2.GetRawText(). '{jsonElement1.GetRawText()}' != '{jsonElement2.GetRawText()}'."); - return context.Merge(localContext); + localContext.Diffs.Add($"'{uri1.OriginalString}'"); + localContext.Diffs.Add($"!="); + localContext.Diffs.Add($"'{uri2.OriginalString}'"); + localContext.Diffs.Add($"'{context.StringComparison}'"); } return context.Merge(localContext); } - public static bool AreJsonWebKeyNet8Equal(object obj1, object obj2, CompareContext context) + public static bool AreUriEnumsEqual(object object1, object object2, CompareContext context) { - var localContext = new CompareContext(context) { IgnoreType = true }; - if (!ContinueCheckingEquality(obj1, obj2, localContext)) - return context.Merge(localContext); - - Type jsonWebKeyType = obj2.GetType(); - var jsonWebKeyPopertyInfos = jsonWebKeyType.GetProperties(BindingFlags.Public | BindingFlags.Instance); - - Type jsonWebKeyNet8Type = obj1.GetType(); - var jsonWebKeyNet8PropertyInfos = jsonWebKeyNet8Type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - - foreach (PropertyInfo jsonWebKeyPropertyInfo in jsonWebKeyPopertyInfos) - { - bool skipProperty = false; - if (context.PropertiesToIgnoreWhenComparing != null && context.PropertiesToIgnoreWhenComparing.TryGetValue(jsonWebKeyNet8Type, out List propertiesToIgnore)) - { - foreach (var val in propertiesToIgnore) - if (string.Equals(val, jsonWebKeyPropertyInfo.Name, StringComparison.OrdinalIgnoreCase)) - { - skipProperty = true; - break; - } - } - - if (skipProperty) - continue; - - PropertyInfo propertyInfoFound = null; - foreach (PropertyInfo jsonWebKeyNet8PropertyInfo in jsonWebKeyNet8PropertyInfos) - { - if (jsonWebKeyNet8PropertyInfo.Name == jsonWebKeyPropertyInfo.Name) - propertyInfoFound = jsonWebKeyNet8PropertyInfo; - } - - if (propertyInfoFound == null) - { - localContext.AddDiff($"jsonWebKeyNet8 property not found: {jsonWebKeyPropertyInfo.Name}"); - continue; - } - - if (jsonWebKeyPropertyInfo.GetMethod != null) - { - var propertyContext = new CompareContext(context); - - object val1 = jsonWebKeyPropertyInfo.GetValue(obj2, null); - object val2 = propertyInfoFound.GetValue(obj1, null); - if ((val1 == null) && (val2 == null)) - continue; - - if ((val1 == null) || (val2 == null)) - { - propertyContext.Diffs.Add($"{jsonWebKeyPropertyInfo.Name}:"); - propertyContext.Diffs.Add(BuildStringDiff(propertyInfoFound.Name, val1, val2)); - } - else if (val1.GetType().BaseType == typeof(System.ValueType) && !_equalityDict.Keys.Contains(val1.GetType().ToString())) - { - if (!val1.Equals(val2)) - { - propertyContext.Diffs.Add($"{jsonWebKeyPropertyInfo.Name}:"); - propertyContext.Diffs.Add(BuildStringDiff(propertyInfoFound.Name, val1, val2)); - } - } - else - { - AreEqual(val1, val2, propertyContext); - localContext.Merge($"{propertyInfoFound.Name}:", propertyContext); - } - } - } + return AreEnumsEqual(object1 as IEnumerable, object2 as IEnumerable, context, AreEqual); + } - return context.Merge(localContext); + public static string BuildStringDiff(string label, object str1, object str2) + { + return (label ?? "label") + ": '" + GetString(str1) + "', '" + GetString(str2) + "'"; } public static bool CompareAllPublicProperties(object obj1, object obj2, CompareContext context) @@ -1166,14 +1183,6 @@ public static bool CompareAllPublicProperties(object obj1, object obj2, CompareC localContext.Diffs.Add($"{propertyInfo.Name}:"); localContext.Diffs.Add(BuildStringDiff(propertyInfo.Name, val1, val2)); } - else if (type == typeof(ClaimsIdentity) && String.Equals(propertyInfo.Name, "AuthenticationType") && String.Equals(Convert.ToString(val1), "AuthenticationTypes.Federation") && String.Equals(Convert.ToString(val2), "AuthenticationTypes.Federation")) - continue; - else if (type == typeof(Claim) && String.Equals(propertyInfo.Name, "Value") && (String.Equals((val1 as string), "urn:oasis:names:tc:SAML:1.0:am:password") || String.Equals((val2 as string), "urn:oasis:names:tc:SAML:1.0:am:password"))) - continue; - else if (type == typeof(ClaimsPrincipal) && String.Equals(propertyInfo.Name, "Claims") && (val1 as IEnumerable).Count() == 0) - continue; - else if (type == typeof(ClaimsIdentity) && String.Equals(propertyInfo.Name, "Claims") && (val1 as IEnumerable).Count() == 0) - continue; else if (val1.GetType().BaseType == typeof(System.ValueType) && !_equalityDict.Keys.Contains(val1.GetType().ToString())) { if (!val1.Equals(val2)) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ECDsaAdapterTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/ECDsaAdapterTests.cs index 55d0a60635..2f81a40f6a 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/ECDsaAdapterTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/ECDsaAdapterTests.cs @@ -3,6 +3,7 @@ using System; using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens.Json.Tests; using Xunit; using KEY = Microsoft.IdentityModel.TestUtils.KeyingMaterial; @@ -161,19 +162,6 @@ public static TheoryData CreateECDsaFromJsonWebKeyTheoryDa }, }; } - - public class JsonWebKeyTheoryData : TheoryDataBase - { - public string Crv { get; set; } - - public string D { get; set; } - - public bool UsePrivateKey { get; set; } - - public string X { get; set; } - - public string Y { get; set; } - } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs index b9aab73259..63c79da8b6 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs @@ -575,9 +575,9 @@ public void CompareStrings() var string2 = "goodbye"; IdentityComparer.AreEqual(string1, string2, context); - Assert.True(context.Diffs.Count(s => s == $"'{string1}'") == 1); - Assert.True(context.Diffs.Count(s => s == "!=") == 1); - Assert.True(context.Diffs.Count(s => s == $"'{string2}'") == 1); + Assert.True(context.Diffs.Count(s => s == "str1 != str2, StringComparison: 'Ordinal'") == 1); + Assert.True(context.Diffs[1] == string1); + Assert.True(context.Diffs[2] == string2); } [Fact] diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonTestClassSerializer.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonTestClassSerializer.cs index dfa3d7fc56..4c4cda0c66 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonTestClassSerializer.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonTestClassSerializer.cs @@ -53,7 +53,7 @@ public static JsonTestClass Deserialize(ref Utf8JsonReader reader, IDictionary objects = new List(); - if (JsonSerializerPrimitives.ReadObjects(ref reader, objects, "ListObject", _className, true) == null) + if (JsonSerializerPrimitives.ReadObjects(ref reader, objects, "ListObject", _className) == null) { throw LogHelper.LogExceptionMessage( new ArgumentNullException( @@ -107,7 +107,7 @@ public static JsonTestClass Deserialize(ref Utf8JsonReader reader, IDictionary strings = new List(); - if (JsonSerializerPrimitives.ReadStrings(ref reader, strings, "ListString", _className, true) == null) + if (JsonSerializerPrimitives.ReadStrings(ref reader, strings, "ListString", _className) == null) { throw LogHelper.LogExceptionMessage( new ArgumentNullException( @@ -126,21 +126,20 @@ public static JsonTestClass Deserialize(ref Utf8JsonReader reader, IDictionary + { + "oth1", + "oth2" + }; + + SetAdditionalData6x(jsonWebKey.AdditionalData); + + return jsonWebKey; + } + + public static JsonWebKeySet FullyPopulatedJsonWebKeySet() + { + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); + jsonWebKeySet.Keys.Add(FullyPopulatedJsonWebKey()); + SetAdditionalData(jsonWebKeySet.AdditionalData); + + return jsonWebKeySet; + } + + public static JsonWebKeySet6x FullyPopulatedJsonWebKeySet6x() + { + JsonWebKeySet6x jsonWebKeySet = new JsonWebKeySet6x(); + jsonWebKeySet.Keys.Add(FullyPopulatedJsonWebKey6x()); + SetAdditionalData6x(jsonWebKeySet.AdditionalData); + + return jsonWebKeySet; + } + + public static void SetAdditionalData(IDictionary dictionary, string key = null, object obj = null) + { + SetAdditionalDataNumbers(dictionary); + SetAdditionalDataValues(dictionary); + dictionary["Object"] = CreateJsonElement(_objectData); + dictionary["Array"] = CreateJsonElement(_arrayData); + if (key != null) + dictionary[key] = obj; + } + + public static void SetAdditionalData6x(IDictionary dictionary, string key = null, object obj = null) + { + SetAdditionalDataNumbers(dictionary); + SetAdditionalDataValues(dictionary); + dictionary["Object"] = JObject.Parse(_objectData); + dictionary["Array"] = JArray.Parse(_arrayData); + if (key != null) + dictionary[key] = obj; + } + + public static void SetAdditionalDataNumbers(IDictionary dictionary) + { + dictionary["int"] = (int)1; + dictionary["long"] = (long)1234567890123456; + } + + public static void SetAdditionalDataValues(IDictionary dictionary) + { + dictionary["string"] = "string"; + dictionary["false"] = false; + dictionary["true"] = true; + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKey6x.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKey6x.cs new file mode 100644 index 0000000000..909f0973fb --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKey6x.cs @@ -0,0 +1,482 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using Microsoft.IdentityModel.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + /// + /// Represents a JSON Web Key as defined in https://datatracker.ietf.org/doc/html/rfc7517. + /// This is the original JsonWebKey in the 6x branch. + /// Used for ensuring backcompat. + /// + [JsonObject] + public class JsonWebKey6x : SecurityKey6x + { + private string _kid; + private const string _className = "Microsoft.IdentityModel.Tokens.JsonWebKey6x"; + + /// + /// Returns a new instance of . + /// + /// A string that contains JSON Web Key parameters in JSON format. + /// + /// If 'json' is null or empty. + /// If 'json' fails to deserialize. + static public JsonWebKey6x Create(string json) + { + if (string.IsNullOrEmpty(json)) + throw LogHelper.LogArgumentNullException(nameof(json)); + + return new JsonWebKey6x(json); + } + + /// + /// Initializes an new instance of . + /// + public JsonWebKey6x() + { + } + + /// + /// Initializes an new instance of from a json string. + /// + /// A string that contains JSON Web Key parameters in JSON format. + /// If 'json' is null or empty. + /// If 'json' fails to deserialize. + public JsonWebKey6x(string json) + { + if (string.IsNullOrEmpty(json)) + throw LogHelper.LogArgumentNullException(nameof(json)); + + try + { + LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(_className)); + JsonConvert.PopulateObject(json, this); + } + catch (Exception ex) + { + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(_className)), ex)); + } + } + + /// + /// If this was converted to or from a SecurityKey, this field will be set. + /// + [JsonIgnore] + internal SecurityKey ConvertedSecurityKey { get; set; } + + /// + /// If this was failed converted to a SecurityKey, this field will be set. + /// + [JsonIgnore] + internal string ConvertKeyInfo { get; set; } + + /// + /// When deserializing from JSON any properties that are not defined will be placed here. + /// + [JsonExtensionData] + public virtual IDictionary AdditionalData { get; } = new Dictionary(); + + /// + /// Gets or sets the 'alg' (KeyType).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Alg, Required = Required.Default)] + public string Alg { get; set; } + + /// + /// Gets or sets the 'crv' (ECC - Curve).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Crv, Required = Required.Default)] + public string Crv { get; set; } + + /// + /// Gets or sets the 'd' (ECC - Private Key OR RSA - Private Exponent).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.D, Required = Required.Default)] + public string D { get; set; } + + /// + /// Gets or sets the 'dp' (RSA - First Factor CRT Exponent).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.DP, Required = Required.Default)] + public string DP { get; set; } + + /// + /// Gets or sets the 'dq' (RSA - Second Factor CRT Exponent).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.DQ, Required = Required.Default)] + public string DQ { get; set; } + + /// + /// Gets or sets the 'e' (RSA - Exponent).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.E, Required = Required.Default)] + public string E { get; set; } + + /// + /// Gets or sets the 'k' (Symmetric - Key Value).. + /// + /// Base64urlEncoding + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.K, Required = Required.Default)] + public string K { get; set; } + + /// + /// Gets the key id of this . + /// + [JsonIgnore] + public override string KeyId + { + get { return _kid; } + set { _kid = value; } + } + + /// + /// Gets the 'key_ops' (Key Operations).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.KeyOps, Required = Required.Default)] + public IList KeyOps { get; private set; } = new List(); + + /// + /// Gets or sets the 'kid' (Key ID).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Kid, Required = Required.Default)] + public string Kid + { + get { return _kid; } + set { _kid = value; } + } + + /// + /// Gets or sets the 'kty' (Key Type).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Kty, Required = Required.Default)] + public string Kty { get; set; } + + /// + /// Gets or sets the 'n' (RSA - Modulus).. + /// + /// Value is formated as: Base64urlEncoding + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.N, Required = Required.Default)] + public string N { get; set; } + + /// + /// Gets or sets the 'oth' (RSA - Other Primes Info).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Oth, Required = Required.Default)] + public IList Oth { get; set; } + + /// + /// Gets or sets the 'p' (RSA - First Prime Factor).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.P, Required = Required.Default)] + public string P { get; set; } + + /// + /// Gets or sets the 'q' (RSA - Second Prime Factor).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Q, Required = Required.Default)] + public string Q { get; set; } + + /// + /// Gets or sets the 'qi' (RSA - First CRT Coefficient).. + /// + /// Value is formated as: Base64urlUInt + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.QI, Required = Required.Default)] + public string QI { get; set; } + + /// + /// Gets or sets the 'use' (Public Key Use).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Use, Required = Required.Default)] + public string Use { get; set; } + + /// + /// Gets or sets the 'x' (ECC - X Coordinate).. + /// + /// Value is formated as: Base64urlEncoding + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X, Required = Required.Default)] + public string X { get; set; } + + /// + /// Gets the 'x5c' collection (X.509 Certificate Chain).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5c, Required = Required.Default)] + public IList X5c { get; } = new List(); + + /// + /// Gets or sets the 'x5t' (X.509 Certificate SHA-1 thumbprint).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5t, Required = Required.Default)] + public string X5t { get; set; } + + /// + /// Gets or sets the 'x5t#S256' (X.509 Certificate SHA-1 thumbprint).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5tS256, Required = Required.Default)] + public string X5tS256 { get; set; } + + /// + /// Gets or sets the 'x5u' (X.509 URL).. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.X5u, Required = Required.Default)] + public string X5u { get; set; } + + /// + /// Gets or sets the 'y' (ECC - Y Coordinate).. + /// + /// Value is formated as: Base64urlEncoding + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeyParameterNames.Y, Required = Required.Default)] + public string Y { get; set; } + + /// + /// Gets the key size of . + /// + [JsonIgnore] + public override int KeySize + { + get + { + if (Kty == JsonWebAlgorithmsKeyTypes.RSA && !string.IsNullOrEmpty(N)) + return Base64UrlEncoder.DecodeBytes(N).Length * 8; + else if (Kty == JsonWebAlgorithmsKeyTypes.EllipticCurve && !string.IsNullOrEmpty(X)) + return Base64UrlEncoder.DecodeBytes(X).Length * 8; + else if (Kty == JsonWebAlgorithmsKeyTypes.Octet && !string.IsNullOrEmpty(K)) + return Base64UrlEncoder.DecodeBytes(K).Length * 8; + else + return 0; + } + } + + /// + /// Gets a bool indicating if a private key exists. + /// + /// true if it has a private key; otherwise, false. + [JsonIgnore] + public bool HasPrivateKey + { + get + { + if (Kty == JsonWebAlgorithmsKeyTypes.RSA) + return D != null && DP != null && DQ != null && P != null && Q != null && QI != null; + else if (Kty == JsonWebAlgorithmsKeyTypes.EllipticCurve) + return D != null; + else + return false; + } + } + + /// + /// Gets a bool that determines if the 'AdditionalData' property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'key_ops' (Key Operations) is not empty; otherwise, false. + public bool ShouldSerializeAdditionalData() + { + return AdditionalData.Count > 0; + } + + /// + /// Gets a bool that determines if the 'key_ops' (Key Operations) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'key_ops' (Key Operations) is not empty; otherwise, false. + public bool ShouldSerializeKeyOps() + { + return KeyOps.Count > 0; + } + + /// + /// Gets a bool that determines if the 'x5c' collection (X.509 Certificate Chain) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'x5c' collection (X.509 Certificate Chain) is not empty; otherwise, false. + public bool ShouldSerializeX5c() + { + return X5c.Count > 0; + } + + internal RSAParameters CreateRsaParameters() + { + if (string.IsNullOrEmpty(N)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("Modulus")))); + + if (string.IsNullOrEmpty(E)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10700, LogHelper.MarkAsNonPII(_className), LogHelper.MarkAsNonPII("Exponent")))); + + return new RSAParameters + { + Modulus = Base64UrlEncoder.DecodeBytes(N), + Exponent = Base64UrlEncoder.DecodeBytes(E), + D = string.IsNullOrEmpty(D) ? null : Base64UrlEncoder.DecodeBytes(D), + P = string.IsNullOrEmpty(P) ? null : Base64UrlEncoder.DecodeBytes(P), + Q = string.IsNullOrEmpty(Q) ? null : Base64UrlEncoder.DecodeBytes(Q), + DP = string.IsNullOrEmpty(DP) ? null : Base64UrlEncoder.DecodeBytes(DP), + DQ = string.IsNullOrEmpty(DQ) ? null : Base64UrlEncoder.DecodeBytes(DQ), + InverseQ = string.IsNullOrEmpty(QI) ? null : Base64UrlEncoder.DecodeBytes(QI) + }; + } + + /// + /// Determines whether the can compute a JWK thumbprint. + /// + /// true if JWK thumbprint can be computed; otherwise, false. + /// https://datatracker.ietf.org/doc/html/rfc7638 + public override bool CanComputeJwkThumbprint() + { + if (string.IsNullOrEmpty(Kty)) + return false; + + if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) + return CanComputeECThumbprint(); + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.RSA)) + return CanComputeRsaThumbprint(); + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.Octet)) + return CanComputeOctThumbprint(); + else + return false; + } + + /// + /// Computes a sha256 hash over the . + /// + /// A JWK thumbprint. + /// https://datatracker.ietf.org/doc/html/rfc7638 + public override byte[] ComputeJwkThumbprint() + { + if (string.IsNullOrEmpty(Kty)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(Kty))))); + + if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) + return ComputeECThumbprint(); + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.RSA)) + return ComputeRsaThumbprint(); + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.Octet)) + return ComputeOctThumbprint(); + else + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10706, LogHelper.MarkAsNonPII(nameof(Kty)), LogHelper.MarkAsNonPII(string.Join(", ", JsonWebAlgorithmsKeyTypes.EllipticCurve, JsonWebAlgorithmsKeyTypes.RSA, JsonWebAlgorithmsKeyTypes.Octet)), LogHelper.MarkAsNonPII(nameof(Kty))))); + } + + private bool CanComputeOctThumbprint() + { + return !string.IsNullOrEmpty(K); + } + + private byte[] ComputeOctThumbprint() + { + if (string.IsNullOrEmpty(K)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(K))))); + + var canonicalJwk = $@"{{""{JsonWebKeyParameterNames.K}"":""{K}"",""{JsonWebKeyParameterNames.Kty}"":""{Kty}""}}"; + return Utility.GenerateSha256Hash(canonicalJwk); + } + + private bool CanComputeRsaThumbprint() + { + return !(string.IsNullOrEmpty(E) || string.IsNullOrEmpty(N)); + } + + private byte[] ComputeRsaThumbprint() + { + if (string.IsNullOrEmpty(E)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(E))))); + + if (string.IsNullOrEmpty(N)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(N))))); + + var canonicalJwk = $@"{{""{JsonWebKeyParameterNames.E}"":""{E}"",""{JsonWebKeyParameterNames.Kty}"":""{Kty}"",""{JsonWebKeyParameterNames.N}"":""{N}""}}"; + return Utility.GenerateSha256Hash(canonicalJwk); + } + + private bool CanComputeECThumbprint() + { + return !(string.IsNullOrEmpty(Crv) || string.IsNullOrEmpty(X) || string.IsNullOrEmpty(Y)); + } + + private byte[] ComputeECThumbprint() + { + if (string.IsNullOrEmpty(Crv)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(Crv))))); + + if (string.IsNullOrEmpty(X)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(X))))); + + if (string.IsNullOrEmpty(Y)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10705, LogHelper.MarkAsNonPII(nameof(Y))))); + + var canonicalJwk = $@"{{""{JsonWebKeyParameterNames.Crv}"":""{Crv}"",""{JsonWebKeyParameterNames.Kty}"":""{Kty}"",""{JsonWebKeyParameterNames.X}"":""{X}"",""{JsonWebKeyParameterNames.Y}"":""{Y}""}}"; + return Utility.GenerateSha256Hash(canonicalJwk); + } + + /// + /// Creates a JsonWebKey6x representation of an asymmetric public key. + /// + /// JsonWebKey6x representation of an asymmetric public key. + /// https://datatracker.ietf.org/doc/html/rfc7800#section-3.2 + internal string RepresentAsAsymmetricPublicJwk() + { + JObject jwk = new JObject(); + + if (!string.IsNullOrEmpty(Kid)) + jwk.Add(JsonWebKeyParameterNames.Kid, Kid); + + if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) + PopulateWithPublicEcParams(jwk); + else if (string.Equals(Kty, JsonWebAlgorithmsKeyTypes.RSA)) + PopulateWithPublicRsaParams(jwk); + else + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10707, LogHelper.MarkAsNonPII(nameof(Kty)), LogHelper.MarkAsNonPII(string.Join(", ", JsonWebAlgorithmsKeyTypes.EllipticCurve, JsonWebAlgorithmsKeyTypes.RSA)), LogHelper.MarkAsNonPII(nameof(Kty))))); + + return jwk.ToString(Formatting.None); + } + + private void PopulateWithPublicEcParams(JObject jwk) + { + if (string.IsNullOrEmpty(Crv)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Crv))))); + + if (string.IsNullOrEmpty(X)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(X))))); + + if (string.IsNullOrEmpty(Y)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10708, LogHelper.MarkAsNonPII(nameof(Y))))); + + jwk.Add(JsonWebKeyParameterNames.Crv, Crv); + jwk.Add(JsonWebKeyParameterNames.Kty, Kty); + jwk.Add(JsonWebKeyParameterNames.X, X); + jwk.Add(JsonWebKeyParameterNames.Y, Y); + } + + private void PopulateWithPublicRsaParams(JObject jwk) + { + if (string.IsNullOrEmpty(E)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(E))))); + + if (string.IsNullOrEmpty(N)) + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10709, LogHelper.MarkAsNonPII(nameof(N))))); + + jwk.Add(JsonWebKeyParameterNames.E, E); + jwk.Add(JsonWebKeyParameterNames.Kty, Kty); + jwk.Add(JsonWebKeyParameterNames.N, N); + } + + /// + /// Returns the formatted string: GetType(), Use: 'value', Kid: 'value', Kty: 'value', InternalId: 'value'. + /// + /// string + public override string ToString() + { + return $"{GetType()}, Use: '{Use}', Kid: '{Kid}', Kty: '{Kty}', InternalId: '{InternalId}'."; + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySerializationTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySerializationTests.cs new file mode 100644 index 0000000000..7510125946 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySerializationTests.cs @@ -0,0 +1,442 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens.Tests; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + /// + /// These set of tests ensure that 7x and 6x serialization of JsonWebKey. + /// 6x uses Newtonsoft, 7x uses System.Text.Json utf8Readers and utf8Writers. + /// Differences will be discovered here and used for release notes. + /// + public class JsonWebKeySerializationTests + { + /// + /// This test is designed to ensure that JsonDeserialize and Utf8Reader are consistent w.r.t. exceptions. + /// + /// + [Theory, MemberData(nameof(DeserializeTheoryData))] + public void DeserializeExceptions(JsonSerializerTheoryData theoryData) + { + var context = new CompareContext(theoryData); + try + { + JsonWebKeySerializer.Read(theoryData.Json); + theoryData.JsonReaderExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.JsonReaderExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData DeserializeTheoryData + { + get + { + var theoryData = new TheoryData(); + AddSingleStringTestCases(theoryData, "Alg", JsonWebKeyParameterNames.Alg); + AddArrayOfStringsTestCases(theoryData, "KeyOps", JsonWebKeyParameterNames.KeyOps); + return theoryData; + } + } + + /// + /// Adds tests cases for a type with the property name of the class and the json property name. + /// + /// place to add the test case. + /// the property name on the class. + /// the property name in the json mapping to the class + private static void AddSingleStringTestCases(TheoryData theoryData, string instancePropertyName, string jsonPropertyName) + { + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_DuplicateProperties") + { + Json = $@"{{""{jsonPropertyName}"":""string"", ""{jsonPropertyName}"":""string""}}", + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_SingleString") + { + Json = $@"{{""{jsonPropertyName}"":""string""}}", + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_ArrayString") + { + Json = $@"{{""{jsonPropertyName}"":[""string1"", ""string2""]}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Array") + { + Json = $@"{{""{jsonPropertyName}"":[""value"", 1]}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_true") + { + Json = $@"{{""{jsonPropertyName}"": true}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_false") + { + Json = $@"{{""{jsonPropertyName}"": false}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Object") + { + Json = $@"{{""{jsonPropertyName}"":{{""property"": ""false""}}}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Null") + { + Json = $@"{{""{jsonPropertyName}"":null}}", + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Number") + { + Json = $@"{{""d"":""string"",""d"":""string"",""{jsonPropertyName}"":1}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + } + + /// + /// Adds tests cases for a type with the property name of the class and the json property name. + /// + /// place to add the test case. + /// the property name on the class. + /// the property name in the json mapping to the class + private static void AddArrayOfStringsTestCases(TheoryData theoryData, string instancePropertyName, string jsonPropertyName) + { + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_SingleString") + { + Json = $@"{{""{jsonPropertyName}"":""string""}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11022:"), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted") + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_ArrayString") + { + Json = $@"{{""{jsonPropertyName}"":[""string1"", ""string2""]}}", + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Array") + { + Json = $@"{{""{jsonPropertyName}"":[""value"", 1]}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11020: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted", typeof(InvalidOperationException)) + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_true") + { + Json = $@"{{""{jsonPropertyName}"": true}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11022: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted") + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_false") + { + Json = $@"{{""{jsonPropertyName}"": false}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11022: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted") + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Object") + { + Json = $@"{{""{jsonPropertyName}"":{{""property"": ""false""}}}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11022: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted") + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Null") + { + Json = $@"{{""{jsonPropertyName}"":null}}", + JsonReaderExpectedException = new ExpectedException(typeof(ArgumentNullException), JsonWebKeyParameterNames.KeyOps, typeof(System.Text.Json.JsonException)), + JsonSerializerExpectedException = new ExpectedException(typeof(ArgumentNullException), "IDX10000: ") + }); + + theoryData.Add(new JsonSerializerTheoryData($"{instancePropertyName}_Number") + { + Json = $@"{{""d"":""string"",""d"":""string"",""{jsonPropertyName}"":1}}", + JsonReaderExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "IDX11022: "), + JsonSerializerExpectedException = new ExpectedException(typeof(System.Text.Json.JsonException), "The JSON value could not be converted") + }); + } + + /// + /// Compares and finds differences between our internal Newtonsoft.Json and System.Text.Json + /// comparing the results JsonSerializer.Deserialize and Utf8JsonReader. + /// + /// + [Theory, MemberData(nameof(DeserializeDataSet))] + public void Deserialize(JsonWebKeyTheoryData theoryData) + { + CompareContext context = new CompareContext(theoryData); + + JsonWebKey6x jsonWebKey6x = null; + JsonWebKey jsonWebKeyDeserialize = null; + JsonWebKey jsonWebKeyUtf8Reader = null; + + try + { + jsonWebKey6x = new JsonWebKey6x(theoryData.Json); + theoryData.ExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + try + { + jsonWebKeyDeserialize = System.Text.Json.JsonSerializer.Deserialize(theoryData.Json); + theoryData.JsonSerializerExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.JsonSerializerExpectedException.ProcessException(ex, context); + } + + try + { + jsonWebKeyUtf8Reader = new JsonWebKey(theoryData.Json); + theoryData.JsonReaderExpectedException.ProcessNoException(context); + } + catch (Exception ex) + { + theoryData.JsonReaderExpectedException.ProcessException(ex, context); + } + + // when comparing JsonWebKey (JsonSerializer.Deserialize) and (Newtonsoft.Json) ignore the AdditionalData, Oth, X5c, KeyOps properties are they have no getter. + // We will need to adjust for a 8.0 + CompareContext localContext = new CompareContext(theoryData); + localContext.PropertiesToIgnoreWhenComparing.Add(typeof(JsonWebKey), new List { "AdditionalData", "Oth", "X5c", "KeyOps" }); + if (!IdentityComparer.AreEqual(jsonWebKeyDeserialize, jsonWebKey6x, localContext)) + { + localContext.Diffs.Add("jsonWebKeyDeserialize != jsonWebKey6x"); + localContext.Diffs.Add("========================================="); + } + + context.Merge(localContext); + + // when comparing JsonWebKey (JsonSerializer.Deserialize) and (utf8Reader) ignore the AdditionalData, Oth, X5c, KeyOps properties are they have no getter. + // We will need to adjust for a 8.0 + localContext.Diffs.Clear(); + localContext.PropertiesToIgnoreWhenComparing.Clear(); + + //RELNOTE: JsonSerializer.Deserialize does not handle mixed case + // DataSets.JsonWebKeyMixedCaseString + if (theoryData.TestId == "JsonWebKeyMixedCase") + localContext.PropertiesToIgnoreWhenComparing.Add(typeof(JsonWebKey), new List { "AdditionalData", "Oth", "X5c", "KeyOps", "Alg", "E", "X5tS256" }); + else + localContext.PropertiesToIgnoreWhenComparing.Add(typeof(JsonWebKey), new List { "AdditionalData", "Oth", "X5c", "KeyOps" }); + + if (!IdentityComparer.AreEqual(jsonWebKeyDeserialize, jsonWebKeyUtf8Reader, localContext)) + { + localContext.Diffs.Add("jsonWebKeyDeserialize != jsonWebKeyUtf8"); + localContext.Diffs.Add("========================================="); + } + + context.Merge(localContext); + + // when comparing JsonWebKey (NewtonSoft) and (utf8Reader) ignore the AdditionalData + // (Newtonsoft.Json) sets an Newtonsoft object, utf8Reader sets JsonElement. + localContext.Diffs.Clear(); + localContext.PropertiesToIgnoreWhenComparing.Clear(); + localContext.AddDictionaryKeysToIgnoreWhenComparing("Object", "Array", "int"); + if (!IdentityComparer.AreEqual(jsonWebKeyUtf8Reader, jsonWebKey6x, localContext)) + { + localContext.Diffs.Add("jsonWebKeyUtf8Reader != jsonWebKey6x"); + localContext.Diffs.Add("========================================="); + } + + context.Merge(localContext); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData DeserializeDataSet + { + get + { + var theoryData = new TheoryData(); + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKey") + { + JsonWebKey = DataSets.JsonWebKey1, + Json = DataSets.JsonWebKeyString + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyMixedCase") + { + JsonWebKey = DataSets.JsonWebKey1, + Json = DataSets.JsonWebKeyMixedCaseString + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyES256") + { + JsonWebKey = DataSets.JsonWebKeyES256, + Json = DataSets.JsonWebKeyES256String + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyES384") + { + JsonWebKey = DataSets.JsonWebKeyES384, + Json = DataSets.JsonWebKeyES384String + }); + + JsonWebKey jsonWebKey = JsonUtilities.FullyPopulatedJsonWebKey(); + string json = System.Text.Json.JsonSerializer.Serialize(jsonWebKey); + theoryData.Add(new JsonWebKeyTheoryData("FullyPopulated") + { + JsonWebKey = jsonWebKey, + Json = json + }); + + // System.Text.Json throws a JsonException with an inner of JsonReaderException that is internal. + // We would have to use reflection to compare. + theoryData.Add(new JsonWebKeyTheoryData("BadJson") + { + ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX10805: Error deserializing json: ", typeof(JsonReaderException)), + Json = "{adsd}", + JsonReaderExpectedException = new ExpectedException(typeExpected: typeof(ArgumentException), substringExpected: "IDX10805: Error deserializing json:", ignoreInnerException: true), + JsonSerializerExpectedException = new ExpectedException(typeExpected: typeof(System.Text.Json.JsonException), substringExpected: "'a' is an invalid start of a", ignoreInnerException: true) + }); + + return theoryData; + } + } + + /// + /// This test is to ensure that a JsonWebKey from 6x == 7x. + /// This is different that Deserialize as we are roundtripping objects in AdditionalData. + /// + /// + [Theory, MemberData(nameof(RoundTripDataSet))] + public void RoundTrip(JsonWebKeyTheoryData theoryData) + { + var context = new CompareContext(theoryData); + try + { + string json6x = JsonConvert.SerializeObject(theoryData.JsonWebKey6x, Formatting.None); + string jsonSerialize = System.Text.Json.JsonSerializer.Serialize(theoryData.JsonWebKey); + string jsonUtf8Writer = JsonWebKeySerializer.Write(theoryData.JsonWebKey); + + JsonWebKey6x jsonWebKey6x = JsonConvert.DeserializeObject(json6x); + JsonWebKey jsonWebKeyDeserialize = System.Text.Json.JsonSerializer.Deserialize(jsonUtf8Writer); + JsonWebKey jsonWebKeyUtf8Reader = new JsonWebKey(jsonUtf8Writer); + + // ensure that our utf8writer and newtonsoft are the same + if (!IdentityComparer.AreEqual(jsonUtf8Writer, json6x, context)) + { + context.Diffs.Add("jsonUtf8Writer != json6x"); + context.Diffs.Add("========================================="); + } + + // RELNOTE: ensure that the output from our utf8writer is consummable by JsonSerializer.Deserialize since our collections are not settable, ignore the AdditionalData, KeyOps, Oth, X5c properties are they have no getter. + // We will need to adjust for a 8.0 target + // use a new CompareContext so that Properties to ignore are just applied to this compare + CompareContext localContext = new CompareContext(theoryData); + localContext.PropertiesToIgnoreWhenComparing.Add(typeof(JsonWebKey), new List { "AdditionalData", "KeyOps", "Oth", "X5c" }); + if (!IdentityComparer.AreEqual(jsonWebKeyDeserialize, theoryData.JsonWebKey, localContext)) + { + localContext.Diffs.Add("jsonWebKeyDeserialize != theoryData.JsonWebKey"); + localContext.Diffs.Add("========================================="); + } + + context.Merge(localContext); + + // compare our utf8Reader with expected value + if (!IdentityComparer.AreEqual(jsonWebKeyUtf8Reader, theoryData.JsonWebKey, context)) + { + context.Diffs.Add("jsonWebKeyUtf8Reader != theoryData.JsonWebKey"); + context.Diffs.Add("========================================="); + } + + // RELNOTE: we can give users a sample showing how to deserialize an object into a known type. + if (jsonWebKeyUtf8Reader.AdditionalData.TryGetValue("JsonWebKey", out object jsonWebKey)) + { + JsonElement? jsonElement = jsonWebKey as JsonElement?; + if (jsonElement.HasValue) + jsonWebKeyUtf8Reader.AdditionalData["JsonWebKey"] = JsonWebKeySerializer.Read(jsonElement.Value.GetRawText()); + + jsonWebKey6x.AdditionalData["JsonWebKey"] = JsonConvert.DeserializeObject(jsonWebKey6x.AdditionalData["JsonWebKey"].ToString()); + } + + // ensure what our utf8reader and newtonsoft are the same + // RELNOTE: + // Object for Newtonsoft will be 'Newtonsoft.Json.Linq.JObject', System.Text.Json 'System.Text.Json.JsonElement' + // Array for Newtonsoft will be 'Newtonsoft.Json.Linq.JArray', System.Text.Json 'System.Text.Json.JsonElement' + // int for Newtonsoft will be 'System.Int64', System.Text.Json 'System.Int32' + context.AddDictionaryKeysToIgnoreWhenComparing("Object", "Array", "int"); + if (!IdentityComparer.AreEqual(jsonWebKeyUtf8Reader, jsonWebKey6x, context)) + { + context.Diffs.Add("jsonWebKeyUtf8Reader != jsonWebKey6x"); + context.Diffs.Add("========================================="); + } + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + public static TheoryData RoundTripDataSet + { + get + { + var theoryData = new TheoryData(); + + theoryData.Add(new JsonWebKeyTheoryData("AdditionalDataWithJsonWebKey") + { + JsonWebKey = AdditionalData("JsonWebKey", JsonUtilities.CreateJsonElement(JsonWebKeySerializer.Write(new JsonWebKey { Alg = "Alg" }))), + JsonWebKey6x = AdditionalData6x("JsonWebKey", new JsonWebKey6x { Alg = "Alg" }) + }); + + theoryData.Add(new JsonWebKeyTheoryData("AdditionalData") + { + JsonWebKey = AdditionalData(), + JsonWebKey6x = AdditionalData6x() + }); + + theoryData.Add(new JsonWebKeyTheoryData("FullyPopulated") + { + JsonWebKey = JsonUtilities.FullyPopulatedJsonWebKey(), + JsonWebKey6x = JsonUtilities.FullyPopulatedJsonWebKey6x() + }); + + return theoryData; + } + } + + private static JsonWebKey AdditionalData(string key = null, object obj = null) + { + JsonWebKey jsonWebKey = new JsonWebKey(); + JsonUtilities.SetAdditionalData(jsonWebKey.AdditionalData, key, obj); + return jsonWebKey; + } + + private static JsonWebKey6x AdditionalData6x(string key = null, object obj = null) + { + JsonWebKey6x jsonWebKey = new JsonWebKey6x(); + JsonUtilities.SetAdditionalData6x(jsonWebKey.AdditionalData, key, obj); + return jsonWebKey; + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySet6x.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySet6x.cs new file mode 100644 index 0000000000..b739f99945 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySet6x.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Microsoft.IdentityModel.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + /// + /// Contains a collection of that can be populated from a json string. + /// + /// provides support for https://datatracker.ietf.org/doc/html/rfc7517. + /// This is the original JsonWebKeySet in the 6x branch. + /// Used for ensuring backcompat. + [JsonObject] + public class JsonWebKeySet6x + { + private const string _className = "Microsoft.IdentityModel.Tokens.JsonWebKeySet6x"; + + /// + /// Returns a new instance of . + /// + /// a string that contains JSON Web Key parameters in JSON format. + /// + /// If 'json' is null or empty. + /// If 'json' fails to deserialize. + public static JsonWebKeySet6x Create(string json) + { + if (string.IsNullOrEmpty(json)) + throw LogHelper.LogArgumentNullException(nameof(json)); + + return new JsonWebKeySet6x(json); + } + + /// + /// Initializes an new instance of . + /// + public JsonWebKeySet6x() + { + } + + /// + /// Initializes an new instance of from a json string. + /// + /// a json string containing values. + /// If 'json' is null or empty. + /// If 'json' fails to deserialize. + public JsonWebKeySet6x(string json) + { + if (string.IsNullOrEmpty(json)) + throw LogHelper.LogArgumentNullException(nameof(json)); + + try + { + LogHelper.LogVerbose(LogMessages.IDX10806, json, LogHelper.MarkAsNonPII(_className)); + JsonConvert.PopulateObject(json, this); + } + catch (Exception ex) + { + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10805, json, LogHelper.MarkAsNonPII(_className)), ex)); + } + } + + /// + /// When deserializing from JSON any properties that are not defined will be placed here. + /// + [JsonExtensionData] + public virtual IDictionary AdditionalData { get; } = new Dictionary(); + + /// + /// Gets the . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = JsonWebKeySetParameterNames.Keys, Required = Required.Default)] + public IList Keys { get; private set; } = new List(); + + /// + /// Default value for the flag that controls whether unresolved JsonWebKeys will be included in the resulting collection of method. + /// + [DefaultValue(true)] + public static bool DefaultSkipUnresolvedJsonWebKeys = true; + + /// + /// Flag that controls whether unresolved JsonWebKeys will be included in the resulting collection of method. + /// + [DefaultValue(true)] + [JsonIgnore] + public bool SkipUnresolvedJsonWebKeys { get; set; } = DefaultSkipUnresolvedJsonWebKeys; + + // removed GetSigningKeys as that would pull in too much of 6x and we are only interested in serialization. + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetSerializationTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetSerializationTests.cs new file mode 100644 index 0000000000..8b95dff9a1 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetSerializationTests.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.TestUtils; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + public class JsonWebKeySetSerializationTests + { + /// + /// This test is to ensure that a JsonWebKeySet1 from 6x == 7x. + /// The keysets are fully populated and each property checked. + /// + /// + [Theory, MemberData(nameof(SerializeDataSet))] + public void Serialize(JsonWebKeySetTheoryData theoryData) + { + var context = new CompareContext(theoryData); + try + { + string json6x = JsonConvert.SerializeObject(theoryData.JsonWebKeySet6x); + string jsonSerialize = System.Text.Json.JsonSerializer.Serialize(theoryData.JsonWebKeySet); + string jsonUtf8Writer = JsonWebKeySetSerializer.Write(theoryData.JsonWebKeySet); + + JsonWebKeySet6x jsonWebKeySet6x = JsonConvert.DeserializeObject(json6x); + JsonWebKeySet jsonWebKeySetDeserialize = System.Text.Json.JsonSerializer.Deserialize(jsonUtf8Writer); + JsonWebKeySet jsonWebKeySetUtf8Reader = new JsonWebKeySet(jsonUtf8Writer); + + // ensure that our utf8writer and newtonsoft generate the same json string + if (!IdentityComparer.AreEqual(jsonUtf8Writer, json6x, context)) + { + context.Diffs.Add("jsonUtf8Writer != json6x"); + context.Diffs.Add("========================================="); + } + + // TODO - when a 8.0 target introduced we will need to adjust as JsonSerializer will be able to set collections without setters + + // compare our utf8Reader with expected value + if (!IdentityComparer.AreEqual(jsonWebKeySetUtf8Reader, theoryData.JsonWebKeySet, context)) + { + context.Diffs.Add("jsonWebKeySetUtf8Reader != theoryData.JsonWebKeySet1"); + context.Diffs.Add("========================================="); + } + + // ensure what our utf8reader and newtonsoft keys are the same + CompareContext localContext = new CompareContext(theoryData); + localContext.AddDictionaryKeysToIgnoreWhenComparing("Object", "Array", "int"); + if (jsonWebKeySetUtf8Reader.Keys.Count == jsonWebKeySet6x.Keys.Count) + { + // Now compare Keys assuming same order + for (int i = 0; i < jsonWebKeySetUtf8Reader.Keys.Count; i++) + if (!IdentityComparer.AreEqual(jsonWebKeySetUtf8Reader.Keys[i], jsonWebKeySet6x.Keys[i], localContext)) + { + localContext.Diffs.Add($"jsonWebKeySetUtf8Reader.Keys'{i}' != jsonWebKey6x.Keys'{i}'"); + localContext.Diffs.Add("========================================="); + } + } + else + { + localContext.Diffs.Add("jsonWebKeySetUtf8Reader.Keys.Count != jsonWebKey6x.Keys.Count"); + localContext.Diffs.Add("========================================="); + } + + context.Merge(localContext); + } + catch (Exception ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData SerializeDataSet + { + get + { + var theoryData = new TheoryData(); + + theoryData.Add(new JsonWebKeySetTheoryData("FullyPopulated") + { + JsonWebKeySet = JsonUtilities.FullyPopulatedJsonWebKeySet(), + JsonWebKeySet6x = JsonUtilities.FullyPopulatedJsonWebKeySet6x() + }); + + return theoryData; + } + } + + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetTheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetTheoryData.cs new file mode 100644 index 0000000000..7e55f0c26a --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeySetTheoryData.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using Microsoft.IdentityModel.TestUtils; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + public class JsonWebKeySetTheoryData : TheoryDataBase + { + public JsonWebKeySetTheoryData() { } + + public JsonWebKeySetTheoryData(string testId) : base(testId) { } + + public IList ExpectedSigningKeys { get; set; } + + public string Json { get; set; } + + public JsonWebKeySet JsonWebKeySet { get; set; } + + public JsonWebKeySet6x JsonWebKeySet6x { get; set; } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeyTheoryData.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeyTheoryData.cs new file mode 100644 index 0000000000..874b59945f --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonWebKeyTheoryData.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.TestUtils; + +namespace Microsoft.IdentityModel.Tokens.Json.Tests +{ + public class JsonWebKeyTheoryData : TheoryDataBase + { + public JsonWebKeyTheoryData() { } + + public JsonWebKeyTheoryData(string testId) : base(testId) { } + + public string Crv { get; set; } + + public string D { get; set; } + + public string Json { get; set; } + + public JsonWebKey JsonWebKey { get; set; } + + public JsonWebKey6x JsonWebKey6x { get; set; } + + public ExpectedException JsonReaderExpectedException { get; set; } = ExpectedException.NoExceptionExpected; + + public ExpectedException JsonSerializerExpectedException { get; set; } = ExpectedException.NoExceptionExpected; + + public string X { get; set; } + + public string Y { get; set; } + + public bool UsePrivateKey { get; set; } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/SecurityKey6x.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/SecurityKey6x.cs new file mode 100644 index 0000000000..ab095d0d4e --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/SecurityKey6x.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Logging; +using Newtonsoft.Json; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Base class for Security Key. + /// + /// This is the original SecurityKey in the 6x branch. + /// Used for ensuring backcompat. + public abstract class SecurityKey6x + { + private CryptoProviderFactory _cryptoProviderFactory; + private object _internalIdLock = new object(); + private string _internalId; + + internal SecurityKey6x(SecurityKey6x key) + { + _cryptoProviderFactory = key._cryptoProviderFactory; + KeyId = key.KeyId; + } + + /// + /// Default constructor + /// + public SecurityKey6x() + { + _cryptoProviderFactory = CryptoProviderFactory.Default; + } + + [JsonIgnore] + internal virtual string InternalId + { + get + { + if (_internalId == null) + { + lock (_internalIdLock) + { + if (CanComputeJwkThumbprint()) + _internalId = Base64UrlEncoder.Encode(ComputeJwkThumbprint()); + else + _internalId = string.Empty; + } + } + + return _internalId; + } + } + + /// + /// This must be overridden to get the size of this . + /// + public abstract int KeySize { get; } + + /// + /// Gets the key id of this . + /// + [JsonIgnore] + public virtual string KeyId { get; set; } + + /// + /// Gets or sets . + /// + [JsonIgnore] + public CryptoProviderFactory CryptoProviderFactory + { + get + { + return _cryptoProviderFactory; + } + set + { + _cryptoProviderFactory = value ?? throw LogHelper.LogArgumentNullException(nameof(value)); + } + } + + /// + /// Returns the formatted string: GetType(), KeyId: 'value', InternalId: 'value'. + /// + /// string + public override string ToString() + { + return $"{GetType()}, KeyId: '{KeyId}', InternalId: '{InternalId}'."; + } + + /// + /// Determines whether the can compute a JWK thumbprint. + /// + /// true if JWK thumbprint can be computed; otherwise, false. + /// https://datatracker.ietf.org/doc/html/rfc7638 + public virtual bool CanComputeJwkThumbprint() + { + return false; + } + + /// + /// Computes a sha256 hash over the . + /// + /// A JWK thumbprint. + /// https://datatracker.ietf.org/doc/html/rfc7638 + public virtual byte[] ComputeJwkThumbprint() + { + throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10710))); + } + + /// + /// Checks if can perform the cryptographic operation specified by the with this . + /// + /// the algorithm to apply. + /// true if can perform the cryptographic operation sepecified by the with this . + public virtual bool IsSupportedAlgorithm(string algorithm) + { + // do not throw if algorithm is null or empty to stay in sync with CryptoProviderFactory.IsSupportedAlgorithm. + + // will not compile as SecurityKey is expected + // return CryptoProviderFactory.IsSupportedAlgorithm(algorithm, this); + + return true; + } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeySetTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeySetTests.cs index f90c42486b..a83568e726 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeySetTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeySetTests.cs @@ -7,60 +7,141 @@ using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text.Json; using Microsoft.IdentityModel.TestUtils; using Newtonsoft.Json; using Xunit; -namespace Microsoft.IdentityModel.Tokens.Tests +namespace Microsoft.IdentityModel.Tokens.Json.Tests { public class JsonWebKeySetTests { - [Theory, MemberData(nameof(JsonWebKeySetDataSet))] - private void Constructors( - string testId, - string json, - JsonWebKeySet compareTo, - ExpectedException ee) + [Theory, MemberData(nameof(ConstructorDataSet))] + public void Constructors(JsonWebKeySetTheoryData theoryData) { - var context = new CompareContext($"{this}.{testId}"); + var context = new CompareContext(theoryData); context.PropertiesToIgnoreWhenComparing.Add(typeof(JsonWebKeySet), new List() { "SkipUnresolvedJsonWebKeys" }); + try { - var jsonWebKeys = new JsonWebKeySet(json); + var jsonWebKeys = new JsonWebKeySet(theoryData.Json); var keys = jsonWebKeys.GetSigningKeys(); - ee.ProcessNoException(context); - if (compareTo != null) - IdentityComparer.AreEqual(jsonWebKeys, compareTo, context); + theoryData.ExpectedException.ProcessNoException(context); + if (theoryData.JsonWebKeySet != null) + IdentityComparer.AreEqual(jsonWebKeys, theoryData.JsonWebKeySet, context); + + if (theoryData.ExpectedSigningKeys != null) + IdentityComparer.AreEqual(keys, theoryData.ExpectedSigningKeys, context); } catch (Exception ex) { - ee.ProcessException(ex, context.Diffs); + theoryData.ExpectedException.ProcessException(ex, context); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData JsonWebKeySetDataSet + public static TheoryData ConstructorDataSet { get { - var dataset = new TheoryData(); - dataset.Add("Test1", DataSets.JsonWebKeySetAdditionalDataString1, DataSets.JsonWebKeySetAdditionalData1, ExpectedException.NoExceptionExpected); - dataset.Add("Test2", null, null, ExpectedException.ArgumentNullException()); - dataset.Add("Test3", DataSets.JsonWebKeySetString1, DataSets.JsonWebKeySet1, ExpectedException.NoExceptionExpected); - dataset.Add("Test4", DataSets.JsonWebKeySetBadFormatingString, null, ExpectedException.ArgumentException(substringExpected: "IDX10805:", inner: typeof(JsonReaderException))); - dataset.Add("Test5", File.ReadAllText(DataSets.GoogleCertsFile), DataSets.GoogleCertsExpected, ExpectedException.NoExceptionExpected); - dataset.Add("Test6", DataSets.JsonWebKeySetBadRsaExponentString, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test7", DataSets.JsonWebKeySetBadRsaModulusString, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test8", DataSets.JsonWebKeySetKtyNotRsaString, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test9", DataSets.JsonWebKeySetUseNotSigString, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test10", DataSets.JsonWebKeySetBadX509String, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test11", DataSets.JsonWebKeySetECCString, DataSets.JsonWebKeySetEC, ExpectedException.NoExceptionExpected); - dataset.Add("Test12", DataSets.JsonWebKeySetBadECCurveString, null, ExpectedException.NoExceptionExpected); - dataset.Add("Test13", DataSets.JsonWebKeySetOnlyX5tString, DataSets.JsonWebKeySetOnlyX5t, ExpectedException.NoExceptionExpected); - dataset.Add("Test14", DataSets.JsonWebKeySetX509DataString, DataSets.JsonWebKeySetX509Data, ExpectedException.NoExceptionExpected); - - return dataset; + var theoryData = new TheoryData(); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySet1") + { + Json = DataSets.JsonWebKeySetString1, + JsonWebKeySet = DataSets.JsonWebKeySet1 + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("Null") + { + ExpectedException = ExpectedException.ArgumentNullException() + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetBadFormatingString") + { + ExpectedException = ExpectedException.ArgumentException(substringExpected: "IDX10805:", inner: typeof(System.Text.Json.JsonException)), + Json = DataSets.JsonWebKeySetBadFormatingString + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("GoogleCertsExpected") + { + Json = File.ReadAllText(DataSets.GoogleCertsFile), + JsonWebKeySet = DataSets.GoogleCertsExpected + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetBadRsaExponentString") + { + Json = DataSets.JsonWebKeySetBadRsaExponentString, + ExpectedSigningKeys = new List() + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetBadRsaModulusString") + { + Json = DataSets.JsonWebKeySetBadRsaModulusString, + ExpectedSigningKeys = new List() + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetKtyNotRsaString") + { + Json = DataSets.JsonWebKeySetKtyNotRsaString, + ExpectedSigningKeys = new List() + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetUseNotSigString") + { + Json = DataSets.JsonWebKeySetUseNotSigString, + ExpectedSigningKeys = new List() + }); + + List keys = new List(); + if (JsonWebKeyConverter.TryCreateToRsaSecurityKey(DataSets.JsonWebKeyBadX509Data, out SecurityKey securityKey)) + keys.Add(securityKey); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetBadX509String") + { + Json = DataSets.JsonWebKeySetBadX509String, + ExpectedSigningKeys = keys + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetECCString") + { + Json = DataSets.JsonWebKeySetECCString, + JsonWebKeySet = DataSets.JsonWebKeySetEC + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetBadECCurveString") + { + Json = DataSets.JsonWebKeySetBadECCurveString, + ExpectedSigningKeys = new List() + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetOnlyX5tString") + { + Json = DataSets.JsonWebKeySetOnlyX5tString, + JsonWebKeySet = DataSets.JsonWebKeySetOnlyX5t + }); + + theoryData.Add( + new JsonWebKeySetTheoryData("JsonWebKeySetX509DataString") + { + Json = DataSets.JsonWebKeySetX509DataString, + JsonWebKeySet = DataSets.JsonWebKeySetX509Data + }); + + return theoryData; } } @@ -83,16 +164,6 @@ public void Defaults() TestUtilities.AssertFailIfErrors(context); } - [Fact] - public void GetSets() - { - } - - [Fact] - public void Publics() - { - } - [Fact] public void SigningKeysExtensibility() { @@ -381,12 +452,5 @@ private static ECDsaSecurityKey CreateEcdsaSecurityKey(JsonWebKeySet webKeySet, KeyId = webKey.KeyId }; } - - public class JsonWebKeySetTheoryData : TheoryDataBase - { - public JsonWebKeySet JsonWebKeySet { get; set; } - - public List ExpectedSigningKeys { get; set; } - } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeyTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeyTests.cs index 4a627ddd10..ea0bd883f5 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeyTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/JsonWebKeyTests.cs @@ -4,47 +4,70 @@ using System; using System.Collections.Generic; using Microsoft.IdentityModel.TestUtils; -using Newtonsoft.Json; using Xunit; -namespace Microsoft.IdentityModel.Tokens.Tests +namespace Microsoft.IdentityModel.Tokens.Json.Tests { public class JsonWebKeyTests { - [Theory, MemberData(nameof(JsonWebKeyDataSet))] - public void Constructors(string json, JsonWebKey compareTo, ExpectedException ee) + [Theory, MemberData(nameof(ConstructorDataSet))] + public void Constructors(JsonWebKeyTheoryData theoryData) { var context = new CompareContext(); try { - var jsonWebKey = new JsonWebKey(json); - ee.ProcessNoException(context); - if (compareTo != null) - IdentityComparer.AreEqual(jsonWebKey, compareTo, context); + var jsonWebKey = new JsonWebKey(theoryData.Json); + theoryData.ExpectedException.ProcessNoException(context); + if (theoryData.JsonWebKey != null) + { + IdentityComparer.AreEqual(jsonWebKey, theoryData.JsonWebKey, context); + JsonWebKey6x jsonWebKey6x = new JsonWebKey6x(theoryData.Json); + IdentityComparer.AreEqual(jsonWebKey6x, jsonWebKey, context); + } } catch (Exception ex) { - ee.ProcessException(ex, context.Diffs); + theoryData.ExpectedException.ProcessException(ex, context.Diffs); } TestUtilities.AssertFailIfErrors(context); } - public static TheoryData JsonWebKeyDataSet + public static TheoryData ConstructorDataSet { get { - var dataset = new TheoryData(); + var theoryData = new TheoryData(); + theoryData.Add(new JsonWebKeyTheoryData("Null_Json") + { + ExpectedException = ExpectedException.ArgumentNullException(substringExpected: "json") + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyString") + { + JsonWebKey = DataSets.JsonWebKey1, + Json = DataSets.JsonWebKeyString + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyBadFormatString1") + { + ExpectedException = ExpectedException.ArgumentException(substringExpected: "IDX10805:", inner: typeof(System.Text.Json.JsonException)), + Json = DataSets.JsonWebKeyBadFormatString1 + }); + + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyBadFormatString2") + { + ExpectedException = ExpectedException.ArgumentException(substringExpected: "IDX10805:", inner: typeof(System.Text.Json.JsonException)), + Json = DataSets.JsonWebKeyBadFormatString2 + }); - dataset.Add(null, null, ExpectedException.ArgumentNullException(substringExpected: "json")); - dataset.Add(DataSets.JsonWebKeyFromPingString1, DataSets.JsonWebKeyFromPing1, ExpectedException.NoExceptionExpected); - dataset.Add(DataSets.JsonWebKeyString1, DataSets.JsonWebKey1, ExpectedException.NoExceptionExpected); - dataset.Add(DataSets.JsonWebKeyString2, DataSets.JsonWebKey2, ExpectedException.NoExceptionExpected); - dataset.Add(DataSets.JsonWebKeyBadFormatString1, null, ExpectedException.ArgumentException(inner: typeof(JsonReaderException))); - dataset.Add(DataSets.JsonWebKeyBadFormatString2, null, ExpectedException.ArgumentException(inner: typeof(JsonSerializationException))); - dataset.Add(DataSets.JsonWebKeyBadX509String, DataSets.JsonWebKeyBadX509Data, ExpectedException.NoExceptionExpected); + theoryData.Add(new JsonWebKeyTheoryData("JsonWebKeyBadX509String") + { + JsonWebKey = DataSets.JsonWebKeyBadX509Data, + Json = DataSets.JsonWebKeyBadX509DataString + }); - return dataset; + return theoryData; } } @@ -111,11 +134,6 @@ public void GetSets() TestUtilities.AssertFailIfErrors("JsonWebKey_GetSets", errors); } - [Fact] - public void Publics() - { - } - // Tests to make sure conditional property serialization for JsonWebKeys is working properly. [Fact] public void ConditionalPropertySerialization() @@ -133,18 +151,20 @@ public void ConditionalPropertySerialization() Use = "sig", }; - var jsonString1 = JsonConvert.SerializeObject(jsonWebKeyEmptyCollections); + var jsonString1 = JsonWebKeySerializer.Write(jsonWebKeyEmptyCollections); if (jsonString1.Contains("key_ops")) context.Diffs.Add("key_ops is empty and should not be present in serialized JsonWebKey"); + if (jsonString1.Contains("x5c")) context.Diffs.Add("x5c is empty and should not be present in serialized JsonWebKey"); var jsonWebKeyWithCollections = new JsonWebKey(); jsonWebKeyWithCollections.X5c.Add("MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng"); jsonWebKeyWithCollections.KeyOps.Add("signing"); - var jsonString2 = JsonConvert.SerializeObject(jsonWebKeyWithCollections); + var jsonString2 = JsonWebKeySerializer.Write(jsonWebKeyWithCollections); if (!jsonString2.Contains("key_ops")) context.Diffs.Add("key_ops is non-empty and should be present in serialized JsonWebKey"); + if (!jsonString2.Contains("x5c")) context.Diffs.Add("x5c is non-empty and should be present in serialized JsonWebKey"); @@ -154,7 +174,7 @@ public void ConditionalPropertySerialization() [Fact] public void ComputeJwkThumbprintSpec() { - // https://datatracker.ietf.org/doc/html/rfc7638#section-3.1 + // https://datatracker.ietf.org/doc/html/rfc7638#section-3-1 var context = TestUtilities.WriteHeader($"{this}.ComputeJwkThumbprintSpec", "", true); var jwk = new JsonWebKey() diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj index 15387c7535..571c3cdde6 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj @@ -12,12 +12,6 @@ true - - - PreserveNewest - - - @@ -34,4 +28,10 @@ + + + Always + + + diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtHeaderTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtHeaderTests.cs index 598a507718..7e63a64fc5 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtHeaderTests.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtHeaderTests.cs @@ -123,7 +123,7 @@ public static TheoryData ConstructorTheoryData [Fact] public void Kid() { - var jsonWebKey = new JsonWebKey(DataSets.JsonWebKeyString1); + var jsonWebKey = new JsonWebKey(DataSets.JsonWebKeyString); var credentials = new SigningCredentials(jsonWebKey, SecurityAlgorithms.RsaSha256Signature); var token = new JwtSecurityToken(claims: Default.Claims, signingCredentials: credentials); Assert.Equal(jsonWebKey.Kid, token.Header.Kid);