Skip to content

Commit

Permalink
Implement Get*PublicKey methods on S.S.C.X509Certificates.PublicKey
Browse files Browse the repository at this point in the history
  • Loading branch information
vcsjones authored May 24, 2021
1 parent c8c4ead commit a38fee5
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<byte> destination, out int bytesWritten) { throw null; }
}
public static partial class RSACertificateExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -140,6 +141,122 @@ public static PublicKey CreateFromSubjectPublicKeyInfo(ReadOnlySpan<byte> source
return new PublicKey(localOid, localParameters, localKeyValue);
}

/// <summary>
/// Gets the <see cref="RSA" /> public key, or <see langword="null" /> if the key is not an RSA key.
/// </summary>
/// <returns>
/// The public key, or <see langword="null" /> if the key is not an RSA key.
/// </returns>
/// <exception cref="CryptographicException">
/// The key contents are corrupt or could not be read successfully.
/// </exception>
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;
}
}

/// <summary>
/// Gets the <see cref="DSA" /> public key, or <see langword="null" /> if the key is not an DSA key.
/// </summary>
/// <returns>
/// The public key, or <see langword="null" /> if the key is not an DSA key.
/// </returns>
/// <exception cref="CryptographicException">
/// The key contents are corrupt or could not be read successfully.
/// </exception>
[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;
}
}

/// <summary>
/// Gets the <see cref="ECDsa" /> public key, or <see langword="null" /> if the key is not an ECDsa key.
/// </summary>
/// <returns>
/// The public key, or <see langword="null" /> if the key is not an ECDsa key.
/// </returns>
/// <exception cref="CryptographicException">
/// The key contents are corrupt or could not be read successfully.
/// </exception>
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;
}
}

/// <summary>
/// Gets the <see cref="ECDiffieHellman" /> public key, or <see langword="null" />
/// if the key is not an ECDiffieHellman key.
/// </summary>
/// <returns>
/// The public key, or <see langword="null" /> if the key is not an ECDiffieHellman key.
/// </returns>
/// <exception cref="CryptographicException">
/// The key contents are corrupt or could not be read successfully.
/// </exception>
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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<CryptographicException>(() => 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<CryptographicException>(() => 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<CryptographicException>(() => 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<CryptographicException>(() => key.GetECDiffieHellmanPublicKey());
}

private static void TestKey_ECDsaCng(byte[] certBytes, TestData.ECDsaCngKeyValues expected)
{
using (X509Certificate2 cert = new X509Certificate2(certBytes))
Expand Down

0 comments on commit a38fee5

Please sign in to comment.