From 8d54e875b604285d445f61a704d05e633c1a4204 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 11 Nov 2021 22:13:49 +0000 Subject: [PATCH 1/6] Implement PEM exports for RSA PKCS#1 and ECPrivateKey. --- .../EC/ECKeyPemTests.cs | 66 ++++++++ .../ECDiffieHellmanKeyPemTests.cs | 9 ++ .../ECDsa/ECDsaKeyPemTests.cs | 9 ++ .../RSA/RSAKeyPemTests.cs | 132 +++++++++++++++ ...System.Security.Cryptography.Algorithms.cs | 8 + .../Security/Cryptography/ECDiffieHellman.cs | 82 ++++++++++ .../src/System/Security/Cryptography/ECDsa.cs | 82 ++++++++++ .../src/System/Security/Cryptography/RSA.cs | 153 ++++++++++++++++++ 8 files changed, 541 insertions(+) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs index be7670c006da64..f9d442e6073da5 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs @@ -16,6 +16,9 @@ public abstract class ECKeyPemTests where TAlg : AsymmetricAlgorithm protected abstract TAlg CreateKey(); protected abstract ECParameters ExportParameters(TAlg key, bool includePrivateParameters); + protected abstract void ImportParameters(TAlg key, ECParameters ecParameters); + protected abstract string ExportECPrivateKeyPem(TAlg key); + protected abstract bool TryExportECPrivateKeyPem(TAlg key, Span destination, out int charsWritten); [Fact] public void ImportFromPem_NoPem() @@ -434,5 +437,68 @@ public void ImportFromEncryptedPem_NoPem() Assert.Contains(NoPemExceptionMarker, ae.Message); } } + + [Fact] + public void ExportPem_ExportECPrivateKeyPem() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49\n" + + "AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS\n" + + "NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ==\n" + + "-----END EC PRIVATE KEY-----"; + + using (TAlg key = CreateKey()) + { + ImportParameters(key, EccTestData.GetNistP256ReferenceKey()); + Assert.Equal(expectedPem, ExportECPrivateKeyPem(key)); + } + } + + [Fact] + public void ExportPem_TryExportECPrivateKeyPem() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49\n" + + "AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS\n" + + "NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ==\n" + + "-----END EC PRIVATE KEY-----"; + + using (TAlg key = CreateKey()) + { + ImportParameters(key, EccTestData.GetNistP256ReferenceKey()); + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = TryExportECPrivateKeyPem(key, buffer, out written); + Assert.False(result, nameof(TryExportECPrivateKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = TryExportECPrivateKeyPem(key, buffer, out written); + Assert.True(result, nameof(TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = TryExportECPrivateKeyPem(key, bufferSpan, out written); + Assert.True(result, nameof(TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs index a03cdb0c766d0e..3ebe9ee87c9daf 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs @@ -10,7 +10,16 @@ namespace System.Security.Cryptography.EcDsa.Tests public sealed class ECDiffieHellmanKeyPemTests : ECKeyPemTests { protected override ECDiffieHellman CreateKey() => ECDiffieHellman.Create(); + protected override ECParameters ExportParameters(ECDiffieHellman key, bool includePrivateParameters) => key.ExportParameters(includePrivateParameters); + + protected override void ImportParameters(ECDiffieHellman key, ECParameters ecParameters) => + key.ImportParameters(ecParameters); + + protected override string ExportECPrivateKeyPem(ECDiffieHellman key) => key.ExportECPrivateKeyPem(); + + protected override bool TryExportECPrivateKeyPem(ECDiffieHellman key, Span destination, out int charsWritten) => + key.TryExportECPrivateKeyPem(destination, out charsWritten); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs index b969afa0fe4550..00a567fb48681b 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs @@ -10,7 +10,16 @@ namespace System.Security.Cryptography.EcDsa.Tests public sealed class ECDsaKeyPemTests : ECKeyPemTests { protected override ECDsa CreateKey() => ECDsa.Create(); + protected override ECParameters ExportParameters(ECDsa key, bool includePrivateParameters) => key.ExportParameters(includePrivateParameters); + + protected override void ImportParameters(ECDsa key, ECParameters ecParameters) => + key.ImportParameters(ecParameters); + + protected override string ExportECPrivateKeyPem(ECDsa key) => key.ExportECPrivateKeyPem(); + + protected override bool TryExportECPrivateKeyPem(ECDsa key, Span destination, out int charsWritten) => + key.TryExportECPrivateKeyPem(destination, out charsWritten); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs index 806e996cf3a7c1..ec09b630886394 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -518,6 +518,138 @@ public static void ImportFromEncryptedPem_Pkcs8Encrypted_Char_NoPem() } } + [Fact] + public static void ExportPem_ExportRSAPublicKeyPem() + { + string expectedPem = + "-----BEGIN RSA PUBLIC KEY-----\n" + + "MEgCQQC3P1n17ovVXiS3/wKa0WqFQ8ltJT5UMZuTUyxBw8FHe4nbLS8z17modFhI\n" + + "4GqOaDtQRFEeG8o2JSfhfPQrOAYVAgMBAAE=\n" + + "-----END RSA PUBLIC KEY-----"; + + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.DiminishedDPParameters.ToPublic()); + Assert.Equal(expectedPem, rsa.ExportRSAPublicKeyPem()); + } + } + + [Fact] + public static void ExportPem_TryExportRSAPublicKeyPem() + { + string expectedPem = + "-----BEGIN RSA PUBLIC KEY-----\n" + + "MEgCQQC3P1n17ovVXiS3/wKa0WqFQ8ltJT5UMZuTUyxBw8FHe4nbLS8z17modFhI\n" + + "4GqOaDtQRFEeG8o2JSfhfPQrOAYVAgMBAAE=\n" + + "-----END RSA PUBLIC KEY-----"; + + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.DiminishedDPParameters.ToPublic()); + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = rsa.TryExportRSAPublicKeyPem(buffer, out written); + Assert.False(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = rsa.TryExportRSAPublicKeyPem(buffer, out written); + Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = rsa.TryExportRSAPublicKeyPem(bufferSpan, out written); + Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + + [Fact] + public static void ExportPem_ExportRSAPrivateKeyPem() + { + string expectedPem = + "-----BEGIN RSA PRIVATE KEY-----\n" + + "MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX\n" + + "uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh\n" + + "Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F\n" + + "9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7\n" + + "Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk\n" + + "rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC\n" + + "yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw==\n" + + "-----END RSA PRIVATE KEY-----"; + + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.DiminishedDPParameters); + Assert.Equal(expectedPem, rsa.ExportRSAPrivateKeyPem()); + } + } + + [Fact] + public static void ExportPem_TryExportRSAPrivatePem() + { + string expectedPem = + "-----BEGIN RSA PRIVATE KEY-----\n" + + "MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX\n" + + "uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh\n" + + "Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F\n" + + "9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7\n" + + "Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk\n" + + "rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC\n" + + "yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw==\n" + + "-----END RSA PRIVATE KEY-----"; + + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.DiminishedDPParameters); + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); + Assert.False(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); + Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = rsa.TryExportRSAPrivateKeyPem(bufferSpan, out written); + Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + private static RSAParameters ToPublic(this RSAParameters rsaParams) { return new RSAParameters diff --git a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index 86b9871075b1fd..e82baf6c4857a0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -322,6 +322,7 @@ protected ECDiffieHellman() { } public virtual byte[] DeriveKeyMaterial(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey) { throw null; } public virtual byte[] DeriveKeyTls(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed) { throw null; } public virtual byte[] ExportECPrivateKey() { throw null; } + public string ExportECPrivateKeyPem() { throw null; } public virtual System.Security.Cryptography.ECParameters ExportExplicitParameters(bool includePrivateParameters) { throw null; } public virtual System.Security.Cryptography.ECParameters ExportParameters(bool includePrivateParameters) { throw null; } public override void FromXmlString(string xmlString) { } @@ -337,6 +338,7 @@ public virtual void ImportParameters(System.Security.Cryptography.ECParameters p public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override string ToXmlString(bool includePrivateParameters) { throw null; } public virtual bool TryExportECPrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public bool TryExportECPrivateKeyPem(System.Span destination, out int charsWritten) { throw null; } public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } @@ -367,6 +369,7 @@ protected ECDsa() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The default algorithm implementations might be removed, use strong type references like 'RSA.Create()' instead.")] public static new System.Security.Cryptography.ECDsa? Create(string algorithm) { throw null; } public virtual byte[] ExportECPrivateKey() { throw null; } + public string ExportECPrivateKeyPem() { throw null; } public virtual System.Security.Cryptography.ECParameters ExportExplicitParameters(bool includePrivateParameters) { throw null; } public virtual System.Security.Cryptography.ECParameters ExportParameters(bool includePrivateParameters) { throw null; } public override void FromXmlString(string xmlString) { } @@ -396,6 +399,7 @@ public virtual void ImportParameters(System.Security.Cryptography.ECParameters p protected virtual byte[] SignHashCore(System.ReadOnlySpan hash, System.Security.Cryptography.DSASignatureFormat signatureFormat) { throw null; } public override string ToXmlString(bool includePrivateParameters) { throw null; } public virtual bool TryExportECPrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public bool TryExportECPrivateKeyPem(System.Span destination, out int charsWritten) { throw null; } public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } @@ -692,7 +696,9 @@ protected RSA() { } public virtual byte[] EncryptValue(byte[] rgb) { throw null; } public abstract System.Security.Cryptography.RSAParameters ExportParameters(bool includePrivateParameters); public virtual byte[] ExportRSAPrivateKey() { throw null; } + public string ExportRSAPrivateKeyPem() { throw null; } public virtual byte[] ExportRSAPublicKey() { throw null; } + public string ExportRSAPublicKeyPem() { throw null; } public override void FromXmlString(string xmlString) { } protected virtual byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } @@ -717,7 +723,9 @@ public override void ImportFromPem(System.ReadOnlySpan input) { } public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } public virtual bool TryExportRSAPrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public bool TryExportRSAPrivateKeyPem(System.Span destination, out int charsWritten) { throw null; } public virtual bool TryExportRSAPublicKey(System.Span destination, out int bytesWritten) { throw null; } + public bool TryExportRSAPublicKeyPem(System.Span destination, out int charsWritten) { throw null; } public override bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } protected virtual bool TryHashData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; } public virtual bool TrySignData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding, out int bytesWritten) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs index 3cc29e9bd87254..d810a7f6eaf54d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs @@ -631,5 +631,87 @@ public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySp // override remains for compatibility. base.ImportFromEncryptedPem(input, passwordBytes); } + + /// + /// Exports the current key in the ECPrivateKey format, PEM encoded. + /// + /// A string containing the PEM-encoded ECPrivateKey. + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded ECPrivateKey will begin with -----BEGIN EC PRIVATE KEY----- + /// and end with -----END EC PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public unsafe string ExportECPrivateKeyPem() + { + byte[] exported = ExportECPrivateKey(); + + // Fixed to prevent GC moves. + fixed (byte* pExported = exported) + { + try + { + return PemKeyHelpers.CreatePemFromData(PemLabels.EcPrivateKey, exported); + } + finally + { + CryptographicOperations.ZeroMemory(exported); + } + } + } + + /// + /// Attempts to export the current key in the PEM-encoded + /// ECPrivateKey format into a provided buffer. + /// + /// + /// The character span to receive the PEM-encoded ECPrivateKey data. + /// + /// + /// When this method returns, contains a value that indicates the number + /// of characters written to . This + /// parameter is treated as uninitialized. + /// + /// + /// if is big enough + /// to receive the output; otherwise, . + /// + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded ECPrivateKey will begin with + /// -----BEGIN EC PRIVATE KEY----- and end with + /// -----END EC PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public bool TryExportECPrivateKeyPem(Span destination, out int charsWritten) + { + static bool Export(ECDiffieHellman alg, Span destination, out int bytesWritten) + { + return alg.TryExportECPrivateKey(destination, out bytesWritten); + } + + return PemKeyHelpers.TryExportToPem( + this, + PemLabels.EcPrivateKey, + Export, + destination, + out charsWritten); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs index 77d87b102f2547..4701f5d539edc5 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs @@ -1495,5 +1495,87 @@ public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySp // override remains for compatibility. base.ImportFromEncryptedPem(input, passwordBytes); } + + /// + /// Exports the current key in the ECPrivateKey format, PEM encoded. + /// + /// A string containing the PEM-encoded ECPrivateKey. + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded ECPrivateKey will begin with -----BEGIN EC PRIVATE KEY----- + /// and end with -----END EC PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public unsafe string ExportECPrivateKeyPem() + { + byte[] exported = ExportECPrivateKey(); + + // Fixed to prevent GC moves. + fixed (byte* pExported = exported) + { + try + { + return PemKeyHelpers.CreatePemFromData(PemLabels.EcPrivateKey, exported); + } + finally + { + CryptographicOperations.ZeroMemory(exported); + } + } + } + + /// + /// Attempts to export the current key in the PEM-encoded + /// ECPrivateKey format into a provided buffer. + /// + /// + /// The character span to receive the PEM-encoded ECPrivateKey data. + /// + /// + /// When this method returns, contains a value that indicates the number + /// of characters written to . This + /// parameter is treated as uninitialized. + /// + /// + /// if is big enough + /// to receive the output; otherwise, . + /// + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded ECPrivateKey will begin with + /// -----BEGIN EC PRIVATE KEY----- and end with + /// -----END EC PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public bool TryExportECPrivateKeyPem(Span destination, out int charsWritten) + { + static bool Export(ECDsa alg, Span destination, out int bytesWritten) + { + return alg.TryExportECPrivateKey(destination, out bytesWritten); + } + + return PemKeyHelpers.TryExportToPem( + this, + PemLabels.EcPrivateKey, + Export, + destination, + out charsWritten); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs index 9aa15207646682..3a756837442c94 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs @@ -846,6 +846,159 @@ public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySp base.ImportFromEncryptedPem(input, passwordBytes); } + /// + /// Exports the current key in the PKCS#1 RSAPrivateKey format, PEM encoded. + /// + /// A string containing the PEM-encoded PKCS#1 RSAPrivateKey. + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded PKCS#1 RSAPrivateKey will begin with -----BEGIN RSA PRIVATE KEY----- + /// and end with -----END RSA PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public unsafe string ExportRSAPrivateKeyPem() + { + byte[] exported = ExportRSAPrivateKey(); + + // Fixed to prevent GC moves. + fixed (byte* pExported = exported) + { + try + { + return PemKeyHelpers.CreatePemFromData(PemLabels.RsaPrivateKey, exported); + } + finally + { + CryptographicOperations.ZeroMemory(exported); + } + } + } + + /// + /// Exports the public-key portion of the current key in the PKCS#1 + /// RSAPublicKey format, PEM encoded. + /// + /// A string containing the PEM-encoded PKCS#1 RSAPublicKey. + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded PKCS#1 RSAPublicKey will begin with -----BEGIN RSA PUBLIC KEY----- + /// and end with -----END RSA PUBLIC KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public string ExportRSAPublicKeyPem() + { + byte[] exported = ExportRSAPublicKey(); + return PemKeyHelpers.CreatePemFromData(PemLabels.RsaPublicKey, exported); + } + + /// + /// Attempts to export the current key in the PEM-encoded PKCS#1 + /// RSAPrivateKey format into a provided buffer. + /// + /// + /// The character span to receive the PEM-encoded PKCS#1 RSAPrivateKey data. + /// + /// + /// When this method returns, contains a value that indicates the number + /// of characters written to . This + /// parameter is treated as uninitialized. + /// + /// + /// if is big enough + /// to receive the output; otherwise, . + /// + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded PKCS#1 RSAPrivateKey will begin with + /// -----BEGIN RSA PRIVATE KEY----- and end with + /// -----END RSA PRIVATE KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public bool TryExportRSAPrivateKeyPem(Span destination, out int charsWritten) + { + static bool Export(RSA alg, Span destination, out int bytesWritten) + { + return alg.TryExportRSAPrivateKey(destination, out bytesWritten); + } + + return PemKeyHelpers.TryExportToPem( + this, + PemLabels.RsaPrivateKey, + Export, + destination, + out charsWritten); + } + + /// + /// Attempts to export the current key in the PEM-encoded PKCS#1 + /// RSAPublicKey format into a provided buffer. + /// + /// + /// The character span to receive the PEM-encoded PKCS#1 RSAPublicKey data. + /// + /// + /// When this method returns, contains a value that indicates the number + /// of characters written to . This + /// parameter is treated as uninitialized. + /// + /// + /// if is big enough + /// to receive the output; otherwise, . + /// + /// + /// The key could not be exported. + /// + /// + ///

+ /// A PEM-encoded PKCS#1 RSAPublicKey will begin with + /// -----BEGIN RSA PUBLIC KEY----- and end with + /// -----END RSA PUBLIC KEY-----, with the base64 encoded DER + /// contents of the key between the PEM boundaries. + ///

+ ///

+ /// The PEM is encoded according to the IETF RFC 7468 "strict" + /// encoding rules. + ///

+ ///
+ public bool TryExportRSAPublicKeyPem(Span destination, out int charsWritten) + { + static bool Export(RSA alg, Span destination, out int bytesWritten) + { + return alg.TryExportRSAPublicKey(destination, out bytesWritten); + } + + return PemKeyHelpers.TryExportToPem( + this, + PemLabels.RsaPublicKey, + Export, + destination, + out charsWritten); + } + private static void ClearPrivateParameters(in RSAParameters rsaParameters) { CryptographicOperations.ZeroMemory(rsaParameters.D); From c2a7895886e2f5583e43e0ab2eac59a9138fba7b Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 12 Nov 2021 01:43:36 +0000 Subject: [PATCH 2/6] Use different RSA key for tests. The diminished DP/DQ values are exported different on Windows 7. Use a key that will behave the same on all platforms. --- .../RSA/RSAKeyPemTests.cs | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs index ec09b630886394..354088f3a88920 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -523,13 +523,14 @@ public static void ExportPem_ExportRSAPublicKeyPem() { string expectedPem = "-----BEGIN RSA PUBLIC KEY-----\n" + - "MEgCQQC3P1n17ovVXiS3/wKa0WqFQ8ltJT5UMZuTUyxBw8FHe4nbLS8z17modFhI\n" + - "4GqOaDtQRFEeG8o2JSfhfPQrOAYVAgMBAAE=\n" + + "MIGJAoGBAJ8FH85xyi4XD0PGBEpvt4TYrWJb24dLBa0Ddhlj7iqdzH2vOlEjrn8Z\n" + + "pmO/ZWteNxxqCqfwgrG9wyFQISPvQDCTERFc91tCchje/hv2DgK/KFb7fLXxiM1m\n" + + "j8HKuvmDfglb0pG5FlwrDI6Ru8OEDXPuojCCLzyQUIinkpaZDGi9AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.DiminishedDPParameters.ToPublic()); + rsa.ImportParameters(TestData.RSA1024Params.ToPublic()); Assert.Equal(expectedPem, rsa.ExportRSAPublicKeyPem()); } } @@ -539,13 +540,14 @@ public static void ExportPem_TryExportRSAPublicKeyPem() { string expectedPem = "-----BEGIN RSA PUBLIC KEY-----\n" + - "MEgCQQC3P1n17ovVXiS3/wKa0WqFQ8ltJT5UMZuTUyxBw8FHe4nbLS8z17modFhI\n" + - "4GqOaDtQRFEeG8o2JSfhfPQrOAYVAgMBAAE=\n" + + "MIGJAoGBAJ8FH85xyi4XD0PGBEpvt4TYrWJb24dLBa0Ddhlj7iqdzH2vOlEjrn8Z\n" + + "pmO/ZWteNxxqCqfwgrG9wyFQISPvQDCTERFc91tCchje/hv2DgK/KFb7fLXxiM1m\n" + + "j8HKuvmDfglb0pG5FlwrDI6Ru8OEDXPuojCCLzyQUIinkpaZDGi9AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.DiminishedDPParameters.ToPublic()); + rsa.ImportParameters(TestData.RSA1024Params.ToPublic()); int written; bool result; @@ -584,18 +586,24 @@ public static void ExportPem_ExportRSAPrivateKeyPem() { string expectedPem = "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX\n" + - "uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh\n" + - "Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F\n" + - "9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7\n" + - "Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk\n" + - "rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC\n" + - "yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw==\n" + + "MIICXAIBAAKBgQCfBR/OccouFw9DxgRKb7eE2K1iW9uHSwWtA3YZY+4qncx9rzpR\n" + + "I65/GaZjv2VrXjccagqn8IKxvcMhUCEj70AwkxERXPdbQnIY3v4b9g4CvyhW+3y1\n" + + "8YjNZo/Byrr5g34JW9KRuRZcKwyOkbvDhA1z7qIwgi88kFCIp5KWmQxovQIDAQAB\n" + + "AoGASub5f97mWkZfWM+NVth/a3I6XSGianxCfKesObJxzR4N48elYvG5MEIPN13A\n" + + "ck3rDJXAVjF6Bim5n1fkfE4l/90RDtnowE+YxhPXJH48PrTHhY/VlAsSVPigC2Fv\n" + + "3eibkZ0qbxWZjJG5Pe9lx3imV1LEF1v8qGVOHpWCaxVJbGsCQQDSSwkDkimV52WU\n" + + "GU8g0rf2ubB4H7fC3bRotf190ZK6qFAe3ER5vyEvwrWgCA7OgAw39mDqu1d+pXSC\n" + + "ip+nW0IDAkEAwZUxGvTlpB+shkg1m35y+YIXKoYKPTHM0Rwju2SQ3x9Wp0gknq8Y\n" + + "4wy25nvkH/cZbU7bqVDN1ihi72VySKkOPwJAd813nSkvt87T98NTaQei9lRjTIwF\n" + + "TGax2NWVTJCQXvZ0bqBeAl34shTjFACDLvGUBG3AWPnRprzr21LOEbHTsQJBAK5S\n" + + "Mw4bSlApVar2i4+iptaYl1PrsHy6w73qoSK2xN6n0diB1rgu5TJQ2MNk/WDrmzIb\n" + + "uSMXaMRZSf5aVDeqRPECQCo5r+J2BkiPSZPfAc17ssZoFa8OyfHgFT2paCfWZB4I\n" + + "3AE7ODJiT80MCfeaa8oPdsL73Xxd8YUWZ5+Yp/NAfRw=\n" + "-----END RSA PRIVATE KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.DiminishedDPParameters); + rsa.ImportParameters(TestData.RSA1024Params); Assert.Equal(expectedPem, rsa.ExportRSAPrivateKeyPem()); } } @@ -605,18 +613,24 @@ public static void ExportPem_TryExportRSAPrivatePem() { string expectedPem = "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX\n" + - "uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh\n" + - "Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F\n" + - "9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7\n" + - "Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk\n" + - "rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC\n" + - "yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw==\n" + + "MIICXAIBAAKBgQCfBR/OccouFw9DxgRKb7eE2K1iW9uHSwWtA3YZY+4qncx9rzpR\n" + + "I65/GaZjv2VrXjccagqn8IKxvcMhUCEj70AwkxERXPdbQnIY3v4b9g4CvyhW+3y1\n" + + "8YjNZo/Byrr5g34JW9KRuRZcKwyOkbvDhA1z7qIwgi88kFCIp5KWmQxovQIDAQAB\n" + + "AoGASub5f97mWkZfWM+NVth/a3I6XSGianxCfKesObJxzR4N48elYvG5MEIPN13A\n" + + "ck3rDJXAVjF6Bim5n1fkfE4l/90RDtnowE+YxhPXJH48PrTHhY/VlAsSVPigC2Fv\n" + + "3eibkZ0qbxWZjJG5Pe9lx3imV1LEF1v8qGVOHpWCaxVJbGsCQQDSSwkDkimV52WU\n" + + "GU8g0rf2ubB4H7fC3bRotf190ZK6qFAe3ER5vyEvwrWgCA7OgAw39mDqu1d+pXSC\n" + + "ip+nW0IDAkEAwZUxGvTlpB+shkg1m35y+YIXKoYKPTHM0Rwju2SQ3x9Wp0gknq8Y\n" + + "4wy25nvkH/cZbU7bqVDN1ihi72VySKkOPwJAd813nSkvt87T98NTaQei9lRjTIwF\n" + + "TGax2NWVTJCQXvZ0bqBeAl34shTjFACDLvGUBG3AWPnRprzr21LOEbHTsQJBAK5S\n" + + "Mw4bSlApVar2i4+iptaYl1PrsHy6w73qoSK2xN6n0diB1rgu5TJQ2MNk/WDrmzIb\n" + + "uSMXaMRZSf5aVDeqRPECQCo5r+J2BkiPSZPfAc17ssZoFa8OyfHgFT2paCfWZB4I\n" + + "3AE7ODJiT80MCfeaa8oPdsL73Xxd8YUWZ5+Yp/NAfRw=\n" + "-----END RSA PRIVATE KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.DiminishedDPParameters); + rsa.ImportParameters(TestData.RSA1024Params); int written; bool result; From 0e74ee2de850e067d421b0ddcb8c37b51273743e Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 11 Nov 2021 23:15:54 -0500 Subject: [PATCH 3/6] Use RSA parameters that CNG and Unix agree on encoding --- .../RSA/RSAKeyPemTests.cs | 60 +++++------ .../AlgorithmImplementations/RSA/TestData.cs | 99 +++++++++++++++++++ 2 files changed, 129 insertions(+), 30 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs index 354088f3a88920..a93b8fde9f5e90 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -548,7 +548,7 @@ public static void ExportPem_TryExportRSAPublicKeyPem() using (RSA rsa = RSAFactory.Create()) { rsa.ImportParameters(TestData.RSA1024Params.ToPublic()); - + int written; bool result; char[] buffer; @@ -586,24 +586,24 @@ public static void ExportPem_ExportRSAPrivateKeyPem() { string expectedPem = "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIICXAIBAAKBgQCfBR/OccouFw9DxgRKb7eE2K1iW9uHSwWtA3YZY+4qncx9rzpR\n" + - "I65/GaZjv2VrXjccagqn8IKxvcMhUCEj70AwkxERXPdbQnIY3v4b9g4CvyhW+3y1\n" + - "8YjNZo/Byrr5g34JW9KRuRZcKwyOkbvDhA1z7qIwgi88kFCIp5KWmQxovQIDAQAB\n" + - "AoGASub5f97mWkZfWM+NVth/a3I6XSGianxCfKesObJxzR4N48elYvG5MEIPN13A\n" + - "ck3rDJXAVjF6Bim5n1fkfE4l/90RDtnowE+YxhPXJH48PrTHhY/VlAsSVPigC2Fv\n" + - "3eibkZ0qbxWZjJG5Pe9lx3imV1LEF1v8qGVOHpWCaxVJbGsCQQDSSwkDkimV52WU\n" + - "GU8g0rf2ubB4H7fC3bRotf190ZK6qFAe3ER5vyEvwrWgCA7OgAw39mDqu1d+pXSC\n" + - "ip+nW0IDAkEAwZUxGvTlpB+shkg1m35y+YIXKoYKPTHM0Rwju2SQ3x9Wp0gknq8Y\n" + - "4wy25nvkH/cZbU7bqVDN1ihi72VySKkOPwJAd813nSkvt87T98NTaQei9lRjTIwF\n" + - "TGax2NWVTJCQXvZ0bqBeAl34shTjFACDLvGUBG3AWPnRprzr21LOEbHTsQJBAK5S\n" + - "Mw4bSlApVar2i4+iptaYl1PrsHy6w73qoSK2xN6n0diB1rgu5TJQ2MNk/WDrmzIb\n" + - "uSMXaMRZSf5aVDeqRPECQCo5r+J2BkiPSZPfAc17ssZoFa8OyfHgFT2paCfWZB4I\n" + - "3AE7ODJiT80MCfeaa8oPdsL73Xxd8YUWZ5+Yp/NAfRw=\n" + + "MIICXAIBAAKBgQDO7Bf6EsX3Ynlnh9xugIk1x9CRWFd/hf7V5cl4PWrlH/WBPbAU\n" + + "6zMF46PVJ9qFezS6DvaM7RWig7UCktfvwFn2iNK1x3wymzIxQujI9fSG5YNh4cq6\n" + + "K2Kaco4HyA4D0xKsT2S6Tz3rVHZ0ZDYFSvbFeZ5UVESvZBXxgIwt+VMBtQIDAQAB\n" + + "AoGBAIm3dJXJnNxzO939/GXHQor6vAOgSkvjgKIX16khJ13rkVg9GE4z47eNtGKP\n" + + "rc6dG5ChmNJekMGi6vwVg4Zb/QL3kQnV2f5rff5J9WhHMqX4/1xrRTZSf4+P7MRk\n" + + "6tjqvw/WDDZcZVRgVNcyZQhmegEaHzVrdAmzHEIDQKrb7bvZAkEA8G54/N6sHUNI\n" + + "ykSVdovZzgIZVy3bFqIQ/Iw6AqBL8P16MSp/hB9DPOLHGt6M8Gexvsv72kXNjHLD\n" + + "mO5iKP/VjwJBANxSJvz/O6/aiyD4U/ZS6xJtHtToDrR2tvAkT/xYPPxdav7HxHHR\n" + + "GOkfEliEll8TZLMbmFozKF7x2rfueP8rOnsCQF4wSQF3EiIcm8VxfMQGIXtM4zR9\n" + + "6FhDgTgOjyM8PMPZ6SadqXrO7H5LsQhbqwb6+GzwaGen9NBSnUGqEZZSMw0CQDOa\n" + + "clNTnLFFqMyiHVhWd4SrVktbxwXxwEti1fEu4aniX4spUxOte/4uLufYtjeynSVu\n" + + "UzLdRJloYGgwTNrHTz0CQHK2xOnRoauKfVVCWxeTPNZQIXAOv7gIpxgQOprroUzm\n" + + "C5Gqw+8rVETHgQ9xIKSEWq03v2psbhcvj4YlJAfL8q0=\n" + "-----END RSA PRIVATE KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.RSA1024Params); + rsa.ImportParameters(TestData.RSA1024FullPrivateExponent); Assert.Equal(expectedPem, rsa.ExportRSAPrivateKeyPem()); } } @@ -613,25 +613,25 @@ public static void ExportPem_TryExportRSAPrivatePem() { string expectedPem = "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIICXAIBAAKBgQCfBR/OccouFw9DxgRKb7eE2K1iW9uHSwWtA3YZY+4qncx9rzpR\n" + - "I65/GaZjv2VrXjccagqn8IKxvcMhUCEj70AwkxERXPdbQnIY3v4b9g4CvyhW+3y1\n" + - "8YjNZo/Byrr5g34JW9KRuRZcKwyOkbvDhA1z7qIwgi88kFCIp5KWmQxovQIDAQAB\n" + - "AoGASub5f97mWkZfWM+NVth/a3I6XSGianxCfKesObJxzR4N48elYvG5MEIPN13A\n" + - "ck3rDJXAVjF6Bim5n1fkfE4l/90RDtnowE+YxhPXJH48PrTHhY/VlAsSVPigC2Fv\n" + - "3eibkZ0qbxWZjJG5Pe9lx3imV1LEF1v8qGVOHpWCaxVJbGsCQQDSSwkDkimV52WU\n" + - "GU8g0rf2ubB4H7fC3bRotf190ZK6qFAe3ER5vyEvwrWgCA7OgAw39mDqu1d+pXSC\n" + - "ip+nW0IDAkEAwZUxGvTlpB+shkg1m35y+YIXKoYKPTHM0Rwju2SQ3x9Wp0gknq8Y\n" + - "4wy25nvkH/cZbU7bqVDN1ihi72VySKkOPwJAd813nSkvt87T98NTaQei9lRjTIwF\n" + - "TGax2NWVTJCQXvZ0bqBeAl34shTjFACDLvGUBG3AWPnRprzr21LOEbHTsQJBAK5S\n" + - "Mw4bSlApVar2i4+iptaYl1PrsHy6w73qoSK2xN6n0diB1rgu5TJQ2MNk/WDrmzIb\n" + - "uSMXaMRZSf5aVDeqRPECQCo5r+J2BkiPSZPfAc17ssZoFa8OyfHgFT2paCfWZB4I\n" + - "3AE7ODJiT80MCfeaa8oPdsL73Xxd8YUWZ5+Yp/NAfRw=\n" + + "MIICXAIBAAKBgQDO7Bf6EsX3Ynlnh9xugIk1x9CRWFd/hf7V5cl4PWrlH/WBPbAU\n" + + "6zMF46PVJ9qFezS6DvaM7RWig7UCktfvwFn2iNK1x3wymzIxQujI9fSG5YNh4cq6\n" + + "K2Kaco4HyA4D0xKsT2S6Tz3rVHZ0ZDYFSvbFeZ5UVESvZBXxgIwt+VMBtQIDAQAB\n" + + "AoGBAIm3dJXJnNxzO939/GXHQor6vAOgSkvjgKIX16khJ13rkVg9GE4z47eNtGKP\n" + + "rc6dG5ChmNJekMGi6vwVg4Zb/QL3kQnV2f5rff5J9WhHMqX4/1xrRTZSf4+P7MRk\n" + + "6tjqvw/WDDZcZVRgVNcyZQhmegEaHzVrdAmzHEIDQKrb7bvZAkEA8G54/N6sHUNI\n" + + "ykSVdovZzgIZVy3bFqIQ/Iw6AqBL8P16MSp/hB9DPOLHGt6M8Gexvsv72kXNjHLD\n" + + "mO5iKP/VjwJBANxSJvz/O6/aiyD4U/ZS6xJtHtToDrR2tvAkT/xYPPxdav7HxHHR\n" + + "GOkfEliEll8TZLMbmFozKF7x2rfueP8rOnsCQF4wSQF3EiIcm8VxfMQGIXtM4zR9\n" + + "6FhDgTgOjyM8PMPZ6SadqXrO7H5LsQhbqwb6+GzwaGen9NBSnUGqEZZSMw0CQDOa\n" + + "clNTnLFFqMyiHVhWd4SrVktbxwXxwEti1fEu4aniX4spUxOte/4uLufYtjeynSVu\n" + + "UzLdRJloYGgwTNrHTz0CQHK2xOnRoauKfVVCWxeTPNZQIXAOv7gIpxgQOprroUzm\n" + + "C5Gqw+8rVETHgQ9xIKSEWq03v2psbhcvj4YlJAfL8q0=\n" + "-----END RSA PRIVATE KEY-----"; using (RSA rsa = RSAFactory.Create()) { - rsa.ImportParameters(TestData.RSA1024Params); - + rsa.ImportParameters(TestData.RSA1024FullPrivateExponent); + int written; bool result; char[] buffer; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs index c5bbd6bed7c515..6b9069fcfd39fd 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs @@ -175,6 +175,105 @@ internal class TestData }, }; + // D and Modulus have their high bits set so they both encode to 1024-bit values. + public static readonly RSAParameters RSA1024FullPrivateExponent = new RSAParameters + { + Modulus = new byte[] + { + 0xCE, 0xEC, 0x17, 0xFA, 0x12, 0xC5, 0xF7, 0x62, + 0x79, 0x67, 0x87, 0xDC, 0x6E, 0x80, 0x89, 0x35, + 0xC7, 0xD0, 0x91, 0x58, 0x57, 0x7F, 0x85, 0xFE, + 0xD5, 0xE5, 0xC9, 0x78, 0x3D, 0x6A, 0xE5, 0x1F, + 0xF5, 0x81, 0x3D, 0xB0, 0x14, 0xEB, 0x33, 0x05, + 0xE3, 0xA3, 0xD5, 0x27, 0xDA, 0x85, 0x7B, 0x34, + 0xBA, 0x0E, 0xF6, 0x8C, 0xED, 0x15, 0xA2, 0x83, + 0xB5, 0x02, 0x92, 0xD7, 0xEF, 0xC0, 0x59, 0xF6, + 0x88, 0xD2, 0xB5, 0xC7, 0x7C, 0x32, 0x9B, 0x32, + 0x31, 0x42, 0xE8, 0xC8, 0xF5, 0xF4, 0x86, 0xE5, + 0x83, 0x61, 0xE1, 0xCA, 0xBA, 0x2B, 0x62, 0x9A, + 0x72, 0x8E, 0x07, 0xC8, 0x0E, 0x03, 0xD3, 0x12, + 0xAC, 0x4F, 0x64, 0xBA, 0x4F, 0x3D, 0xEB, 0x54, + 0x76, 0x74, 0x64, 0x36, 0x05, 0x4A, 0xF6, 0xC5, + 0x79, 0x9E, 0x54, 0x54, 0x44, 0xAF, 0x64, 0x15, + 0xF1, 0x80, 0x8C, 0x2D, 0xF9, 0x53, 0x01, 0xB5, + }, + Exponent = new byte[] { 0x01, 0x00, 0x01 }, + D = new byte[] + { + 0x89, 0xB7, 0x74, 0x95, 0xC9, 0x9C, 0xDC, 0x73, + 0x3B, 0xDD, 0xFD, 0xFC, 0x65, 0xC7, 0x42, 0x8A, + 0xFA, 0xBC, 0x03, 0xA0, 0x4A, 0x4B, 0xE3, 0x80, + 0xA2, 0x17, 0xD7, 0xA9, 0x21, 0x27, 0x5D, 0xEB, + 0x91, 0x58, 0x3D, 0x18, 0x4E, 0x33, 0xE3, 0xB7, + 0x8D, 0xB4, 0x62, 0x8F, 0xAD, 0xCE, 0x9D, 0x1B, + 0x90, 0xA1, 0x98, 0xD2, 0x5E, 0x90, 0xC1, 0xA2, + 0xEA, 0xFC, 0x15, 0x83, 0x86, 0x5B, 0xFD, 0x02, + 0xF7, 0x91, 0x09, 0xD5, 0xD9, 0xFE, 0x6B, 0x7D, + 0xFE, 0x49, 0xF5, 0x68, 0x47, 0x32, 0xA5, 0xF8, + 0xFF, 0x5C, 0x6B, 0x45, 0x36, 0x52, 0x7F, 0x8F, + 0x8F, 0xEC, 0xC4, 0x64, 0xEA, 0xD8, 0xEA, 0xBF, + 0x0F, 0xD6, 0x0C, 0x36, 0x5C, 0x65, 0x54, 0x60, + 0x54, 0xD7, 0x32, 0x65, 0x08, 0x66, 0x7A, 0x01, + 0x1A, 0x1F, 0x35, 0x6B, 0x74, 0x09, 0xB3, 0x1C, + 0x42, 0x03, 0x40, 0xAA, 0xDB, 0xED, 0xBB, 0xD9, + }, + P = new byte[] + { + 0xF0, 0x6E, 0x78, 0xFC, 0xDE, 0xAC, 0x1D, 0x43, + 0x48, 0xCA, 0x44, 0x95, 0x76, 0x8B, 0xD9, 0xCE, + 0x02, 0x19, 0x57, 0x2D, 0xDB, 0x16, 0xA2, 0x10, + 0xFC, 0x8C, 0x3A, 0x02, 0xA0, 0x4B, 0xF0, 0xFD, + 0x7A, 0x31, 0x2A, 0x7F, 0x84, 0x1F, 0x43, 0x3C, + 0xE2, 0xC7, 0x1A, 0xDE, 0x8C, 0xF0, 0x67, 0xB1, + 0xBE, 0xCB, 0xFB, 0xDA, 0x45, 0xCD, 0x8C, 0x72, + 0xC3, 0x98, 0xEE, 0x62, 0x28, 0xFF, 0xD5, 0x8F, + }, + DP = new byte[] + { + 0x5E, 0x30, 0x49, 0x01, 0x77, 0x12, 0x22, 0x1C, + 0x9B, 0xC5, 0x71, 0x7C, 0xC4, 0x06, 0x21, 0x7B, + 0x4C, 0xE3, 0x34, 0x7D, 0xE8, 0x58, 0x43, 0x81, + 0x38, 0x0E, 0x8F, 0x23, 0x3C, 0x3C, 0xC3, 0xD9, + 0xE9, 0x26, 0x9D, 0xA9, 0x7A, 0xCE, 0xEC, 0x7E, + 0x4B, 0xB1, 0x08, 0x5B, 0xAB, 0x06, 0xFA, 0xF8, + 0x6C, 0xF0, 0x68, 0x67, 0xA7, 0xF4, 0xD0, 0x52, + 0x9D, 0x41, 0xAA, 0x11, 0x96, 0x52, 0x33, 0x0D, + }, + Q = new byte[] + { + 0xDC, 0x52, 0x26, 0xFC, 0xFF, 0x3B, 0xAF, 0xDA, + 0x8B, 0x20, 0xF8, 0x53, 0xF6, 0x52, 0xEB, 0x12, + 0x6D, 0x1E, 0xD4, 0xE8, 0x0E, 0xB4, 0x76, 0xB6, + 0xF0, 0x24, 0x4F, 0xFC, 0x58, 0x3C, 0xFC, 0x5D, + 0x6A, 0xFE, 0xC7, 0xC4, 0x71, 0xD1, 0x18, 0xE9, + 0x1F, 0x12, 0x58, 0x84, 0x96, 0x5F, 0x13, 0x64, + 0xB3, 0x1B, 0x98, 0x5A, 0x33, 0x28, 0x5E, 0xF1, + 0xDA, 0xB7, 0xEE, 0x78, 0xFF, 0x2B, 0x3A, 0x7B, + }, + DQ = new byte[] + { + 0x33, 0x9A, 0x72, 0x53, 0x53, 0x9C, 0xB1, 0x45, + 0xA8, 0xCC, 0xA2, 0x1D, 0x58, 0x56, 0x77, 0x84, + 0xAB, 0x56, 0x4B, 0x5B, 0xC7, 0x05, 0xF1, 0xC0, + 0x4B, 0x62, 0xD5, 0xF1, 0x2E, 0xE1, 0xA9, 0xE2, + 0x5F, 0x8B, 0x29, 0x53, 0x13, 0xAD, 0x7B, 0xFE, + 0x2E, 0x2E, 0xE7, 0xD8, 0xB6, 0x37, 0xB2, 0x9D, + 0x25, 0x6E, 0x53, 0x32, 0xDD, 0x44, 0x99, 0x68, + 0x60, 0x68, 0x30, 0x4C, 0xDA, 0xC7, 0x4F, 0x3D, + }, + InverseQ = new byte[] + { + 0x72, 0xB6, 0xC4, 0xE9, 0xD1, 0xA1, 0xAB, 0x8A, + 0x7D, 0x55, 0x42, 0x5B, 0x17, 0x93, 0x3C, 0xD6, + 0x50, 0x21, 0x70, 0x0E, 0xBF, 0xB8, 0x08, 0xA7, + 0x18, 0x10, 0x3A, 0x9A, 0xEB, 0xA1, 0x4C, 0xE6, + 0x0B, 0x91, 0xAA, 0xC3, 0xEF, 0x2B, 0x54, 0x44, + 0xC7, 0x81, 0x0F, 0x71, 0x20, 0xA4, 0x84, 0x5A, + 0xAD, 0x37, 0xBF, 0x6A, 0x6C, 0x6E, 0x17, 0x2F, + 0x8F, 0x86, 0x25, 0x24, 0x07, 0xCB, 0xF2, 0xAD, + }, + }; + public static readonly RSAParameters RSA1032Parameters = new RSAParameters { Modulus = new byte[] From f0bb0b31715ca5d1ef5a072ad3a84f89335a3d76 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 12 Nov 2021 14:24:12 +0000 Subject: [PATCH 4/6] Scrap tests --- .../EC/ECKeyPemTests.cs | 66 -------- .../ECDiffieHellmanKeyPemTests.cs | 9 -- .../ECDsa/ECDsaKeyPemTests.cs | 9 -- .../RSA/RSAKeyPemTests.cs | 146 ------------------ .../AlgorithmImplementations/RSA/TestData.cs | 99 ------------ 5 files changed, 329 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs index f9d442e6073da5..be7670c006da64 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs @@ -16,9 +16,6 @@ public abstract class ECKeyPemTests where TAlg : AsymmetricAlgorithm protected abstract TAlg CreateKey(); protected abstract ECParameters ExportParameters(TAlg key, bool includePrivateParameters); - protected abstract void ImportParameters(TAlg key, ECParameters ecParameters); - protected abstract string ExportECPrivateKeyPem(TAlg key); - protected abstract bool TryExportECPrivateKeyPem(TAlg key, Span destination, out int charsWritten); [Fact] public void ImportFromPem_NoPem() @@ -437,68 +434,5 @@ public void ImportFromEncryptedPem_NoPem() Assert.Contains(NoPemExceptionMarker, ae.Message); } } - - [Fact] - public void ExportPem_ExportECPrivateKeyPem() - { - string expectedPem = - "-----BEGIN EC PRIVATE KEY-----\n" + - "MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49\n" + - "AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS\n" + - "NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ==\n" + - "-----END EC PRIVATE KEY-----"; - - using (TAlg key = CreateKey()) - { - ImportParameters(key, EccTestData.GetNistP256ReferenceKey()); - Assert.Equal(expectedPem, ExportECPrivateKeyPem(key)); - } - } - - [Fact] - public void ExportPem_TryExportECPrivateKeyPem() - { - string expectedPem = - "-----BEGIN EC PRIVATE KEY-----\n" + - "MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49\n" + - "AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS\n" + - "NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ==\n" + - "-----END EC PRIVATE KEY-----"; - - using (TAlg key = CreateKey()) - { - ImportParameters(key, EccTestData.GetNistP256ReferenceKey()); - - int written; - bool result; - char[] buffer; - - // buffer not enough - buffer = new char[expectedPem.Length - 1]; - result = TryExportECPrivateKeyPem(key, buffer, out written); - Assert.False(result, nameof(TryExportECPrivateKeyPem)); - Assert.Equal(0, written); - - // buffer just enough - buffer = new char[expectedPem.Length]; - result = TryExportECPrivateKeyPem(key, buffer, out written); - Assert.True(result, nameof(TryExportECPrivateKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(buffer)); - - // buffer more than enough - buffer = new char[expectedPem.Length + 20]; - buffer.AsSpan().Fill('!'); - Span bufferSpan = buffer.AsSpan(10); - result = TryExportECPrivateKeyPem(key, bufferSpan, out written); - Assert.True(result, nameof(TryExportECPrivateKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); - - // Ensure padding has not been touched. - AssertExtensions.FilledWith('!', buffer[0..10]); - AssertExtensions.FilledWith('!', buffer[^10..]); - } - } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs index 3ebe9ee87c9daf..a03cdb0c766d0e 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs @@ -10,16 +10,7 @@ namespace System.Security.Cryptography.EcDsa.Tests public sealed class ECDiffieHellmanKeyPemTests : ECKeyPemTests { protected override ECDiffieHellman CreateKey() => ECDiffieHellman.Create(); - protected override ECParameters ExportParameters(ECDiffieHellman key, bool includePrivateParameters) => key.ExportParameters(includePrivateParameters); - - protected override void ImportParameters(ECDiffieHellman key, ECParameters ecParameters) => - key.ImportParameters(ecParameters); - - protected override string ExportECPrivateKeyPem(ECDiffieHellman key) => key.ExportECPrivateKeyPem(); - - protected override bool TryExportECPrivateKeyPem(ECDiffieHellman key, Span destination, out int charsWritten) => - key.TryExportECPrivateKeyPem(destination, out charsWritten); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs index 00a567fb48681b..b969afa0fe4550 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs @@ -10,16 +10,7 @@ namespace System.Security.Cryptography.EcDsa.Tests public sealed class ECDsaKeyPemTests : ECKeyPemTests { protected override ECDsa CreateKey() => ECDsa.Create(); - protected override ECParameters ExportParameters(ECDsa key, bool includePrivateParameters) => key.ExportParameters(includePrivateParameters); - - protected override void ImportParameters(ECDsa key, ECParameters ecParameters) => - key.ImportParameters(ecParameters); - - protected override string ExportECPrivateKeyPem(ECDsa key) => key.ExportECPrivateKeyPem(); - - protected override bool TryExportECPrivateKeyPem(ECDsa key, Span destination, out int charsWritten) => - key.TryExportECPrivateKeyPem(destination, out charsWritten); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs index a93b8fde9f5e90..806e996cf3a7c1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -518,152 +518,6 @@ public static void ImportFromEncryptedPem_Pkcs8Encrypted_Char_NoPem() } } - [Fact] - public static void ExportPem_ExportRSAPublicKeyPem() - { - string expectedPem = - "-----BEGIN RSA PUBLIC KEY-----\n" + - "MIGJAoGBAJ8FH85xyi4XD0PGBEpvt4TYrWJb24dLBa0Ddhlj7iqdzH2vOlEjrn8Z\n" + - "pmO/ZWteNxxqCqfwgrG9wyFQISPvQDCTERFc91tCchje/hv2DgK/KFb7fLXxiM1m\n" + - "j8HKuvmDfglb0pG5FlwrDI6Ru8OEDXPuojCCLzyQUIinkpaZDGi9AgMBAAE=\n" + - "-----END RSA PUBLIC KEY-----"; - - using (RSA rsa = RSAFactory.Create()) - { - rsa.ImportParameters(TestData.RSA1024Params.ToPublic()); - Assert.Equal(expectedPem, rsa.ExportRSAPublicKeyPem()); - } - } - - [Fact] - public static void ExportPem_TryExportRSAPublicKeyPem() - { - string expectedPem = - "-----BEGIN RSA PUBLIC KEY-----\n" + - "MIGJAoGBAJ8FH85xyi4XD0PGBEpvt4TYrWJb24dLBa0Ddhlj7iqdzH2vOlEjrn8Z\n" + - "pmO/ZWteNxxqCqfwgrG9wyFQISPvQDCTERFc91tCchje/hv2DgK/KFb7fLXxiM1m\n" + - "j8HKuvmDfglb0pG5FlwrDI6Ru8OEDXPuojCCLzyQUIinkpaZDGi9AgMBAAE=\n" + - "-----END RSA PUBLIC KEY-----"; - - using (RSA rsa = RSAFactory.Create()) - { - rsa.ImportParameters(TestData.RSA1024Params.ToPublic()); - - int written; - bool result; - char[] buffer; - - // buffer not enough - buffer = new char[expectedPem.Length - 1]; - result = rsa.TryExportRSAPublicKeyPem(buffer, out written); - Assert.False(result, nameof(rsa.TryExportRSAPublicKeyPem)); - Assert.Equal(0, written); - - // buffer just enough - buffer = new char[expectedPem.Length]; - result = rsa.TryExportRSAPublicKeyPem(buffer, out written); - Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(buffer)); - - // buffer more than enough - buffer = new char[expectedPem.Length + 20]; - buffer.AsSpan().Fill('!'); - Span bufferSpan = buffer.AsSpan(10); - result = rsa.TryExportRSAPublicKeyPem(bufferSpan, out written); - Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); - - // Ensure padding has not been touched. - AssertExtensions.FilledWith('!', buffer[0..10]); - AssertExtensions.FilledWith('!', buffer[^10..]); - } - } - - [Fact] - public static void ExportPem_ExportRSAPrivateKeyPem() - { - string expectedPem = - "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIICXAIBAAKBgQDO7Bf6EsX3Ynlnh9xugIk1x9CRWFd/hf7V5cl4PWrlH/WBPbAU\n" + - "6zMF46PVJ9qFezS6DvaM7RWig7UCktfvwFn2iNK1x3wymzIxQujI9fSG5YNh4cq6\n" + - "K2Kaco4HyA4D0xKsT2S6Tz3rVHZ0ZDYFSvbFeZ5UVESvZBXxgIwt+VMBtQIDAQAB\n" + - "AoGBAIm3dJXJnNxzO939/GXHQor6vAOgSkvjgKIX16khJ13rkVg9GE4z47eNtGKP\n" + - "rc6dG5ChmNJekMGi6vwVg4Zb/QL3kQnV2f5rff5J9WhHMqX4/1xrRTZSf4+P7MRk\n" + - "6tjqvw/WDDZcZVRgVNcyZQhmegEaHzVrdAmzHEIDQKrb7bvZAkEA8G54/N6sHUNI\n" + - "ykSVdovZzgIZVy3bFqIQ/Iw6AqBL8P16MSp/hB9DPOLHGt6M8Gexvsv72kXNjHLD\n" + - "mO5iKP/VjwJBANxSJvz/O6/aiyD4U/ZS6xJtHtToDrR2tvAkT/xYPPxdav7HxHHR\n" + - "GOkfEliEll8TZLMbmFozKF7x2rfueP8rOnsCQF4wSQF3EiIcm8VxfMQGIXtM4zR9\n" + - "6FhDgTgOjyM8PMPZ6SadqXrO7H5LsQhbqwb6+GzwaGen9NBSnUGqEZZSMw0CQDOa\n" + - "clNTnLFFqMyiHVhWd4SrVktbxwXxwEti1fEu4aniX4spUxOte/4uLufYtjeynSVu\n" + - "UzLdRJloYGgwTNrHTz0CQHK2xOnRoauKfVVCWxeTPNZQIXAOv7gIpxgQOprroUzm\n" + - "C5Gqw+8rVETHgQ9xIKSEWq03v2psbhcvj4YlJAfL8q0=\n" + - "-----END RSA PRIVATE KEY-----"; - - using (RSA rsa = RSAFactory.Create()) - { - rsa.ImportParameters(TestData.RSA1024FullPrivateExponent); - Assert.Equal(expectedPem, rsa.ExportRSAPrivateKeyPem()); - } - } - - [Fact] - public static void ExportPem_TryExportRSAPrivatePem() - { - string expectedPem = - "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIICXAIBAAKBgQDO7Bf6EsX3Ynlnh9xugIk1x9CRWFd/hf7V5cl4PWrlH/WBPbAU\n" + - "6zMF46PVJ9qFezS6DvaM7RWig7UCktfvwFn2iNK1x3wymzIxQujI9fSG5YNh4cq6\n" + - "K2Kaco4HyA4D0xKsT2S6Tz3rVHZ0ZDYFSvbFeZ5UVESvZBXxgIwt+VMBtQIDAQAB\n" + - "AoGBAIm3dJXJnNxzO939/GXHQor6vAOgSkvjgKIX16khJ13rkVg9GE4z47eNtGKP\n" + - "rc6dG5ChmNJekMGi6vwVg4Zb/QL3kQnV2f5rff5J9WhHMqX4/1xrRTZSf4+P7MRk\n" + - "6tjqvw/WDDZcZVRgVNcyZQhmegEaHzVrdAmzHEIDQKrb7bvZAkEA8G54/N6sHUNI\n" + - "ykSVdovZzgIZVy3bFqIQ/Iw6AqBL8P16MSp/hB9DPOLHGt6M8Gexvsv72kXNjHLD\n" + - "mO5iKP/VjwJBANxSJvz/O6/aiyD4U/ZS6xJtHtToDrR2tvAkT/xYPPxdav7HxHHR\n" + - "GOkfEliEll8TZLMbmFozKF7x2rfueP8rOnsCQF4wSQF3EiIcm8VxfMQGIXtM4zR9\n" + - "6FhDgTgOjyM8PMPZ6SadqXrO7H5LsQhbqwb6+GzwaGen9NBSnUGqEZZSMw0CQDOa\n" + - "clNTnLFFqMyiHVhWd4SrVktbxwXxwEti1fEu4aniX4spUxOte/4uLufYtjeynSVu\n" + - "UzLdRJloYGgwTNrHTz0CQHK2xOnRoauKfVVCWxeTPNZQIXAOv7gIpxgQOprroUzm\n" + - "C5Gqw+8rVETHgQ9xIKSEWq03v2psbhcvj4YlJAfL8q0=\n" + - "-----END RSA PRIVATE KEY-----"; - - using (RSA rsa = RSAFactory.Create()) - { - rsa.ImportParameters(TestData.RSA1024FullPrivateExponent); - - int written; - bool result; - char[] buffer; - - // buffer not enough - buffer = new char[expectedPem.Length - 1]; - result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); - Assert.False(result, nameof(rsa.TryExportRSAPrivateKeyPem)); - Assert.Equal(0, written); - - // buffer just enough - buffer = new char[expectedPem.Length]; - result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); - Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(buffer)); - - // buffer more than enough - buffer = new char[expectedPem.Length + 20]; - buffer.AsSpan().Fill('!'); - Span bufferSpan = buffer.AsSpan(10); - result = rsa.TryExportRSAPrivateKeyPem(bufferSpan, out written); - Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); - Assert.Equal(expectedPem.Length, written); - Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); - - // Ensure padding has not been touched. - AssertExtensions.FilledWith('!', buffer[0..10]); - AssertExtensions.FilledWith('!', buffer[^10..]); - } - } - private static RSAParameters ToPublic(this RSAParameters rsaParams) { return new RSAParameters diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs index 6b9069fcfd39fd..c5bbd6bed7c515 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/TestData.cs @@ -175,105 +175,6 @@ internal class TestData }, }; - // D and Modulus have their high bits set so they both encode to 1024-bit values. - public static readonly RSAParameters RSA1024FullPrivateExponent = new RSAParameters - { - Modulus = new byte[] - { - 0xCE, 0xEC, 0x17, 0xFA, 0x12, 0xC5, 0xF7, 0x62, - 0x79, 0x67, 0x87, 0xDC, 0x6E, 0x80, 0x89, 0x35, - 0xC7, 0xD0, 0x91, 0x58, 0x57, 0x7F, 0x85, 0xFE, - 0xD5, 0xE5, 0xC9, 0x78, 0x3D, 0x6A, 0xE5, 0x1F, - 0xF5, 0x81, 0x3D, 0xB0, 0x14, 0xEB, 0x33, 0x05, - 0xE3, 0xA3, 0xD5, 0x27, 0xDA, 0x85, 0x7B, 0x34, - 0xBA, 0x0E, 0xF6, 0x8C, 0xED, 0x15, 0xA2, 0x83, - 0xB5, 0x02, 0x92, 0xD7, 0xEF, 0xC0, 0x59, 0xF6, - 0x88, 0xD2, 0xB5, 0xC7, 0x7C, 0x32, 0x9B, 0x32, - 0x31, 0x42, 0xE8, 0xC8, 0xF5, 0xF4, 0x86, 0xE5, - 0x83, 0x61, 0xE1, 0xCA, 0xBA, 0x2B, 0x62, 0x9A, - 0x72, 0x8E, 0x07, 0xC8, 0x0E, 0x03, 0xD3, 0x12, - 0xAC, 0x4F, 0x64, 0xBA, 0x4F, 0x3D, 0xEB, 0x54, - 0x76, 0x74, 0x64, 0x36, 0x05, 0x4A, 0xF6, 0xC5, - 0x79, 0x9E, 0x54, 0x54, 0x44, 0xAF, 0x64, 0x15, - 0xF1, 0x80, 0x8C, 0x2D, 0xF9, 0x53, 0x01, 0xB5, - }, - Exponent = new byte[] { 0x01, 0x00, 0x01 }, - D = new byte[] - { - 0x89, 0xB7, 0x74, 0x95, 0xC9, 0x9C, 0xDC, 0x73, - 0x3B, 0xDD, 0xFD, 0xFC, 0x65, 0xC7, 0x42, 0x8A, - 0xFA, 0xBC, 0x03, 0xA0, 0x4A, 0x4B, 0xE3, 0x80, - 0xA2, 0x17, 0xD7, 0xA9, 0x21, 0x27, 0x5D, 0xEB, - 0x91, 0x58, 0x3D, 0x18, 0x4E, 0x33, 0xE3, 0xB7, - 0x8D, 0xB4, 0x62, 0x8F, 0xAD, 0xCE, 0x9D, 0x1B, - 0x90, 0xA1, 0x98, 0xD2, 0x5E, 0x90, 0xC1, 0xA2, - 0xEA, 0xFC, 0x15, 0x83, 0x86, 0x5B, 0xFD, 0x02, - 0xF7, 0x91, 0x09, 0xD5, 0xD9, 0xFE, 0x6B, 0x7D, - 0xFE, 0x49, 0xF5, 0x68, 0x47, 0x32, 0xA5, 0xF8, - 0xFF, 0x5C, 0x6B, 0x45, 0x36, 0x52, 0x7F, 0x8F, - 0x8F, 0xEC, 0xC4, 0x64, 0xEA, 0xD8, 0xEA, 0xBF, - 0x0F, 0xD6, 0x0C, 0x36, 0x5C, 0x65, 0x54, 0x60, - 0x54, 0xD7, 0x32, 0x65, 0x08, 0x66, 0x7A, 0x01, - 0x1A, 0x1F, 0x35, 0x6B, 0x74, 0x09, 0xB3, 0x1C, - 0x42, 0x03, 0x40, 0xAA, 0xDB, 0xED, 0xBB, 0xD9, - }, - P = new byte[] - { - 0xF0, 0x6E, 0x78, 0xFC, 0xDE, 0xAC, 0x1D, 0x43, - 0x48, 0xCA, 0x44, 0x95, 0x76, 0x8B, 0xD9, 0xCE, - 0x02, 0x19, 0x57, 0x2D, 0xDB, 0x16, 0xA2, 0x10, - 0xFC, 0x8C, 0x3A, 0x02, 0xA0, 0x4B, 0xF0, 0xFD, - 0x7A, 0x31, 0x2A, 0x7F, 0x84, 0x1F, 0x43, 0x3C, - 0xE2, 0xC7, 0x1A, 0xDE, 0x8C, 0xF0, 0x67, 0xB1, - 0xBE, 0xCB, 0xFB, 0xDA, 0x45, 0xCD, 0x8C, 0x72, - 0xC3, 0x98, 0xEE, 0x62, 0x28, 0xFF, 0xD5, 0x8F, - }, - DP = new byte[] - { - 0x5E, 0x30, 0x49, 0x01, 0x77, 0x12, 0x22, 0x1C, - 0x9B, 0xC5, 0x71, 0x7C, 0xC4, 0x06, 0x21, 0x7B, - 0x4C, 0xE3, 0x34, 0x7D, 0xE8, 0x58, 0x43, 0x81, - 0x38, 0x0E, 0x8F, 0x23, 0x3C, 0x3C, 0xC3, 0xD9, - 0xE9, 0x26, 0x9D, 0xA9, 0x7A, 0xCE, 0xEC, 0x7E, - 0x4B, 0xB1, 0x08, 0x5B, 0xAB, 0x06, 0xFA, 0xF8, - 0x6C, 0xF0, 0x68, 0x67, 0xA7, 0xF4, 0xD0, 0x52, - 0x9D, 0x41, 0xAA, 0x11, 0x96, 0x52, 0x33, 0x0D, - }, - Q = new byte[] - { - 0xDC, 0x52, 0x26, 0xFC, 0xFF, 0x3B, 0xAF, 0xDA, - 0x8B, 0x20, 0xF8, 0x53, 0xF6, 0x52, 0xEB, 0x12, - 0x6D, 0x1E, 0xD4, 0xE8, 0x0E, 0xB4, 0x76, 0xB6, - 0xF0, 0x24, 0x4F, 0xFC, 0x58, 0x3C, 0xFC, 0x5D, - 0x6A, 0xFE, 0xC7, 0xC4, 0x71, 0xD1, 0x18, 0xE9, - 0x1F, 0x12, 0x58, 0x84, 0x96, 0x5F, 0x13, 0x64, - 0xB3, 0x1B, 0x98, 0x5A, 0x33, 0x28, 0x5E, 0xF1, - 0xDA, 0xB7, 0xEE, 0x78, 0xFF, 0x2B, 0x3A, 0x7B, - }, - DQ = new byte[] - { - 0x33, 0x9A, 0x72, 0x53, 0x53, 0x9C, 0xB1, 0x45, - 0xA8, 0xCC, 0xA2, 0x1D, 0x58, 0x56, 0x77, 0x84, - 0xAB, 0x56, 0x4B, 0x5B, 0xC7, 0x05, 0xF1, 0xC0, - 0x4B, 0x62, 0xD5, 0xF1, 0x2E, 0xE1, 0xA9, 0xE2, - 0x5F, 0x8B, 0x29, 0x53, 0x13, 0xAD, 0x7B, 0xFE, - 0x2E, 0x2E, 0xE7, 0xD8, 0xB6, 0x37, 0xB2, 0x9D, - 0x25, 0x6E, 0x53, 0x32, 0xDD, 0x44, 0x99, 0x68, - 0x60, 0x68, 0x30, 0x4C, 0xDA, 0xC7, 0x4F, 0x3D, - }, - InverseQ = new byte[] - { - 0x72, 0xB6, 0xC4, 0xE9, 0xD1, 0xA1, 0xAB, 0x8A, - 0x7D, 0x55, 0x42, 0x5B, 0x17, 0x93, 0x3C, 0xD6, - 0x50, 0x21, 0x70, 0x0E, 0xBF, 0xB8, 0x08, 0xA7, - 0x18, 0x10, 0x3A, 0x9A, 0xEB, 0xA1, 0x4C, 0xE6, - 0x0B, 0x91, 0xAA, 0xC3, 0xEF, 0x2B, 0x54, 0x44, - 0xC7, 0x81, 0x0F, 0x71, 0x20, 0xA4, 0x84, 0x5A, - 0xAD, 0x37, 0xBF, 0x6A, 0x6C, 0x6E, 0x17, 0x2F, - 0x8F, 0x86, 0x25, 0x24, 0x07, 0xCB, 0xF2, 0xAD, - }, - }; - public static readonly RSAParameters RSA1032Parameters = new RSAParameters { Modulus = new byte[] From 14303d04bcc8ab7d3cf395442515ab1fe8862042 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 12 Nov 2021 14:48:24 +0000 Subject: [PATCH 5/6] Change PEM tests use delegate approach. The way RSA exports keys private keys is not consistent between platforms and brittle. Change the tests to use well-known data for PEM exports so the tests are stable. --- .../tests/RSATests.cs | 158 ++++++++++++++++++ ...urity.Cryptography.Algorithms.Tests.csproj | 1 + 2 files changed, 159 insertions(+) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs index 0e63e04b0859ad..b127365a0d4d61 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/RSATests.cs @@ -230,6 +230,150 @@ public void RSASignaturePadding_Equality() Assert.True(RSASignaturePadding.Pkcs1 != null); } + [Fact] + public static void ExportPem_ExportRSAPublicKey() + { + string expectedPem = + "-----BEGIN RSA PUBLIC KEY-----\n" + + "cGVubnk=\n" + + "-----END RSA PUBLIC KEY-----"; + + static byte[] ExportRSAPublicKey() + { + return new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + } + + using (DelegateRSA rsa = new DelegateRSA()) + { + rsa.ExportRSAPublicKeyDelegate = ExportRSAPublicKey; + Assert.Equal(expectedPem, rsa.ExportRSAPublicKeyPem()); + } + } + + [Fact] + public static void ExportPem_TryExportRSAPublicKey() + { + string expectedPem = + "-----BEGIN RSA PUBLIC KEY-----\n" + + "cGVubnk=\n" + + "-----END RSA PUBLIC KEY-----"; + + static bool TryExportRSAPublicKey(Span destination, out int bytesWritten) + { + ReadOnlySpan result = new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + bytesWritten = result.Length; + result.CopyTo(destination); + return true; + } + + using (DelegateRSA rsa = new DelegateRSA()) + { + rsa.TryExportRSAPublicKeyDelegate = TryExportRSAPublicKey; + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = rsa.TryExportRSAPublicKeyPem(buffer, out written); + Assert.False(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = rsa.TryExportRSAPublicKeyPem(buffer, out written); + Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = rsa.TryExportRSAPublicKeyPem(bufferSpan, out written); + Assert.True(result, nameof(rsa.TryExportRSAPublicKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + + [Fact] + public static void ExportPem_ExportRSAPrivateKey() + { + string expectedPem = + "-----BEGIN RSA PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END RSA PRIVATE KEY-----"; + + static byte[] ExportRSAPrivateKey() + { + return new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + } + + using (DelegateRSA rsa = new DelegateRSA()) + { + rsa.ExportRSAPrivateKeyDelegate = ExportRSAPrivateKey; + Assert.Equal(expectedPem, rsa.ExportRSAPrivateKeyPem()); + } + } + + [Fact] + public static void ExportPem_TryExportRSAPrivateKey() + { + string expectedPem = + "-----BEGIN RSA PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END RSA PRIVATE KEY-----"; + + static bool TryExportRSAPrivateKey(Span destination, out int bytesWritten) + { + ReadOnlySpan result = new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + bytesWritten = result.Length; + result.CopyTo(destination); + return true; + } + + using (DelegateRSA rsa = new DelegateRSA()) + { + rsa.TryExportRSAPrivateKeyDelegate = TryExportRSAPrivateKey; + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); + Assert.False(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = rsa.TryExportRSAPrivateKeyPem(buffer, out written); + Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = rsa.TryExportRSAPrivateKeyPem(bufferSpan, out written); + Assert.True(result, nameof(rsa.TryExportRSAPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + private sealed class EmptyRSA : RSA { public override RSAParameters ExportParameters(bool includePrivateParameters) => throw new NotImplementedException(); @@ -240,12 +384,17 @@ private sealed class EmptyRSA : RSA private sealed class DelegateRSA : RSA { + public delegate bool TryExportFunc(Span destination, out int bytesWritten); public Func DecryptDelegate; public Func EncryptDelegate; public Func SignHashDelegate = null; public Func VerifyHashDelegate = null; public Func HashDataArrayDelegate = null; public Func HashDataStreamDelegate = null; + public Func ExportRSAPublicKeyDelegate = null; + public TryExportFunc TryExportRSAPublicKeyDelegate = null; + public Func ExportRSAPrivateKeyDelegate = null; + public TryExportFunc TryExportRSAPrivateKeyDelegate = null; public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) => EncryptDelegate(data, padding); @@ -265,6 +414,15 @@ protected override byte[] HashData(byte[] data, int offset, int count, HashAlgor protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) => HashDataStreamDelegate(data, hashAlgorithm); + public override byte[] ExportRSAPublicKey() => ExportRSAPublicKeyDelegate(); + public override byte[] ExportRSAPrivateKey() => ExportRSAPrivateKeyDelegate(); + + public override bool TryExportRSAPublicKey(Span destination, out int bytesWritten) => + TryExportRSAPublicKeyDelegate(destination, out bytesWritten); + + public override bool TryExportRSAPrivateKey(Span destination, out int bytesWritten) => + TryExportRSAPrivateKeyDelegate(destination, out bytesWritten); + public new bool TryHashData(ReadOnlySpan source, Span destination, HashAlgorithmName hashAlgorithm, out int bytesWritten) => base.TryHashData(source, destination, hashAlgorithm, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index f4f089bea4cf0f..d81c2cd9ea75a5 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -179,6 +179,7 @@ + From 05ed34ff7338059d80e8f2812b92fcc238b1cd85 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 12 Nov 2021 16:36:56 +0000 Subject: [PATCH 6/6] Actually commit the tests --- .../tests/ECPemExportTests.cs | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/libraries/System.Security.Cryptography.Algorithms/tests/ECPemExportTests.cs diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECPemExportTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECPemExportTests.cs new file mode 100644 index 00000000000000..098cda4f2359d4 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECPemExportTests.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using Xunit; + +namespace System.Security.Cryptography.Algorithms.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] + public static class ECPemExportTests + { + [Fact] + public static void ExportPem_ECDsa_ExportECPrivateKey() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END EC PRIVATE KEY-----"; + + static byte[] ExportECPrivateKey() + { + return new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + } + + using (DelegateECDsa ec = new DelegateECDsa()) + { + ec.ExportECPrivateKeyDelegate = ExportECPrivateKey; + Assert.Equal(expectedPem, ec.ExportECPrivateKeyPem()); + } + } + + [Fact] + public static void ExportPem_ECDsa_TryExportECPrivateKey() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END EC PRIVATE KEY-----"; + + static bool TryExportECPrivateKey(Span destination, out int bytesWritten) + { + ReadOnlySpan result = new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + bytesWritten = result.Length; + result.CopyTo(destination); + return true; + } + + using (DelegateECDsa ec = new DelegateECDsa()) + { + ec.TryExportECPrivateKeyDelegate = TryExportECPrivateKey; + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = ec.TryExportECPrivateKeyPem(buffer, out written); + Assert.False(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = ec.TryExportECPrivateKeyPem(buffer, out written); + Assert.True(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = ec.TryExportECPrivateKeyPem(bufferSpan, out written); + Assert.True(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + [Fact] + public static void ExportPem_ECDiffieHellman_ExportECPrivateKey() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END EC PRIVATE KEY-----"; + + static byte[] ExportECPrivateKey() + { + return new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + } + + using (DelegateECDiffieHellman ec = new DelegateECDiffieHellman()) + { + ec.ExportECPrivateKeyDelegate = ExportECPrivateKey; + Assert.Equal(expectedPem, ec.ExportECPrivateKeyPem()); + } + } + + [Fact] + public static void ExportPem_ECDiffieHellman_TryExportECPrivateKey() + { + string expectedPem = + "-----BEGIN EC PRIVATE KEY-----\n" + + "cGVubnk=\n" + + "-----END EC PRIVATE KEY-----"; + + static bool TryExportECPrivateKey(Span destination, out int bytesWritten) + { + ReadOnlySpan result = new byte[] { 0x70, 0x65, 0x6e, 0x6e, 0x79 }; + bytesWritten = result.Length; + result.CopyTo(destination); + return true; + } + + using (DelegateECDiffieHellman ec = new DelegateECDiffieHellman()) + { + ec.TryExportECPrivateKeyDelegate = TryExportECPrivateKey; + + int written; + bool result; + char[] buffer; + + // buffer not enough + buffer = new char[expectedPem.Length - 1]; + result = ec.TryExportECPrivateKeyPem(buffer, out written); + Assert.False(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(0, written); + + // buffer just enough + buffer = new char[expectedPem.Length]; + result = ec.TryExportECPrivateKeyPem(buffer, out written); + Assert.True(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(buffer)); + + // buffer more than enough + buffer = new char[expectedPem.Length + 20]; + buffer.AsSpan().Fill('!'); + Span bufferSpan = buffer.AsSpan(10); + result = ec.TryExportECPrivateKeyPem(bufferSpan, out written); + Assert.True(result, nameof(ec.TryExportECPrivateKeyPem)); + Assert.Equal(expectedPem.Length, written); + Assert.Equal(expectedPem, new string(bufferSpan.Slice(0, written))); + + // Ensure padding has not been touched. + AssertExtensions.FilledWith('!', buffer[0..10]); + AssertExtensions.FilledWith('!', buffer[^10..]); + } + } + + private class DelegateECDsa : ECDsa + { + public delegate bool TryExportFunc(Span destination, out int bytesWritten); + + public Func ExportECPrivateKeyDelegate = null; + public TryExportFunc TryExportECPrivateKeyDelegate = null; + + + public DelegateECDsa() + { + } + + public override byte[] ExportECPrivateKey() => ExportECPrivateKeyDelegate(); + + public override bool TryExportECPrivateKey(Span destination, out int bytesWritten) => + TryExportECPrivateKeyDelegate(destination, out bytesWritten); + + public override byte[] SignHash(byte[] hash) => throw new NotImplementedException(); + public override bool VerifyHash(byte[] hash, byte[] signature) => throw new NotImplementedException(); + protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) => throw new NotImplementedException(); + protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) => throw new NotImplementedException(); + public override void ImportParameters(ECParameters parameters) => throw new NotImplementedException(); + public override ECParameters ExportParameters(bool includePrivateParameters) => throw new NotImplementedException(); + public override ECParameters ExportExplicitParameters(bool includePrivateParameters) => throw new NotImplementedException(); + public override void GenerateKey(ECCurve curve) => throw new NotImplementedException(); + } + + private class DelegateECDiffieHellman : ECDiffieHellman + { + public delegate bool TryExportFunc(Span destination, out int bytesWritten); + + public Func ExportECPrivateKeyDelegate = null; + public TryExportFunc TryExportECPrivateKeyDelegate = null; + + + public DelegateECDiffieHellman() + { + } + + public override byte[] ExportECPrivateKey() => ExportECPrivateKeyDelegate(); + + public override bool TryExportECPrivateKey(Span destination, out int bytesWritten) => + TryExportECPrivateKeyDelegate(destination, out bytesWritten); + + public override ECDiffieHellmanPublicKey PublicKey => throw new NotImplementedException(); + } + } +} \ No newline at end of file