Skip to content

Commit 2099752

Browse files
authored
Add PEM PKCS#8 and SPKI exports for AsymmetricAlgorithm
1 parent 8684c21 commit 2099752

File tree

9 files changed

+701
-23
lines changed

9 files changed

+701
-23
lines changed

src/libraries/Common/src/Internal/Cryptography/PemKeyImportHelpers.cs renamed to src/libraries/Common/src/Internal/Cryptography/PemKeyHelpers.cs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,106 @@
77

88
namespace Internal.Cryptography
99
{
10-
internal static class PemKeyImportHelpers
10+
internal static class PemKeyHelpers
1111
{
12+
public delegate bool TryExportKeyAction<T>(T arg, Span<byte> destination, out int bytesWritten);
13+
public delegate bool TryExportEncryptedKeyAction<T>(
14+
T arg,
15+
ReadOnlySpan<char> password,
16+
PbeParameters pbeParameters,
17+
Span<byte> destination,
18+
out int bytesWritten);
19+
20+
public static unsafe bool TryExportToEncryptedPem<T>(
21+
T arg,
22+
ReadOnlySpan<char> password,
23+
PbeParameters pbeParameters,
24+
TryExportEncryptedKeyAction<T> exporter,
25+
Span<char> destination,
26+
out int charsWritten)
27+
{
28+
int bufferSize = 4096;
29+
30+
while (true)
31+
{
32+
byte[] buffer = CryptoPool.Rent(bufferSize);
33+
int bytesWritten = 0;
34+
bufferSize = buffer.Length;
35+
36+
// Fixed to prevent GC moves.
37+
fixed (byte* bufferPtr = buffer)
38+
{
39+
try
40+
{
41+
if (exporter(arg, password, pbeParameters, buffer, out bytesWritten))
42+
{
43+
Span<byte> writtenSpan = new Span<byte>(buffer, 0, bytesWritten);
44+
return PemEncoding.TryWrite(PemLabels.EncryptedPkcs8PrivateKey, writtenSpan, destination, out charsWritten);
45+
}
46+
}
47+
finally
48+
{
49+
CryptoPool.Return(buffer, bytesWritten);
50+
}
51+
52+
bufferSize = checked(bufferSize * 2);
53+
}
54+
}
55+
}
56+
57+
public static unsafe bool TryExportToPem<T>(
58+
T arg,
59+
string label,
60+
TryExportKeyAction<T> exporter,
61+
Span<char> destination,
62+
out int charsWritten)
63+
{
64+
int bufferSize = 4096;
65+
66+
while (true)
67+
{
68+
byte[] buffer = CryptoPool.Rent(bufferSize);
69+
int bytesWritten = 0;
70+
bufferSize = buffer.Length;
71+
72+
// Fixed to prevent GC moves.
73+
fixed (byte* bufferPtr = buffer)
74+
{
75+
try
76+
{
77+
if (exporter(arg, buffer, out bytesWritten))
78+
{
79+
Span<byte> writtenSpan = new Span<byte>(buffer, 0, bytesWritten);
80+
return PemEncoding.TryWrite(label, writtenSpan, destination, out charsWritten);
81+
}
82+
}
83+
finally
84+
{
85+
CryptoPool.Return(buffer, bytesWritten);
86+
}
87+
88+
bufferSize = checked(bufferSize * 2);
89+
}
90+
}
91+
}
92+
93+
internal static string CreatePemFromData(string label, ReadOnlyMemory<byte> data)
94+
{
95+
int pemSize = PemEncoding.GetEncodedSize(label.Length, data.Length);
96+
97+
return string.Create(pemSize, (label, data), static (destination, args) =>
98+
{
99+
(string label, ReadOnlyMemory<byte> data) = args;
100+
101+
if (!PemEncoding.TryWrite(label, data.Span, destination, out int charsWritten) ||
102+
charsWritten != destination.Length)
103+
{
104+
Debug.Fail("Pre-allocated buffer was not the correct size.");
105+
throw new CryptographicException();
106+
}
107+
});
108+
}
109+
12110
public delegate void ImportKeyAction(ReadOnlySpan<byte> source, out int bytesRead);
13111
public delegate ImportKeyAction? FindImportActionFunc(ReadOnlySpan<char> label);
14112
public delegate void ImportEncryptedKeyAction<TPass>(

src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@
108108
Link="Internal\Cryptography\BasicSymmetricCipher.cs" />
109109
<Compile Include="$(CommonPath)Internal\Cryptography\Helpers.cs"
110110
Link="Internal\Cryptography\Helpers.cs" />
111-
<Compile Include="$(CommonPath)Internal\Cryptography\PemKeyImportHelpers.cs"
112-
Link="Common\Internal\Cryptography\PemKeyImportHelpers.cs" />
111+
<Compile Include="$(CommonPath)Internal\Cryptography\PemKeyHelpers.cs"
112+
Link="Common\Internal\Cryptography\PemKeyHelpers.cs" />
113113
<Compile Include="$(CommonPath)Internal\Cryptography\ILiteSymmetricCipher.cs"
114114
Link="Internal\Cryptography\ILiteSymmetricCipher.cs" />
115115
<Compile Include="$(CommonPath)Internal\Cryptography\SymmetricPadding.cs"

src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ public virtual unsafe bool TryExportECPrivateKey(Span<byte> destination, out int
469469
/// </remarks>
470470
public override void ImportFromPem(ReadOnlySpan<char> input)
471471
{
472-
PemKeyImportHelpers.ImportPem(input, label => {
472+
PemKeyHelpers.ImportPem(input, label => {
473473
if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey))
474474
{
475475
return ImportPkcs8PrivateKey;

src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ public int GetMaxSignatureSize(DSASignatureFormat signatureFormat)
13331333
/// </remarks>
13341334
public override void ImportFromPem(ReadOnlySpan<char> input)
13351335
{
1336-
PemKeyImportHelpers.ImportPem(input, label => {
1336+
PemKeyHelpers.ImportPem(input, label => {
13371337
if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey))
13381338
{
13391339
return ImportPkcs8PrivateKey;

src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ public override unsafe void ImportEncryptedPkcs8PrivateKey(
679679
/// </remarks>
680680
public override void ImportFromPem(ReadOnlySpan<char> input)
681681
{
682-
PemKeyImportHelpers.ImportPem(input, label => {
682+
PemKeyHelpers.ImportPem(input, label => {
683683
if (label.SequenceEqual(PemLabels.RsaPrivateKey))
684684
{
685685
return ImportRSAPrivateKey;

src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ public void Dispose() { }
6363
protected virtual void Dispose(bool disposing) { }
6464
public virtual byte[] ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
6565
public virtual byte[] ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
66+
public string ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
6667
public virtual byte[] ExportPkcs8PrivateKey() { throw null; }
68+
public string ExportPkcs8PrivateKeyPem() { throw null; }
6769
public virtual byte[] ExportSubjectPublicKeyInfo() { throw null; }
70+
public string ExportSubjectPublicKeyInfoPem() { throw null; }
6871
public virtual void FromXmlString(string xmlString) { }
6972
public virtual void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
7073
public virtual void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
@@ -76,8 +79,11 @@ public virtual void ImportFromPem(System.ReadOnlySpan<char> input) { }
7679
public virtual string ToXmlString(bool includePrivateParameters) { throw null; }
7780
public virtual bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
7881
public virtual bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
82+
public bool TryExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<char> destination, out int charsWritten) { throw null; }
7983
public virtual bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
84+
public bool TryExportPkcs8PrivateKeyPem(System.Span<char> destination, out int charsWritten) { throw null; }
8085
public virtual bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten) { throw null; }
86+
public bool TryExportSubjectPublicKeyInfoPem(System.Span<char> destination, out int charsWritten) { throw null; }
8187
}
8288
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
8389
public abstract partial class AsymmetricKeyExchangeFormatter

src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
Link="Common\System\Obsoletions.cs" />
2222
<Compile Include="$(CommonPath)Internal\Cryptography\Helpers.cs"
2323
Link="Internal\Cryptography\Helpers.cs" />
24-
<Compile Include="$(CommonPath)Internal\Cryptography\PemKeyImportHelpers.cs"
25-
Link="Internal\Cryptography\PemKeyImportHelpers.cs" />
24+
<Compile Include="$(CommonPath)Internal\Cryptography\PemKeyHelpers.cs"
25+
Link="Internal\Cryptography\PemKeyHelpers.cs" />
2626
<Compile Include="$(CommonPath)System\Security\Cryptography\CryptoPool.cs"
2727
Link="Common\System\Security\Cryptography\CryptoPool.cs" />
2828
<Compile Include="$(CommonPath)System\Security\Cryptography\KeySizeHelpers.cs"

0 commit comments

Comments
 (0)