C# 在windows server中导出和导入证书时出现问题
我在C#的证书编程中遇到了一个问题,详细信息如下。 1-我需要一个导出证书(私钥和公钥)并将此证书导入其他服务器。所以我将其导出并导入到新服务器中(导出和导入在MMC中完成)。 2-我执行以下代码以确保导出和导入的正确性C# 在windows server中导出和导入证书时出现问题,c#,certificate,C#,Certificate,我在C#的证书编程中遇到了一个问题,详细信息如下。 1-我需要一个导出证书(私钥和公钥)并将此证书导入其他服务器。所以我将其导出并导入到新服务器中(导出和导入在MMC中完成)。 2-我执行以下代码以确保导出和导入的正确性 var signedCer = CertificateHelper.GetCertificateFromStoreBySubject("MySubject", StoreName.My, StoreLocation.LocalMachine); if (signedCer !=
var signedCer = CertificateHelper.GetCertificateFromStoreBySubject("MySubject", StoreName.My, StoreLocation.LocalMachine);
if (signedCer != null)
{
var rsapk = signedCer.GetRSAPrivateKey();
var rsaParameter=rsapk.ExportParameters(true);
var z = rsapk.ToXmlString(true);
}
上述代码在旧服务器中正常工作,
但当我在新服务器中执行时,会发生以下错误
The requested operation is not supported.
at System.Security.Cryptography.NCryptNative.ExportKey(SafeNCryptKeyHandle key, String format)
at System.Security.Cryptography.CngKey.Export(CngKeyBlobFormat format)
at System.Security.Cryptography.RSACng.ExportParameters(Boolean includePrivateParameters)
at System.Security.Cryptography.RSA.ToXmlString(Boolean includePrivateParameters)
谁能帮我找到解决这个问题的办法
谢谢。本期的背景
- 我们无法提取pfx的RSA参数的原因
证书使用
是因为 证书在内部使用CNG而不是CryptoAPI(CAPI)certificate.GetRSAPrivateKey.ExportParameters(true)
- 下一代密码术(CNG)是长期的替代品 用于加密API(CAPI)。NG设计为可在多个位置扩展 级别和密码在行为上是不可知的
- CNG出口有两种类型,“出口”和 “明文导出”
- 大多数情况下,默认的出口政策是“出口”,以便我们 获取还必须设置策略的RSA参数 “AllowPlaintextExport”
- 为了设置“AllowPlainTextExport”,我们必须访问非托管 C语言的C++库,
目标细分:
static void Main(string[] args)
{
var certName = "CN=localhost";
var certBytes = GetByteArrayCertificateFromStore(certName, StoreName.My, StoreLocation.CurrentUser);
X509Certificate2 cert = ImportExportable(certBytes, "password", machineScope: false);
var rsaPrivateParams = cert.GetRSAPrivateKey().ExportParameters(true);
store.Close();
}
- 从证书存储中找到要导出其rsa参数的现有pfx
- 调用
,返回所述证书的byteArrayGetByteArrayCertificateFromStore
- 以字节数组作为参数导出证书
- 将
x509keystrageflags的枚举设置为
Exportable
- 导入导出的证书
- 设置导出策略“明文导出”
- 调用
和GetRSAPrivatekey()
ExportParameters()
- 关店
Program.cs
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace certificate
{
public class Program
{
public static X509Store store; public static SafeNCryptKeyHandle keyHandler;
static void Main(string[] args)
{
var certName = "CN=localhost";
var certBytes = GetByteArrayCertificateFromStore(certName, StoreName.My, StoreLocation.CurrentUser);
X509Certificate2 cert = ImportExportable(certBytes, "password", machineScope: false);
var rsaPrivateParams = cert.GetRSAPrivateKey().ExportParameters(true);
store.Close();
}
public static byte [] GetByteArrayCertificateFromStore(string certName, StoreName sname, StoreLocation sLoc)
{
store = new X509Store(sname, sLoc);
try
{
store.Open(OpenFlags.MaxAllowed);
X509Certificate2Collection certCollection = store.Certificates;
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
if (signingCert.Count == 0)
throw new InvalidOperationException();
return signingCert.Export(X509ContentType.Pfx, "password");
}
finally
{
//store.close();
}
}
private static X509Certificate2 ImportExportable(byte[] pfxBytes, string password, bool machineScope)
{
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
if (machineScope)
{
flags |= X509KeyStorageFlags.MachineKeySet;
}
else
{
flags |= X509KeyStorageFlags.UserKeySet;
}
X509Certificate2 cert = new X509Certificate2(pfxBytes, password, flags);
try
{
bool gotKey = NativeMethods.Crypt32.CryptAcquireCertificatePrivateKey(
cert.Handle,
NativeMethods.Crypt32.AcquireCertificateKeyOptions.CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
IntPtr.Zero,
out SafeNCryptKeyHandle keyHandle,
out int keySpec,
out bool callerFree);
if (!gotKey)
{
keyHandle.Dispose();
throw new InvalidOperationException("No private key");
}
if (!callerFree)
{
keyHandle.SetHandleAsInvalid();
keyHandle.Dispose();
throw new InvalidOperationException("Key is not persisted");
}
using (keyHandle)
{
// -1 == CNG, otherwise CAPI
if (keySpec == -1)
{
using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
{
// If the CNG->CAPI bridge opened the key then AllowPlaintextExport is already set.
if ((cngKey.ExportPolicy & CngExportPolicies.AllowPlaintextExport) == 0)
{
FixExportability(cngKey, machineScope);
}
}
}
}
keyHandler = keyHandle;
}
catch
{
cert.Reset();
throw;
}
return cert;
}
internal static void FixExportability(CngKey cngKey, bool machineScope)
{
string password = nameof(NativeMethods.Crypt32.AcquireCertificateKeyOptions);
byte[] encryptedPkcs8 = ExportEncryptedPkcs8(cngKey, password, 1);
string keyName = cngKey.KeyName;
using (SafeNCryptProviderHandle provHandle = cngKey.ProviderHandle)
{
ImportEncryptedPkcs8Overwrite(
encryptedPkcs8,
keyName,
provHandle,
machineScope,
password);
}
}
internal const string NCRYPT_PKCS8_PRIVATE_KEY_BLOB = "PKCS8_PRIVATEKEY";
private static readonly byte[] s_pkcs12TripleDesOidBytes =
System.Text.Encoding.ASCII.GetBytes("1.2.840.113549.1.12.1.3\0");
private static unsafe byte[] ExportEncryptedPkcs8(
CngKey cngKey,
string password,
int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
NativeMethods.NCrypt.PbeParams* pbeParamsPtr = &pbeParams;
byte[] 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 = s_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 = s_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,
};
using (var keyHandle = cngKey.Handle)
{
int result = NativeMethods.NCrypt.NCryptExportKey(
keyHandle,
IntPtr.Zero,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
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,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
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 unsafe void ImportEncryptedPkcs8Overwrite(
byte[] encryptedPkcs8,
string keyName,
SafeNCryptProviderHandle provHandle,
bool machineScope,
string password)
{
SafeNCryptKeyHandle keyHandle;
fixed (char* passwordPtr = password)
fixed (char* keyNamePtr = keyName)
fixed (byte* blobPtr = encryptedPkcs8)
{
NativeMethods.NCrypt.NCryptBuffer* buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[2];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = new IntPtr(passwordPtr),
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsName,
cbBuffer = checked(2 * (keyName.Length + 1)),
pvBuffer = new IntPtr(keyNamePtr),
};
NativeMethods.NCrypt.NCryptBufferDesc desc = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 2,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
NativeMethods.NCrypt.NCryptImportFlags flags =
NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG |
NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_DO_NOT_FINALIZE_FLAG;
if (machineScope)
{
flags |= NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_MACHINE_KEY_FLAG;
}
int errorCode = NativeMethods.NCrypt.NCryptImportKey(
provHandle,
IntPtr.Zero,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
ref desc,
out keyHandle,
new IntPtr(blobPtr),
encryptedPkcs8.Length,
flags);
if (errorCode != 0)
{
keyHandle.Dispose();
throw new Win32Exception(errorCode);
}
using (keyHandle)
using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
{
const CngExportPolicies desiredPolicies =
CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport;
cngKey.SetProperty(
new CngProperty(
"Export Policy",
BitConverter.GetBytes((int)desiredPolicies),
CngPropertyOptions.Persist));
int error = NativeMethods.NCrypt.NCryptFinalizeKey(keyHandle, 0);
if (error != 0)
{
throw new Win32Exception(error);
}
}
}
}
}
internal static class NativeMethods
{
internal static class Crypt32
{
internal enum AcquireCertificateKeyOptions
{
None = 0x00000000,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG = 0x00040000,
}
[DllImport("crypt32.dll", SetLastError = true)]
internal static extern bool CryptAcquireCertificatePrivateKey(
IntPtr pCert,
AcquireCertificateKeyOptions dwFlags,
IntPtr pvReserved,
out SafeNCryptKeyHandle phCryptProvOrNCryptKey,
out int dwKeySpec,
out bool pfCallerFreeProvOrNCryptKey);
}
internal static class NCrypt
{
[DllImport("ncrypt.dll", 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);
[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,
PkcsName = 45,
PkcsSecret = 46,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptOpenStorageProvider(
out SafeNCryptProviderHandle phProvider,
string pszProviderName,
int dwFlags);
internal enum NCryptImportFlags
{
None = 0,
NCRYPT_MACHINE_KEY_FLAG = 0x00000020,
NCRYPT_OVERWRITE_KEY_FLAG = 0x00000080,
NCRYPT_DO_NOT_FINALIZE_FLAG = 0x00000400,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptImportKey(
SafeNCryptProviderHandle hProvider,
IntPtr hImportKey,
string pszBlobType,
ref NCryptBufferDesc pParameterList,
out SafeNCryptKeyHandle phKey,
IntPtr pbData,
int cbData,
NCryptImportFlags dwFlags);
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
}
}
}
输出:
参考文献:
哪一行出错?谢谢Clint,这一行:var rsapameter=rsapk.ExportParameters(true);问题是,当您导入PFX且密钥进入CNG时,它将变为“可导出”而不是“可明文导出”,因此系统将不允许导出它。有一个一次性的变更解决方案,已经得到了回答。(提议的副本即将出现)这是否回答了你的问题@user2352554您好,我只是想知道答案是否能够帮助您找到解决方案。我能够提取私钥;我需要把旗子调准。当我设定为“真”时,它fails@user2352554您好,我只是想知道答案是否能够帮助您找到解决方案。因为我能够用这个解决方案提取私钥