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