Skip to content

Commit 0456c7e

Browse files
author
Mirroring
committed
Merge commit '2b46f3a7eb3d0df99c523e5648f00cc8b53caa05'
2 parents 2df1137 + 2b46f3a commit 0456c7e

File tree

12 files changed

+471
-66
lines changed

12 files changed

+471
-66
lines changed

src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,20 @@ private static unsafe void GenerateV2DsaBlob(out byte[] blob, DSAParameters para
310310

311311
public override DSAParameters ExportParameters(bool includePrivateParameters)
312312
{
313+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
314+
315+
if (includePrivateParameters && encryptedOnlyExport)
316+
{
317+
const string TemporaryExportPassword = "DotnetExportPhrase";
318+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
319+
DSAKeyFormatHelper.ReadEncryptedPkcs8(
320+
exported,
321+
TemporaryExportPassword,
322+
out _,
323+
out DSAParameters dsaParameters);
324+
return dsaParameters;
325+
}
326+
313327
byte[] dsaBlob = ExportKeyBlob(includePrivateParameters);
314328

315329
KeyBlobMagicNumber magic = (KeyBlobMagicNumber)BitConverter.ToInt32(dsaBlob, 0);
@@ -423,6 +437,5 @@ private static void CheckMagicValueOfKey(KeyBlobMagicNumber magic, bool includeP
423437
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
424438
}
425439
}
426-
427440
}
428441
}

src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -66,50 +66,12 @@ public override void ImportParameters(ECParameters parameters)
6666

6767
public override ECParameters ExportExplicitParameters(bool includePrivateParameters)
6868
{
69-
byte[] blob = ExportFullKeyBlob(includePrivateParameters);
70-
71-
try
72-
{
73-
ECParameters ecparams = default;
74-
ECCng.ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters);
75-
return ecparams;
76-
}
77-
finally
78-
{
79-
Array.Clear(blob);
80-
}
69+
return ECCng.ExportExplicitParameters(Key, includePrivateParameters);
8170
}
8271

8372
public override ECParameters ExportParameters(bool includePrivateParameters)
8473
{
85-
ECParameters ecparams = default;
86-
87-
string? curveName = GetCurveName(out string? oidValue);
88-
byte[]? blob = null;
89-
90-
try
91-
{
92-
if (string.IsNullOrEmpty(curveName))
93-
{
94-
blob = ExportFullKeyBlob(includePrivateParameters);
95-
ECCng.ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters);
96-
}
97-
else
98-
{
99-
blob = ExportKeyBlob(includePrivateParameters);
100-
ECCng.ExportNamedCurveParameters(ref ecparams, blob, includePrivateParameters);
101-
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
102-
}
103-
104-
return ecparams;
105-
}
106-
finally
107-
{
108-
if (blob != null)
109-
{
110-
Array.Clear(blob);
111-
}
112-
}
74+
return ECCng.ExportParameters(Key, includePrivateParameters);
11375
}
11476

11577
public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)

src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using Internal.NativeCrypto;
56

67
namespace System.Security.Cryptography
@@ -87,10 +88,7 @@ public override void ImportParameters(ECParameters parameters)
8788
/// <returns>The key and explicit curve parameters used by the ECC object.</returns>
8889
public override ECParameters ExportExplicitParameters(bool includePrivateParameters)
8990
{
90-
byte[] blob = ExportFullKeyBlob(includePrivateParameters);
91-
ECParameters ecparams = default;
92-
ECCng.ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters);
93-
return ecparams;
91+
return ECCng.ExportExplicitParameters(Key, includePrivateParameters);
9492
}
9593

9694
/// <summary>
@@ -103,23 +101,7 @@ public override ECParameters ExportExplicitParameters(bool includePrivateParamet
103101
/// <returns>The key and named curve parameters used by the ECC object.</returns>
104102
public override ECParameters ExportParameters(bool includePrivateParameters)
105103
{
106-
ECParameters ecparams = default;
107-
108-
string? curveName = GetCurveName(out string? oidValue);
109-
110-
if (string.IsNullOrEmpty(curveName))
111-
{
112-
byte[] fullKeyBlob = ExportFullKeyBlob(includePrivateParameters);
113-
ECCng.ExportPrimeCurveParameters(ref ecparams, fullKeyBlob, includePrivateParameters);
114-
}
115-
else
116-
{
117-
byte[] keyBlob = ExportKeyBlob(includePrivateParameters);
118-
ECCng.ExportNamedCurveParameters(ref ecparams, keyBlob, includePrivateParameters);
119-
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
120-
}
121-
122-
return ecparams;
104+
return ECCng.ExportParameters(Key, includePrivateParameters);
123105
}
124106

125107
public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)

src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ public override bool TryExportEncryptedPkcs8PrivateKey(
180180
/// </summary>
181181
public override RSAParameters ExportParameters(bool includePrivateParameters)
182182
{
183+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
184+
185+
if (includePrivateParameters && encryptedOnlyExport)
186+
{
187+
const string TemporaryExportPassword = "DotnetExportPhrase";
188+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
189+
RSAKeyFormatHelper.ReadEncryptedPkcs8(
190+
exported,
191+
TemporaryExportPassword,
192+
out _,
193+
out RSAParameters rsaParameters);
194+
return rsaParameters;
195+
}
196+
183197
byte[] rsaBlob = ExportKeyBlob(includePrivateParameters);
184198
RSAParameters rsaParams = default;
185199
rsaParams.FromBCryptBlob(rsaBlob, includePrivateParameters);

src/libraries/System.Security.Cryptography.Cng/tests/CngPkcs8Tests.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,32 @@ public void NoPlaintextExportFailsPkcs8()
1717
{
1818
SetExportPolicy(cngKey, CngExportPolicies.AllowExport);
1919

20-
Assert.ThrowsAny<CryptographicException>(
21-
() => key.ExportPkcs8PrivateKey());
20+
byte[] exported = key.ExportPkcs8PrivateKey();
21+
22+
using (T imported = CreateKey(out _))
23+
{
24+
imported.ImportPkcs8PrivateKey(exported, out int importRead);
25+
Assert.Equal(exported.Length, importRead);
26+
VerifyMatch(key, imported);
27+
}
28+
29+
byte[] tryExported = new byte[exported.Length];
30+
31+
int written;
32+
33+
while (!key.TryExportPkcs8PrivateKey(tryExported, out written))
34+
{
35+
Array.Resize(ref tryExported, checked(tryExported.Length * 2));
36+
}
37+
38+
using (T imported = CreateKey(out _))
39+
{
40+
imported.ImportPkcs8PrivateKey(tryExported.AsSpan(0, written), out int tryImportRead);
41+
Assert.Equal(written, tryImportRead);
42+
VerifyMatch(key, imported);
43+
}
44+
2245

23-
Assert.ThrowsAny<CryptographicException>(
24-
() => key.TryExportPkcs8PrivateKey(Span<byte>.Empty, out _));
2546
}
2647
}
2748

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,11 @@ private static Pkcs8Response ImportPkcs8(
4343
Key = key,
4444
};
4545
}
46+
47+
internal static bool AllowsOnlyEncryptedExport(CngKey key)
48+
{
49+
const CngExportPolicies Exportable = CngExportPolicies.AllowPlaintextExport | CngExportPolicies.AllowExport;
50+
return (key.ExportPolicy & Exportable) == CngExportPolicies.AllowExport;
51+
}
4652
}
4753
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/DSACng.ImportExport.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)
5050

5151
public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
5252
{
53+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
54+
55+
if (encryptedOnlyExport)
56+
{
57+
const string TemporaryExportPassword = "DotnetExportPhrase";
58+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
59+
DSAKeyFormatHelper.ReadEncryptedPkcs8(
60+
exported,
61+
TemporaryExportPassword,
62+
out _,
63+
out DSAParameters dsaParameters);
64+
return DSAKeyFormatHelper.WritePkcs8(dsaParameters).TryEncode(destination, out bytesWritten);
65+
}
66+
5367
return Key.TryExportKeyBlob(
5468
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
5569
destination,

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECCng.ImportExport.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using static Interop.BCrypt;
56

67
namespace System.Security.Cryptography
@@ -77,6 +78,100 @@ internal static byte[] ExportKeyBlob(
7778
return blob;
7879
}
7980

81+
internal static ECParameters ExportExplicitParameters(CngKey key, bool includePrivateParameters)
82+
{
83+
if (includePrivateParameters)
84+
{
85+
return ExportPrivateExplicitParameters(key);
86+
}
87+
else
88+
{
89+
byte[] blob = ExportFullKeyBlob(key, includePrivateParameters: false);
90+
ECParameters ecparams = default;
91+
ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters: false);
92+
return ecparams;
93+
}
94+
}
95+
96+
internal static ECParameters ExportParameters(CngKey key, bool includePrivateParameters)
97+
{
98+
ECParameters ecparams = default;
99+
100+
const string TemporaryExportPassword = "DotnetExportPhrase";
101+
string? curveName = key.GetCurveName(out string? oidValue);
102+
103+
if (string.IsNullOrEmpty(curveName))
104+
{
105+
if (includePrivateParameters)
106+
{
107+
ecparams = ExportPrivateExplicitParameters(key);
108+
}
109+
else
110+
{
111+
byte[] fullKeyBlob = ExportFullKeyBlob(key, includePrivateParameters: false);
112+
ECCng.ExportPrimeCurveParameters(ref ecparams, fullKeyBlob, includePrivateParameters: false);
113+
}
114+
}
115+
else
116+
{
117+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(key);
118+
119+
if (includePrivateParameters && encryptedOnlyExport)
120+
{
121+
byte[] exported = key.ExportPkcs8KeyBlob(TemporaryExportPassword, 1);
122+
EccKeyFormatHelper.ReadEncryptedPkcs8(
123+
exported,
124+
TemporaryExportPassword,
125+
out _,
126+
out ecparams);
127+
}
128+
else
129+
{
130+
byte[] keyBlob = ExportKeyBlob(key, includePrivateParameters);
131+
ECCng.ExportNamedCurveParameters(ref ecparams, keyBlob, includePrivateParameters);
132+
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
133+
}
134+
}
135+
136+
return ecparams;
137+
}
138+
139+
private static ECParameters ExportPrivateExplicitParameters(CngKey key)
140+
{
141+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(key);
142+
143+
ECParameters ecparams = default;
144+
145+
if (encryptedOnlyExport)
146+
{
147+
// We can't ask CNG for the explicit parameters when performing a PKCS#8 export. Instead,
148+
// we ask CNG for the explicit parameters for the public part only, since the parameters are public.
149+
// Then we ask CNG by encrypted PKCS#8 for the private parameters (D) and combine the explicit public
150+
// key along with the private key.
151+
const string TemporaryExportPassword = "DotnetExportPhrase";
152+
byte[] publicKeyBlob = ExportFullKeyBlob(key, includePrivateParameters: false);
153+
ExportPrimeCurveParameters(ref ecparams, publicKeyBlob, includePrivateParameters: false);
154+
155+
byte[] exported = key.ExportPkcs8KeyBlob(TemporaryExportPassword, 1);
156+
EccKeyFormatHelper.ReadEncryptedPkcs8(
157+
exported,
158+
TemporaryExportPassword,
159+
out _,
160+
out ECParameters localParameters);
161+
162+
Debug.Assert(ecparams.Q.X.AsSpan().SequenceEqual(localParameters.Q.X));
163+
Debug.Assert(ecparams.Q.Y.AsSpan().SequenceEqual(localParameters.Q.Y));
164+
ecparams.D = localParameters.D;
165+
}
166+
else
167+
{
168+
byte[] blob = ExportFullKeyBlob(key, includePrivateParameters: true);
169+
ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters: true);
170+
}
171+
172+
return ecparams;
173+
}
174+
80175
private static unsafe void FixupGenericBlob(byte[] blob)
81176
{
82177
if (blob.Length > sizeof(BCRYPT_ECCKEY_BLOB))

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)
206206

207207
public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
208208
{
209+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
210+
211+
if (encryptedOnlyExport)
212+
{
213+
const string TemporaryExportPassword = "DotnetExportPhrase";
214+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
215+
EccKeyFormatHelper.ReadEncryptedPkcs8(
216+
exported,
217+
TemporaryExportPassword,
218+
out _,
219+
out ECParameters ecParameters);
220+
return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters).TryEncode(destination, out bytesWritten);
221+
}
222+
209223
return Key.TryExportKeyBlob(
210224
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
211225
destination,

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)
151151

152152
public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
153153
{
154+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
155+
156+
if (encryptedOnlyExport)
157+
{
158+
const string TemporaryExportPassword = "DotnetExportPhrase";
159+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
160+
EccKeyFormatHelper.ReadEncryptedPkcs8(
161+
exported,
162+
TemporaryExportPassword,
163+
out _,
164+
out ECParameters ecParameters);
165+
return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters).TryEncode(destination, out bytesWritten);
166+
}
167+
154168
return Key.TryExportKeyBlob(
155169
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
156170
destination,

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSACng.ImportExport.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ private byte[] ExportKeyBlob(bool includePrivateParameters)
5252

5353
public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
5454
{
55+
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
56+
57+
if (encryptedOnlyExport)
58+
{
59+
const string TemporaryExportPassword = "DotnetExportPhrase";
60+
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
61+
RSAKeyFormatHelper.ReadEncryptedPkcs8(
62+
exported,
63+
TemporaryExportPassword,
64+
out _,
65+
out RSAParameters rsaParameters);
66+
return RSAKeyFormatHelper.WritePkcs8PrivateKey(rsaParameters).TryEncode(destination, out bytesWritten);
67+
}
68+
5569
return Key.TryExportKeyBlob(
5670
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
5771
destination,

0 commit comments

Comments
 (0)