| File: System\Security\Cryptography\ECCng.ImportExport.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography) |
// 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 { internal static partial class ECCng { internal static CngKey ImportKeyBlob(byte[] ecBlob, string curveName, bool includePrivateParameters) { CngKeyBlobFormat blobFormat = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicBlob; CngKey newKey = CngKey.Import(ecBlob, curveName, blobFormat); newKey.ExportPolicy |= CngExportPolicies.AllowPlaintextExport; return newKey; } internal static CngKey ImportFullKeyBlob(byte[] ecBlob, bool includePrivateParameters) { CngKeyBlobFormat blobFormat = includePrivateParameters ? CngKeyBlobFormat.EccFullPrivateBlob : CngKeyBlobFormat.EccFullPublicBlob; CngKey newKey = CngKey.Import(ecBlob, blobFormat); newKey.ExportPolicy |= CngExportPolicies.AllowPlaintextExport; return newKey; } internal static byte[] ExportKeyBlob(CngKey key, bool includePrivateParameters) { CngKeyBlobFormat blobFormat = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicBlob; return key.Export(blobFormat); } internal static byte[] ExportFullKeyBlob(CngKey key, bool includePrivateParameters) { CngKeyBlobFormat blobFormat = includePrivateParameters ? CngKeyBlobFormat.EccFullPrivateBlob : CngKeyBlobFormat.EccFullPublicBlob; return key.Export(blobFormat); } internal static byte[] ExportKeyBlob( CngKey key, bool includePrivateParameters, out CngKeyBlobFormat format, out string? curveName) { curveName = key.GetCurveName(out _); bool forceGenericBlob = false; if (string.IsNullOrEmpty(curveName)) { // Normalize curveName to null. curveName = null; forceGenericBlob = true; format = includePrivateParameters ? CngKeyBlobFormat.EccFullPrivateBlob : CngKeyBlobFormat.EccFullPublicBlob; } else { format = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicBlob; } byte[] blob = key.Export(format); // Importing a known NIST curve as explicit parameters NCryptExportKey may // cause it to export with the dwMagic of the known curve and a generic blob body. // This combination can't be re-imported. So correct the dwMagic value to allow it // to import. if (forceGenericBlob) { FixupGenericBlob(blob); } 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)) { fixed (byte* pBlob = blob) { BCRYPT_ECCKEY_BLOB* pBcryptBlob = (BCRYPT_ECCKEY_BLOB*)pBlob; switch ((KeyBlobMagicNumber)pBcryptBlob->Magic) { case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P521_MAGIC: pBcryptBlob->Magic = KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC; break; case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P521_MAGIC: pBcryptBlob->Magic = KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC; break; case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: pBcryptBlob->Magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC; break; case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC: pBcryptBlob->Magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC; break; } } } } } }