无法使用 NCryptExportKey 和 NCryptImportKey 重新导入私钥
Cannot re-import private key with NCryptExportKey and NCryptImportKey
我正在尝试重新加载具有不同导出策略的证书私钥来修复此问题 . I reused the code from this 以导出私钥,然后将其导入并将导出策略设置为 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[=10=]");
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,
}
}
}
}
}
证书被导出然后导入。但是,导入的私钥不能重新分配给原始证书。我得到 "The provided key does not match the public key for this certificate" 或 "Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported"。我做错了什么吗?
// 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
CAPI(CspParameters 背后的库)在 Windows 7 或 8.1 上根本无法理解 CNG 中的键;它(理论上)在 10 上支持它,但你肯定必须告诉它密钥位于 CNG 中 (CspParameters.ProviderName)。
此处的代码在 "Microsoft RSA and AES Enhanced Cryptographic Service Provider" 中创建了一个 ProviderType 24 的新 CAPI 密钥,恰好与您的 CNG 密钥具有相同的本地密钥名称。
您没有指定 UseExistingOnly 标志,并且密钥不存在,因此创建了一个新密钥...这就是 public 密钥与证书中的内容不匹配的原因。
// Attempt #2
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
certificate.PrivateKey = rsaCngKey; // Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported.
PrivateKey
属性只支持CAPI,无论是get还是set。该集合使用起来非常危险,因为它不会修改证书对象,它会修改 Windows 证书存储系统中证书的状态...这意味着它还会影响任何其他现在或未来的对象在同一个 (Windows) 证书上运行。
// Attempt #3
certificate.PrivateKey = null;
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaKey); // The provided key does not match the public key for this certificate.
这与尝试 1 创建的新随机密钥相同。
如果您删除尝试 1,然后合并 2 和 3,您应该以
结束
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaCngKey);
这应该有效。 (如果您已经将证书导入到证书库中,则只需将 certWithKey
添加到证书库中,这将具有与 cert.set_PrivateKey
相同的 "everyone suddenly knows about this" 更新更改,除了它的方式更明显的是你要求证书商店进行更改)
我正在尝试重新加载具有不同导出策略的证书私钥来修复此问题
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[=10=]");
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,
}
}
}
}
}
证书被导出然后导入。但是,导入的私钥不能重新分配给原始证书。我得到 "The provided key does not match the public key for this certificate" 或 "Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported"。我做错了什么吗?
// 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
CAPI(CspParameters 背后的库)在 Windows 7 或 8.1 上根本无法理解 CNG 中的键;它(理论上)在 10 上支持它,但你肯定必须告诉它密钥位于 CNG 中 (CspParameters.ProviderName)。
此处的代码在 "Microsoft RSA and AES Enhanced Cryptographic Service Provider" 中创建了一个 ProviderType 24 的新 CAPI 密钥,恰好与您的 CNG 密钥具有相同的本地密钥名称。
您没有指定 UseExistingOnly 标志,并且密钥不存在,因此创建了一个新密钥...这就是 public 密钥与证书中的内容不匹配的原因。
// Attempt #2
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
certificate.PrivateKey = rsaCngKey; // Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported.
PrivateKey
属性只支持CAPI,无论是get还是set。该集合使用起来非常危险,因为它不会修改证书对象,它会修改 Windows 证书存储系统中证书的状态...这意味着它还会影响任何其他现在或未来的对象在同一个 (Windows) 证书上运行。
// Attempt #3
certificate.PrivateKey = null;
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaKey); // The provided key does not match the public key for this certificate.
这与尝试 1 创建的新随机密钥相同。
如果您删除尝试 1,然后合并 2 和 3,您应该以
结束var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaCngKey);
这应该有效。 (如果您已经将证书导入到证书库中,则只需将 certWithKey
添加到证书库中,这将具有与 cert.set_PrivateKey
相同的 "everyone suddenly knows about this" 更新更改,除了它的方式更明显的是你要求证书商店进行更改)