Я пытаюсь перезагрузить закрытый ключ сертификата с другими политиками экспорта, чтобы исправить эту проблему .Я повторно использовал код из этого ответа , чтобы экспортировать закрытый ключ, а затем импортировать его и установить политику экспорта в AllowPlainTextExport.После этого я смогу восстановить исходный сертификат с импортированным закрытым ключом и экспортировать его параметры, если это необходимо.Вот код, который у меня сейчас есть:
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace TestRsaCngExportImport
{
class Program
{
internal const string NcryptPkcs8PrivateKeyBlob = "PKCS8_PRIVATEKEY";
private const int NcryptDoNotFinalizeFlag = 0x00000400;
public const string MicrosoftSoftwareKeyStorageProvider = "Microsoft Software Key Storage Provider";
private static readonly byte[] pkcs12TripleDesOidBytes = Encoding.ASCII.GetBytes("1.2.840.113549.1.12.1.3\0");
static void Main(string[] args)
{
var certificate = CreateCertificate();
FixPrivateKey(certificate);
}
public static void FixPrivateKey(X509Certificate2 certificate)
{
var cngKey = (RSACng)RSACertificateExtensions.GetRSAPrivateKey(certificate);
var exported = ExportPkcs8KeyBlob(cngKey.Key.Handle, "", 1);
var importedKeyName = ImportPkcs8KeyBlob(exported, "", 1);
// Attempt #1
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = importedKeyName;
var rsaKey = new RSACryptoServiceProvider(parameters);
certificate.PrivateKey = rsaKey; // public key doesn't match the private key
// Attempt #2
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
certificate.PrivateKey = rsaCngKey; // Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported.
// Attempt #3
certificate.PrivateKey = null;
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaKey); // The provided key does not match the public key for this certificate.
}
private static X509Certificate2 CreateCertificate()
{
var keyParams = new CngKeyCreationParameters();
keyParams.KeyUsage = CngKeyUsages.Signing;
keyParams.Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
keyParams.ExportPolicy = CngExportPolicies.AllowExport; // here I don't have AllowPlaintextExport
keyParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.None));
var cngKey = CngKey.Create(CngAlgorithm.Rsa, Guid.NewGuid().ToString(), keyParams);
var rsaKey = new RSACng(cngKey);
var req = new CertificateRequest("cn=mah_cert", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); // requires .net 4.7.2
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
return cert;
}
private unsafe static string ImportPkcs8KeyBlob(byte[] exported, string password, int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
var pbeParamsPtr = &pbeParams;
var salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
rng.GetBytes(salt);
pbeParams.Params.cbSalt = salt.Length;
Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);
pbeParams.Params.iIterations = kdfCount;
var keyName = Guid.NewGuid().ToString("D");
fixed (char* passwordPtr = password)
fixed (char* keyNamePtr = keyName)
fixed (byte* oidPtr = pkcs12TripleDesOidBytes)
{
NativeMethods.NCrypt.NCryptOpenStorageProvider(out var safeNCryptProviderHandle, MicrosoftSoftwareKeyStorageProvider, 0);
NativeMethods.NCrypt.NCryptBuffer* buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[4];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = (IntPtr)passwordPtr,
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid,
cbBuffer = pkcs12TripleDesOidBytes.Length,
pvBuffer = (IntPtr)oidPtr,
};
buffers[2] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam,
cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams),
pvBuffer = (IntPtr)pbeParamsPtr,
};
buffers[3] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsKeyName,
cbBuffer = checked(2 * (keyName.Length + 1)),
pvBuffer = (IntPtr)keyNamePtr,
};
var desc2 = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 4,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
var result = NativeMethods.NCrypt.NCryptImportKey(safeNCryptProviderHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc2, out var safeNCryptKeyHandle, exported, exported.Length, NcryptDoNotFinalizeFlag);
if (result != 0)
throw new Win32Exception(result);
var exportPolicyBytes = BitConverter.GetBytes(
(int)(CngExportPolicies.AllowExport |
CngExportPolicies.AllowPlaintextExport |
CngExportPolicies.AllowArchiving |
CngExportPolicies.AllowPlaintextArchiving));
NativeMethods.NCrypt.NCryptSetProperty(safeNCryptKeyHandle, "Export Policy", exportPolicyBytes, exportPolicyBytes.Length, CngPropertyOptions.Persist);
NativeMethods.NCrypt.NCryptFinalizeKey(safeNCryptKeyHandle, 0);
return keyName;
}
}
private static unsafe byte[] ExportPkcs8KeyBlob(SafeNCryptKeyHandle keyHandle, string password, int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
var pbeParamsPtr = &pbeParams;
var salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
rng.GetBytes(salt);
pbeParams.Params.cbSalt = salt.Length;
Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);
pbeParams.Params.iIterations = kdfCount;
fixed (char* stringPtr = password)
fixed (byte* oidPtr = pkcs12TripleDesOidBytes)
{
NativeMethods.NCrypt.NCryptBuffer* buffers =
stackalloc NativeMethods.NCrypt.NCryptBuffer[3];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = (IntPtr)stringPtr,
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid,
cbBuffer = pkcs12TripleDesOidBytes.Length,
pvBuffer = (IntPtr)oidPtr,
};
buffers[2] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam,
cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams),
pvBuffer = (IntPtr)pbeParamsPtr,
};
var desc = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 3,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
int result = NativeMethods.NCrypt.NCryptExportKey(keyHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc, null, 0, out int bytesNeeded, 0);
if (result != 0)
throw new Win32Exception(result);
byte[] exported = new byte[bytesNeeded];
result = NativeMethods.NCrypt.NCryptExportKey(keyHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc, exported, exported.Length, out bytesNeeded, 0);
if (result != 0)
throw new Win32Exception(result);
if (bytesNeeded != exported.Length)
Array.Resize(ref exported, bytesNeeded);
return exported;
}
}
private static class NativeMethods
{
internal static class NCrypt
{
public const string NCryptLibraryName = "ncrypt.dll";
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszAlgId, string pszKeyName, int dwLegacyKeySpec, CngKeyCreationOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider, [MarshalAs(UnmanagedType.LPWStr)] string pszProviderName, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, [Out] out SafeNCryptKeyHandle phKey, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int cbData, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, string pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, IntPtr pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PbeParams
{
internal const int RgbSaltSize = 8;
internal CryptPkcs12PbeParams Params;
internal fixed byte rgbSalt[RgbSaltSize];
}
[StructLayout(LayoutKind.Sequential)]
internal struct CryptPkcs12PbeParams
{
internal int iIterations;
internal int cbSalt;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBufferDesc
{
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBuffer
{
public int cbBuffer;
public BufferType BufferType;
public IntPtr pvBuffer;
}
internal enum BufferType
{
PkcsAlgOid = 41,
PkcsAlgParam = 42,
PkcsAlgId = 43,
PkcsKeyName = 45,
PkcsSecret = 46,
}
}
}
}
}
Сертификат экспортируется, а затем импортируется.Однако импортированный закрытый ключ не может быть переназначен на исходный сертификат.Я получаю либо «Предоставленный ключ не соответствует открытому ключу для этого сертификата», либо «Поддерживаются только асимметричные ключи, которые реализуют алгоритм ICspAsymmetricAlgorithm».Что-то я делаю не так?