diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index 4fe04afb5d1b4..c8619ed4408bc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -68,6 +68,13 @@ public PublicKey(System.Security.Cryptography.Oid oid, System.Security.Cryptogra public System.Security.Cryptography.Oid Oid { get { throw null; } } public static System.Security.Cryptography.X509Certificates.PublicKey CreateFromSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } public byte[] ExportSubjectPublicKeyInfo() { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("maccatalyst")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + public System.Security.Cryptography.DSA? GetDSAPublicKey() { throw null; } + public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPublicKey() { throw null; } + public System.Security.Cryptography.ECDsa? GetECDsaPublicKey() { throw null; } + public System.Security.Cryptography.RSA? GetRSAPublicKey() { throw null; } public bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } } public static partial class RSACertificateExtensions diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs index 3b1623bf6faa7..a641fe9f0c435 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/PublicKey.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Formats.Asn1; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Security.Cryptography.Asn1; using Internal.Cryptography; @@ -140,6 +141,122 @@ public static PublicKey CreateFromSubjectPublicKeyInfo(ReadOnlySpan source return new PublicKey(localOid, localParameters, localKeyValue); } + /// + /// Gets the public key, or if the key is not an RSA key. + /// + /// + /// The public key, or if the key is not an RSA key. + /// + /// + /// The key contents are corrupt or could not be read successfully. + /// + public RSA? GetRSAPublicKey() + { + if (_oid.Value != Oids.Rsa) + return null; + + RSA rsa = RSA.Create(); + + try + { + rsa.ImportSubjectPublicKeyInfo(ExportSubjectPublicKeyInfo(), out _); + return rsa; + } + catch + { + rsa.Dispose(); + throw; + } + } + + /// + /// Gets the public key, or if the key is not an DSA key. + /// + /// + /// The public key, or if the key is not an DSA key. + /// + /// + /// The key contents are corrupt or could not be read successfully. + /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("maccatalyst")] + [UnsupportedOSPlatform("tvos")] + public DSA? GetDSAPublicKey() + { + if (_oid.Value != Oids.Dsa) + return null; + + DSA dsa = DSA.Create(); + + try + { + dsa.ImportSubjectPublicKeyInfo(ExportSubjectPublicKeyInfo(), out _); + return dsa; + } + catch + { + dsa.Dispose(); + throw; + } + } + + /// + /// Gets the public key, or if the key is not an ECDsa key. + /// + /// + /// The public key, or if the key is not an ECDsa key. + /// + /// + /// The key contents are corrupt or could not be read successfully. + /// + public ECDsa? GetECDsaPublicKey() + { + if (_oid.Value != Oids.EcPublicKey) + return null; + + ECDsa ecdsa = ECDsa.Create(); + + try + { + ecdsa.ImportSubjectPublicKeyInfo(ExportSubjectPublicKeyInfo(), out _); + return ecdsa; + } + catch + { + ecdsa.Dispose(); + throw; + } + } + + /// + /// Gets the public key, or + /// if the key is not an ECDiffieHellman key. + /// + /// + /// The public key, or if the key is not an ECDiffieHellman key. + /// + /// + /// The key contents are corrupt or could not be read successfully. + /// + public ECDiffieHellman? GetECDiffieHellmanPublicKey() + { + if (_oid.Value != Oids.EcPublicKey) + return null; + + ECDiffieHellman ecdh = ECDiffieHellman.Create(); + + try + { + ecdh.ImportSubjectPublicKeyInfo(ExportSubjectPublicKeyInfo(), out _); + return ecdh; + } + catch + { + ecdh.Dispose(); + throw; + } + } + private AsnWriter EncodeSubjectPublicKeyInfo() { SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs index 0f19b3bd35610..b4a4487a1ce5f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs @@ -490,7 +490,7 @@ public static void TestECDsaPublicKey_BrainpoolP160r1_ValidatesSignature(byte[] Assert.Equal("1.2.840.10045.2.1", cert.PublicKey.Oid.Value); bool isSignatureValid = publicKey.VerifyData(helloBytes, existingSignature, HashAlgorithmName.SHA256); - + if (!isSignatureValid) { Assert.True(PlatformDetection.IsAndroid, "signature invalid on Android only"); @@ -823,6 +823,113 @@ public static void CreateFromSubjectPublicKeyInfo_AnyAlgorithm() Assert.Equal(spki.Length, read); } + [Fact] + public static void GetPublicKey_NullForDifferentAlgorithm() + { + byte[] spki = TestData.GostR3410SubjectPublicKeyInfo; + PublicKey key = PublicKey.CreateFromSubjectPublicKeyInfo(spki, out _); + + Assert.Null(key.GetRSAPublicKey()); + Assert.Null(key.GetECDsaPublicKey()); + Assert.Null(key.GetECDiffieHellmanPublicKey()); + } + + [Fact] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] + public static void GetDSAPublicKey_NullForDifferentAlgorithm() + { + byte[] spki = TestData.GostR3410SubjectPublicKeyInfo; + PublicKey key = PublicKey.CreateFromSubjectPublicKeyInfo(spki, out _); + + Assert.Null(key.GetDSAPublicKey()); + } + + [Fact] + public static void GetRSAPublicKey_ReturnsRsaKey() + { + PublicKey key = GetTestRsaKey(); + + using (RSA rsa = key.GetRSAPublicKey()) + { + Assert.NotNull(rsa); + Assert.Equal(rsa.ExportSubjectPublicKeyInfo(), key.ExportSubjectPublicKeyInfo()); + } + } + + [Fact] + public static void GetRSAPublicKey_ThrowsForCorruptKey() + { + AsnEncodedData badData = new AsnEncodedData(new byte[] { 1, 2, 3, 4 }); + PublicKey key = new PublicKey(GetTestRsaKey().Oid, badData, badData); + + Assert.ThrowsAny(() => key.GetRSAPublicKey()); + } + + [Fact] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] + public static void GetDSAPublicKey_ReturnsDsaKey() + { + PublicKey key = GetTestDsaKey(); + + using (DSA dsa = key.GetDSAPublicKey()) + { + Assert.NotNull(dsa); + Assert.Equal(dsa.ExportSubjectPublicKeyInfo(), key.ExportSubjectPublicKeyInfo()); + } + } + + [Fact] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] + public static void GetDSAPublicKey_ThrowsForCorruptKey() + { + AsnEncodedData badData = new AsnEncodedData(new byte[] { 1, 2, 3, 4 }); + PublicKey key = new PublicKey(GetTestDsaKey().Oid, badData, badData); + + Assert.ThrowsAny(() => key.GetDSAPublicKey()); + } + + [Fact] + public static void GetECDsaPublicKey_ReturnsECDsaKey() + { + PublicKey key = GetTestECDsaKey(); + + using (ECDsa ecdsa = key.GetECDsaPublicKey()) + { + Assert.NotNull(ecdsa); + Assert.Equal(ecdsa.ExportSubjectPublicKeyInfo(), key.ExportSubjectPublicKeyInfo()); + } + } + + [Fact] + public static void GetECDsaPublicKey_ThrowsForCorruptKey() + { + AsnEncodedData badData = new AsnEncodedData(new byte[] { 1, 2, 3, 4 }); + PublicKey key = new PublicKey(GetTestECDsaKey().Oid, badData, badData); + + Assert.ThrowsAny(() => key.GetECDsaPublicKey()); + } + + [Fact] + public static void GetECDiffieHellmanPublicKey_ReturnsECDHKey() + { + PublicKey key = GetTestECDHKey(); + + using (ECDiffieHellman ecdh = key.GetECDiffieHellmanPublicKey()) + { + Assert.NotNull(ecdh); + Assert.Equal(ecdh.ExportSubjectPublicKeyInfo(), key.ExportSubjectPublicKeyInfo()); + } + } + + [Fact] + public static void GetECDiffieHellmanPublicKey_ThrowsForCorruptKey() + { + AsnEncodedData badData = new AsnEncodedData(new byte[] { 1, 2, 3, 4 }); + PublicKey key = new PublicKey(GetTestECDHKey().Oid, badData, badData); + + Assert.ThrowsAny(() => key.GetECDiffieHellmanPublicKey()); + } + private static void TestKey_ECDsaCng(byte[] certBytes, TestData.ECDsaCngKeyValues expected) { using (X509Certificate2 cert = new X509Certificate2(certBytes))