Skip to content

Commit

Permalink
Merge commit '2b46f3a7eb3d0df99c523e5648f00cc8b53caa05'
Browse files Browse the repository at this point in the history
  • Loading branch information
Mirroring committed Oct 23, 2024
2 parents 2df1137 + 2b46f3a commit 0456c7e
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,20 @@ private static unsafe void GenerateV2DsaBlob(out byte[] blob, DSAParameters para

public override DSAParameters ExportParameters(bool includePrivateParameters)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (includePrivateParameters && encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
DSAKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out DSAParameters dsaParameters);
return dsaParameters;
}

byte[] dsaBlob = ExportKeyBlob(includePrivateParameters);

KeyBlobMagicNumber magic = (KeyBlobMagicNumber)BitConverter.ToInt32(dsaBlob, 0);
Expand Down Expand Up @@ -423,6 +437,5 @@ private static void CheckMagicValueOfKey(KeyBlobMagicNumber magic, bool includeP
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,50 +66,12 @@ public override void ImportParameters(ECParameters parameters)

public override ECParameters ExportExplicitParameters(bool includePrivateParameters)
{
byte[] blob = ExportFullKeyBlob(includePrivateParameters);

try
{
ECParameters ecparams = default;
ECCng.ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters);
return ecparams;
}
finally
{
Array.Clear(blob);
}
return ECCng.ExportExplicitParameters(Key, includePrivateParameters);
}

public override ECParameters ExportParameters(bool includePrivateParameters)
{
ECParameters ecparams = default;

string? curveName = GetCurveName(out string? oidValue);
byte[]? blob = null;

try
{
if (string.IsNullOrEmpty(curveName))
{
blob = ExportFullKeyBlob(includePrivateParameters);
ECCng.ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters);
}
else
{
blob = ExportKeyBlob(includePrivateParameters);
ECCng.ExportNamedCurveParameters(ref ecparams, blob, includePrivateParameters);
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
}

return ecparams;
}
finally
{
if (blob != null)
{
Array.Clear(blob);
}
}
return ECCng.ExportParameters(Key, includePrivateParameters);
}

public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Internal.NativeCrypto;

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

/// <summary>
Expand All @@ -103,23 +101,7 @@ public override ECParameters ExportExplicitParameters(bool includePrivateParamet
/// <returns>The key and named curve parameters used by the ECC object.</returns>
public override ECParameters ExportParameters(bool includePrivateParameters)
{
ECParameters ecparams = default;

string? curveName = GetCurveName(out string? oidValue);

if (string.IsNullOrEmpty(curveName))
{
byte[] fullKeyBlob = ExportFullKeyBlob(includePrivateParameters);
ECCng.ExportPrimeCurveParameters(ref ecparams, fullKeyBlob, includePrivateParameters);
}
else
{
byte[] keyBlob = ExportKeyBlob(includePrivateParameters);
ECCng.ExportNamedCurveParameters(ref ecparams, keyBlob, includePrivateParameters);
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
}

return ecparams;
return ECCng.ExportParameters(Key, includePrivateParameters);
}

public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,20 @@ public override bool TryExportEncryptedPkcs8PrivateKey(
/// </summary>
public override RSAParameters ExportParameters(bool includePrivateParameters)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (includePrivateParameters && encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
RSAKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out RSAParameters rsaParameters);
return rsaParameters;
}

byte[] rsaBlob = ExportKeyBlob(includePrivateParameters);
RSAParameters rsaParams = default;
rsaParams.FromBCryptBlob(rsaBlob, includePrivateParameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,32 @@ public void NoPlaintextExportFailsPkcs8()
{
SetExportPolicy(cngKey, CngExportPolicies.AllowExport);

Assert.ThrowsAny<CryptographicException>(
() => key.ExportPkcs8PrivateKey());
byte[] exported = key.ExportPkcs8PrivateKey();

using (T imported = CreateKey(out _))
{
imported.ImportPkcs8PrivateKey(exported, out int importRead);
Assert.Equal(exported.Length, importRead);
VerifyMatch(key, imported);
}

byte[] tryExported = new byte[exported.Length];

int written;

while (!key.TryExportPkcs8PrivateKey(tryExported, out written))
{
Array.Resize(ref tryExported, checked(tryExported.Length * 2));
}

using (T imported = CreateKey(out _))
{
imported.ImportPkcs8PrivateKey(tryExported.AsSpan(0, written), out int tryImportRead);
Assert.Equal(written, tryImportRead);
VerifyMatch(key, imported);
}


Assert.ThrowsAny<CryptographicException>(
() => key.TryExportPkcs8PrivateKey(Span<byte>.Empty, out _));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,11 @@ private static Pkcs8Response ImportPkcs8(
Key = key,
};
}

internal static bool AllowsOnlyEncryptedExport(CngKey key)
{
const CngExportPolicies Exportable = CngExportPolicies.AllowPlaintextExport | CngExportPolicies.AllowExport;
return (key.ExportPolicy & Exportable) == CngExportPolicies.AllowExport;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)

public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
DSAKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out DSAParameters dsaParameters);
return DSAKeyFormatHelper.WritePkcs8(dsaParameters).TryEncode(destination, out bytesWritten);
}

return Key.TryExportKeyBlob(
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
destination,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using static Interop.BCrypt;

namespace System.Security.Cryptography
Expand Down Expand Up @@ -77,6 +78,100 @@ internal static byte[] ExportKeyBlob(
return blob;
}

internal static ECParameters ExportExplicitParameters(CngKey key, bool includePrivateParameters)
{
if (includePrivateParameters)
{
return ExportPrivateExplicitParameters(key);
}
else
{
byte[] blob = ExportFullKeyBlob(key, includePrivateParameters: false);
ECParameters ecparams = default;
ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters: false);
return ecparams;
}
}

internal static ECParameters ExportParameters(CngKey key, bool includePrivateParameters)
{
ECParameters ecparams = default;

const string TemporaryExportPassword = "DotnetExportPhrase";
string? curveName = key.GetCurveName(out string? oidValue);

if (string.IsNullOrEmpty(curveName))
{
if (includePrivateParameters)
{
ecparams = ExportPrivateExplicitParameters(key);
}
else
{
byte[] fullKeyBlob = ExportFullKeyBlob(key, includePrivateParameters: false);
ECCng.ExportPrimeCurveParameters(ref ecparams, fullKeyBlob, includePrivateParameters: false);
}
}
else
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(key);

if (includePrivateParameters && encryptedOnlyExport)
{
byte[] exported = key.ExportPkcs8KeyBlob(TemporaryExportPassword, 1);
EccKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out ecparams);
}
else
{
byte[] keyBlob = ExportKeyBlob(key, includePrivateParameters);
ECCng.ExportNamedCurveParameters(ref ecparams, keyBlob, includePrivateParameters);
ecparams.Curve = ECCurve.CreateFromOid(new Oid(oidValue, curveName));
}
}

return ecparams;
}

private static ECParameters ExportPrivateExplicitParameters(CngKey key)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(key);

ECParameters ecparams = default;

if (encryptedOnlyExport)
{
// We can't ask CNG for the explicit parameters when performing a PKCS#8 export. Instead,
// we ask CNG for the explicit parameters for the public part only, since the parameters are public.
// Then we ask CNG by encrypted PKCS#8 for the private parameters (D) and combine the explicit public
// key along with the private key.
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] publicKeyBlob = ExportFullKeyBlob(key, includePrivateParameters: false);
ExportPrimeCurveParameters(ref ecparams, publicKeyBlob, includePrivateParameters: false);

byte[] exported = key.ExportPkcs8KeyBlob(TemporaryExportPassword, 1);
EccKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out ECParameters localParameters);

Debug.Assert(ecparams.Q.X.AsSpan().SequenceEqual(localParameters.Q.X));
Debug.Assert(ecparams.Q.Y.AsSpan().SequenceEqual(localParameters.Q.Y));
ecparams.D = localParameters.D;
}
else
{
byte[] blob = ExportFullKeyBlob(key, includePrivateParameters: true);
ExportPrimeCurveParameters(ref ecparams, blob, includePrivateParameters: true);
}

return ecparams;
}

private static unsafe void FixupGenericBlob(byte[] blob)
{
if (blob.Length > sizeof(BCRYPT_ECCKEY_BLOB))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)

public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
EccKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out ECParameters ecParameters);
return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters).TryEncode(destination, out bytesWritten);
}

return Key.TryExportKeyBlob(
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
destination,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,20 @@ private void AcceptImport(CngPkcs8.Pkcs8Response response)

public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
EccKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out ECParameters ecParameters);
return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters).TryEncode(destination, out bytesWritten);
}

return Key.TryExportKeyBlob(
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
destination,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ private byte[] ExportKeyBlob(bool includePrivateParameters)

public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);

if (encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
RSAKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out RSAParameters rsaParameters);
return RSAKeyFormatHelper.WritePkcs8PrivateKey(rsaParameters).TryEncode(destination, out bytesWritten);
}

return Key.TryExportKeyBlob(
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
destination,
Expand Down
Loading

0 comments on commit 0456c7e

Please sign in to comment.