diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/JsonWebKeyComparison.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/JsonWebKeyComparison.cs
new file mode 100644
index 0000000000..0bf9766feb
--- /dev/null
+++ b/benchmark/Microsoft.IdentityModel.Benchmarks/JsonWebKeyComparison.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#if NETCOREAPP
+
+using System;
+using BenchmarkDotNet.Attributes;
+using Microsoft.IdentityModel.Tokens;
+
+namespace Microsoft.IdentityModel.Benchmarks
+{
+ // dotnet run -c release -f net8.0 --filter Microsoft.IdentityModel.Benchmarks.JsonWebKeyComparison.*
+
+ ///
+ /// The purpose of this benchmark is to compare the performance of different methods to compare JsonWebKeys and RSASecurityKeys.
+ /// JsonWebKeys have a base64url encoded exponent and modulus, while RSASecurityKeys have base64 encoded exponent and modulus.
+ ///
+ public class JsonWebKeyComparison
+ {
+ private RsaSecurityKey _rsaSecurityKey;
+ private JsonWebKey _jsonWebKey;
+ private string _base64EncodedE;
+ private string _base64EncodedN;
+ private string _base64UrlEncodedE;
+ private string _base64UrlEncodedN;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _rsaSecurityKey = BenchmarkUtils.RsaSecurityKey;
+ _base64EncodedE = Convert.ToBase64String(_rsaSecurityKey.Parameters.Exponent);
+ _base64EncodedN = Convert.ToBase64String(_rsaSecurityKey.Parameters.Modulus);
+ _base64UrlEncodedE = Base64UrlEncoder.Encode(_rsaSecurityKey.Parameters.Exponent);
+ _base64UrlEncodedN = Base64UrlEncoder.Encode(_rsaSecurityKey.Parameters.Modulus);
+ _jsonWebKey = new JsonWebKey { E = _base64UrlEncodedE, N = _base64UrlEncodedN };
+ }
+
+ [Benchmark]
+ public bool Base64UrlEncoderDecodeBytes()
+ {
+ return _base64EncodedE.Equals(Convert.ToBase64String(Base64UrlEncoder.DecodeBytes(_jsonWebKey.E)))
+ && _base64EncodedN.Equals(Convert.ToBase64String(Base64UrlEncoder.DecodeBytes(_jsonWebKey.N)));
+ }
+
+ [Benchmark]
+ public bool UtilityAreEqual()
+ {
+ return Utility.AreEqual(Convert.FromBase64String(_base64EncodedE), Base64UrlEncoder.DecodeBytes(_jsonWebKey.E))
+ && Utility.AreEqual(Convert.FromBase64String(_base64EncodedN), Base64UrlEncoder.DecodeBytes(_jsonWebKey.N));
+ }
+
+ [Benchmark]
+ public bool Base64EncodedEquals()
+ {
+ return _base64EncodedE.Equals(Convert.ToBase64String(Base64UrlEncoder.DecodeBytes(_jsonWebKey.E)))
+ && _base64EncodedN.Equals(Convert.ToBase64String(Base64UrlEncoder.DecodeBytes(_jsonWebKey.N)));
+ }
+
+ [Benchmark]
+ public bool Base64UrlEncodedEquals()
+ {
+ return _base64UrlEncodedE.Equals(Base64UrlEncoder.Encode(Convert.FromBase64String(_base64EncodedE)))
+ && _base64UrlEncodedN.Equals(Base64UrlEncoder.Encode(Convert.FromBase64String(_base64EncodedN)));
+ }
+ }
+}
+#endif
diff --git a/src/Microsoft.IdentityModel.Xml/KeyInfo.cs b/src/Microsoft.IdentityModel.Xml/KeyInfo.cs
index 13f5caaabd..d629451fe2 100644
--- a/src/Microsoft.IdentityModel.Xml/KeyInfo.cs
+++ b/src/Microsoft.IdentityModel.Xml/KeyInfo.cs
@@ -115,7 +115,7 @@ public override int GetHashCode()
///
/// Returns true if the KeyInfo object can be matched with the specified SecurityKey, returns false otherwise.
///
- internal bool MatchesKey(SecurityKey key)
+ protected internal virtual bool MatchesKey(SecurityKey key)
{
if (key == null)
return false;
@@ -188,10 +188,14 @@ private bool Matches(JsonWebKey key)
if (key == null)
return false;
- if (RSAKeyValue != null)
+ if (RSAKeyValue != null
+ && !string.IsNullOrEmpty(key.E)
+ && !string.IsNullOrEmpty(key.N)
+ && !string.IsNullOrEmpty(RSAKeyValue.Exponent)
+ && !string.IsNullOrEmpty(RSAKeyValue.Modulus))
{
- return RSAKeyValue.Exponent.Equals(Convert.FromBase64String(key.E))
- && RSAKeyValue.Modulus.Equals(Convert.FromBase64String(key.N));
+ return key.E.Equals(Base64UrlEncoder.Encode(Convert.FromBase64String(RSAKeyValue.Exponent)))
+ && key.N.Equals(Base64UrlEncoder.Encode(Convert.FromBase64String(RSAKeyValue.Modulus)));
}
foreach (var x5c in key.X5c)
diff --git a/src/Microsoft.IdentityModel.Xml/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.Xml/Properties/AssemblyInfo.cs
index ad3d6cce6c..a252424433 100644
--- a/src/Microsoft.IdentityModel.Xml/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.IdentityModel.Xml/Properties/AssemblyInfo.cs
@@ -11,3 +11,4 @@
[assembly: ComVisible(false)]
[assembly: InternalsVisibleTo("Microsoft.IdentityModel.Tokens.Saml, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
+[assembly: InternalsVisibleTo("Microsoft.IdentityModel.Xml.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
diff --git a/src/Microsoft.IdentityModel.Xml/PublicAPI.Unshipped.txt b/src/Microsoft.IdentityModel.Xml/PublicAPI.Unshipped.txt
index 2d773dfa55..fbf2bf4ab6 100644
--- a/src/Microsoft.IdentityModel.Xml/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.IdentityModel.Xml/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
override Microsoft.IdentityModel.Xml.XmlValidationException.StackTrace.get -> string
+virtual Microsoft.IdentityModel.Xml.KeyInfo.MatchesKey(Microsoft.IdentityModel.Tokens.SecurityKey key) -> bool
diff --git a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs
index efbda03b99..e46623cc38 100644
--- a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs
+++ b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs
@@ -29,6 +29,9 @@ public static X509Certificate2 AADSigningCert
public static SecureString SelfSigned2048_SHA384_Password = ConvertToSecureString("SelfSigned2048_SHA384");
public static SecureString SelfSigned2048_SHA512_Password = ConvertToSecureString("SelfSigned2048_SHA512");
+ // X509Certificate created with SKI extension, expires in 15 years from 2/19/2025
+ public static string SelfSignedWithSKIExtension_Public = "MIIC6jCCAdKgAwIBAgIJAMnPnxHDryl8MA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMTEFNlbGYtU2lnbmVkLUNlcnQwHhcNMjUwMjE4MjE1NzUxWhcNNDAwMjE5MjE1NzUxWjAbMRkwFwYDVQQDExBTZWxmLVNpZ25lZC1DZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6oQ5HQXhBKAoY+g/AAEhwMKLPcKQY9up9fDFZeU77rnMoJvuwE6gdqMyJ4684lKdKWWeU4eB1gkA3d8uNuLXoNlRed1wyQFd7RJ5xN+WnHRadLYus4xfYYzdqHlR/4FMd2hpnekmOrKuaAjnjzjppqopSxIvgmYdInahYaebs5ebkgCbJqxrLDWvWsiLMw4d/MP6aol6kUKOI+S2VIwovOM1h1mJvu2gneWQrY3icb7YUGcvJf+VwGpN9Y5qEGcTwWBdgOXiyQim5DQgK9B4lQG07fVEk6O5/U73id78tyv8WHWnz2v1atwe6Rlv/+ePIB+YpACpddpPdVc6Y3TaQIDAQABozEwLzAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKoqKH110TGBQu3MAyDXEDk6qEbiMA0GCSqGSIb3DQEBCwUAA4IBAQBoMdhMtWCkSFge57MXn4uTGioXKNIvGxQ9Fh8JHNJ6Zzmb8+fjEK5Llofp9/sqaKbBiIg8KC9QKoSYtqyKitIYTx08eAXRi7wbEl55eTuHAkcZpxpX8B2JvTF8Z+tLqmre91cL8cARZDdVlIfys5TSvPYPCKbNMbrD5kHjvx7OZBWDKFIaedQ5eD7NwmdhIOOurvmkLRNYoHpVI06ReGzIxLeeLxfNy/xDY3NKoJ0+bO7rDR6lBpiwvdB6hE4LcTJNYVtKztFLkV+s3LEW3rVr0nX4/VEXPq0YdSyW7bAYyEDtStciu9+txY6AA0Xhz6Sp4KHt6WOWAT9tLZvR8Hyc";
+
// .../Certs/SelfSigned1024_SHA256.pfx
// password: SelfSigned1024_SHA256
public static string SelfSigned1024_SHA256 = @"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";
@@ -606,6 +609,7 @@ private static object CreateRSACng2048()
return Activator.CreateInstance(_rsaCngType, 2048);
}
#endif
+
public static SecurityKey DefaultRsaSecurityKey1
{
get
@@ -726,6 +730,26 @@ public static SecurityKey DefaultJsonWebKeyWithParameters2
}
}
+#if NET472_OR_GREATER || NETCOREAPP
+ ///
+ /// Can be used to generate a self-signed certificate for testing purposes.
+ /// Since we support .NET 4.6.2, we can't call directly as CertificateRequest is not available.
+ /// Instead, use Convert.ToBase64string(cert.RawData), copy the string and then rehydrate the cert.
+ ///
+ /// A X509Certificate with the DigitalSignature and SubjectKeyIdentifier extensions set.
+ public static X509Certificate2 GenerateSelfSignedCertificate()
+ {
+ string subjectName = "CN=Self-Signed-Cert";
+ using (var rsa = RSA.Create(2048))
+ {
+ var certRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+ certRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, true));
+ certRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certRequest.PublicKey, false));
+ return certRequest.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(15));
+ }
+ }
+#endif
+
public static string JsonWebKeySymmetricKid128
{
get
@@ -980,8 +1004,6 @@ public static JsonWebKey JsonWebKeyX509_2048_As_RSA
}
}
-
-
public static JsonWebKey JsonWebKeyX509_2048_Public
{
get
diff --git a/test/Microsoft.IdentityModel.Xml.Tests/KeyInfoTests.cs b/test/Microsoft.IdentityModel.Xml.Tests/KeyInfoTests.cs
index 8d0a2388d1..e85ee1fa68 100644
--- a/test/Microsoft.IdentityModel.Xml.Tests/KeyInfoTests.cs
+++ b/test/Microsoft.IdentityModel.Xml.Tests/KeyInfoTests.cs
@@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.TestUtils;
+using Microsoft.IdentityModel.Tokens;
using Xunit;
namespace Microsoft.IdentityModel.Xml.Tests
@@ -76,8 +78,6 @@ public void KeyInfo_HashCodeCollectionTests()
Assert.True(inCollection);
}
-#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
-
[Theory, MemberData(nameof(KeyInfoDataComparisonData), DisableDiscoveryEnumeration = true)]
public void KeyInfo_HashCodeTests(KeyInfoComparisonTheoryData theoryData)
{
@@ -97,7 +97,6 @@ public void KeyInfo_HashCodeTests(KeyInfoComparisonTheoryData theoryData)
TestUtilities.AssertFailIfErrors(context);
}
-
[Theory, MemberData(nameof(KeyInfoDataComparisonData), DisableDiscoveryEnumeration = true)]
public void KeyInfo_EqualsTests(KeyInfoComparisonTheoryData theoryData)
{
@@ -229,8 +228,115 @@ public static TheoryData KeyInfoDataComparisonData
}
}
+ [Theory, MemberData(nameof(KeyInfoMatchesTheoryData), DisableDiscoveryEnumeration = true)]
+ public void KeyInfoMatchesKey(KeyInfoTheoryData theoryData)
+ {
+ var context = TestUtilities.WriteHeader($"{this}.{nameof(KeyInfoMatchesKey)}", theoryData);
+ try
+ {
+ bool matches = theoryData.KeyInfo.MatchesKey(theoryData.SecurityKey);
+ if (matches != theoryData.MatchesKey)
+ context.AddDiff($"KeyInfo.MatchesKey failed, Expected: '{theoryData.MatchesKey}', Actual: '{matches}', SecurityKey: '{theoryData.SecurityKey}', KeyInfo: '{theoryData.KeyInfo}'");
+
+ theoryData.ExpectedException.ProcessNoException(context);
+ }
+ catch (Exception ex)
+ {
+ theoryData.ExpectedException.ProcessException(ex, context);
+ }
+
+ TestUtilities.AssertFailIfErrors(context);
+ }
+
+ public static TheoryData KeyInfoMatchesTheoryData
+ {
+ get
+ {
+ X509Certificate2 x509CertificateWithSki = CertificateHelper.LoadX509Certificate(KeyingMaterial.SelfSignedWithSKIExtension_Public);
+ X509ExtensionCollection extensions = x509CertificateWithSki.Extensions ?? throw new NotSupportedException("Extensions are null");
+ X509SubjectKeyIdentifierExtension skiExtension = extensions["2.5.29.14"] as X509SubjectKeyIdentifierExtension;
+ if (skiExtension == null)
+ throw new NotSupportedException("X509SubjectKeyIdentifierExtension is null");
-#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
+ string ski = Convert.ToBase64String(skiExtension.RawData, 2, skiExtension.RawData.Length - 2);
+
+ TheoryData theoryData = new TheoryData();
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithSKIExtension_UsingExtensibility")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new CustomKeyInfo(ski),
+ SecurityKey = new X509SecurityKey(x509CertificateWithSki),
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithoutSKIExtension_UsingExtensibility_NoMatch")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new CustomKeyInfo(ski),
+ SecurityKey = new X509SecurityKey(KeyingMaterial.X509Certificate2),
+ MatchesKey = false,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithoutSKIExtension_UsingExtensibility")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new CustomKeyInfo(KeyingMaterial.X509Certificate2),
+ SecurityKey = new X509SecurityKey(KeyingMaterial.X509Certificate2),
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithSKIExtension")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(x509CertificateWithSki),
+ SecurityKey = new X509SecurityKey(x509CertificateWithSki),
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithSKIExtension_NoMatch")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(KeyingMaterial.RsaSecurityKey1),
+ SecurityKey = new X509SecurityKey(x509CertificateWithSki),
+ MatchesKey = false,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("X509CertificateWithoutSKIExtension")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(KeyingMaterial.X509Certificate2),
+ SecurityKey = new X509SecurityKey(KeyingMaterial.X509Certificate2),
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("RSASecurityKey")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(KeyingMaterial.RsaSecurityKey1),
+ SecurityKey = KeyingMaterial.RsaSecurityKey1,
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("JsonWebKey_X509Cert")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(KeyingMaterial.DefaultCert_2048_Public),
+ SecurityKey = KeyingMaterial.JsonWebKeyX509_2048_Public,
+ MatchesKey = true,
+ });
+
+ theoryData.Add(new KeyInfoTheoryData("JsonWebKey_RsaSecurityKey")
+ {
+ ExpectedException = ExpectedException.NoExceptionExpected,
+ KeyInfo = new KeyInfo(KeyingMaterial.RsaSecurityKey_2048_Public),
+ SecurityKey = KeyingMaterial.JsonWebKeyRsa_2048_Public,
+ MatchesKey = true,
+ });
+
+ return theoryData;
+ }
+ }
}
public class KeyInfoComparisonTheoryData : TheoryDataBase
@@ -246,6 +352,10 @@ public class KeyInfoComparisonTheoryData : TheoryDataBase
public class KeyInfoTheoryData : TheoryDataBase
{
+ public KeyInfoTheoryData(string testId) : base(testId)
+ {
+ }
+
public DSigSerializer Serializer
{
get;
@@ -263,5 +373,47 @@ public string Xml
get;
set;
}
+
+ public SecurityKey SecurityKey
+ {
+ get;
+ set;
+ }
+
+ public bool MatchesKey
+ {
+ get;
+ set;
+ }
+ }
+
+ public class CustomKeyInfo : KeyInfo
+ {
+ private string _securityKeyIdentifier;
+
+ public CustomKeyInfo(string securityKeyIdentifier)
+ {
+ _securityKeyIdentifier = securityKeyIdentifier;
+ }
+
+ public CustomKeyInfo(X509Certificate2 certificate) : base(certificate)
+ {
+ }
+
+ protected internal override bool MatchesKey(SecurityKey key)
+ {
+ X509SecurityKey x509SecurityKey = key as X509SecurityKey;
+ if (key != null && _securityKeyIdentifier != null)
+ {
+ X509SubjectKeyIdentifierExtension skiExtension = x509SecurityKey.Certificate.Extensions["2.5.29.14"] as X509SubjectKeyIdentifierExtension;
+ if (skiExtension == null)
+ return base.MatchesKey(key);
+
+ string subjectKeyIdentifier = Convert.ToBase64String(skiExtension.RawData, 2, skiExtension.RawData.Length - 2);
+ return _securityKeyIdentifier == subjectKeyIdentifier;
+ }
+
+ return base.MatchesKey(key);
+ }
}
}
diff --git a/test/Microsoft.IdentityModel.Xml.Tests/Microsoft.IdentityModel.Xml.Tests.csproj b/test/Microsoft.IdentityModel.Xml.Tests/Microsoft.IdentityModel.Xml.Tests.csproj
index 7d7792e650..26ca007957 100644
--- a/test/Microsoft.IdentityModel.Xml.Tests/Microsoft.IdentityModel.Xml.Tests.csproj
+++ b/test/Microsoft.IdentityModel.Xml.Tests/Microsoft.IdentityModel.Xml.Tests.csproj
@@ -7,6 +7,9 @@
Microsoft.IdentityModel.Xml.Tests
true
Microsoft.IdentityModel.Xml.Tests
+ true
+ $(MSBuildThisFileDirectory)..\..\build\35MSSharedLib1024.snk
+ true