如何设置CRYPT_FORCE_KEY_PROTECTION_HIGH实现RSACryptoServiceProvider高等级私钥保护?
为导入的PFX证书启用高等级强私钥保护问题
补充说明:正如Luke指出,此前我将CRYPT_FORCE_KEY_PROTECTION_HIGH传入了错误的函数,且标志值也不正确。修正这两处后,用户会弹出设置密钥保护密码的窗口,但访问密钥时仍无需输入密码,目前仍在排查原因。
我需要将公证书和私钥以PFX文件形式导入.NET,目标是将证书添加到当前用户证书存储时,启用高等级强私钥保护——要求添加证书时需输入密码,访问密钥时也需验证密码。我知道可以使用CRYPT_FORCE_KEY_PROTECTION_HIGH标志来设置高等级保护。
我的思路是创建带有该标志的私钥副本,再将其与证书的公钥副本关联。
目前代码运行无异常,证书和私钥已成功添加到存储中,也能正常进行加密/解密操作,但强密钥保护并未启用,操作过程中也没有弹出预期的密码验证窗口。
// certData 来自现有证书的 X509Certificate2.Export(X509ContentType.Pfx) 方法 public static void AddCertificateStrongKeyProtection(byte[] certData) { using (X509Certificate2 certificate = new X509Certificate2(certData, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet)) { IntPtr hProv = IntPtr.Zero; IntPtr hKey = IntPtr.Zero; const string providerName = "Microsoft Enhanced Cryptographic Provider v1.0"; const string containerName = "HighProtectionContainer"; // 获取设置了强保护标志的加密上下文 if (!NCrypt.CryptAcquireContext(ref hProv, containerName, providerName, NCrypt.PROV_RSA_FULL, NCrypt.CRYPT_NEWKEYSET)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } const uint AT_KEYEXCHANGE = 1; const uint CRYPT_FORCE_KEY_PROTECTION_HIGH = 0x00008000; // 生成新密钥 if (!NCrypt.CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_FORCE_KEY_PROTECTION_HIGH, ref hKey)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 通过导出/导入RSA参数将原始RSACng转换为RSACryptoServiceProvider RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(); RSAParameters parameters = certificate.GetRSAPrivateKey().ExportParameters(true); rsaCryptoServiceProvider.ImportParameters(parameters); // 获取RSACryptoServiceProvider的句柄 var keyField = typeof(RSACryptoServiceProvider).GetField("_safeKeyHandle", BindingFlags.NonPublic | BindingFlags.Instance); if (keyField == null) { throw new InvalidOperationException("无法访问 _safeKeyHandle 字段。"); } var safeKeyHandle = (SafeHandle)keyField.GetValue(rsaCryptoServiceProvider); IntPtr originalKey = safeKeyHandle.DangerousGetHandle(); const uint PRIVATEKEYBLOB = 0x7; int blobLength = 0; // 导出原始密钥数据 if (!NCrypt.CryptExportKey(originalKey, IntPtr.Zero, PRIVATEKEYBLOB, 0, null, ref blobLength)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } byte[] keyBlob = new byte[blobLength]; if (!NCrypt.CryptExportKey(originalKey, IntPtr.Zero, PRIVATEKEYBLOB, 0, keyBlob, ref blobLength)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 将原始密钥数据导入到新生成的密钥中 if (!NCrypt.CryptImportKey(hProv, keyBlob, keyBlob.Length, IntPtr.Zero, 0, out hKey)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } CspParameters cspParams = new CspParameters { ProviderType = (int)NCrypt.PROV_RSA_FULL, KeyContainerName = "HighProtectionContainer", // 需与步骤2中使用的容器名一致 Flags = CspProviderFlags.UseExistingKey }; RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams); using (X509Certificate2 cert = new X509Certificate2(certificate.RawData)) { X509Certificate2 newCert = cert.CopyWithPrivateKey(rsaProvider); using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); store.Add(newCert); store.Close(); } } } }
我的P/Invoke签名如下:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool CryptAcquireContext( ref IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool CryptGenKey( IntPtr hProv, uint Algid, uint dwFlags, ref IntPtr phKey); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool CryptExportKey( IntPtr hKey, IntPtr hExpKey, uint dwBlobType, uint dwFlags, byte[] pbData, ref int pdwDataLen); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool CryptImportKey( IntPtr hProv, byte[] pbData, int dwDataLen, IntPtr hPubKey, uint dwFlags, out IntPtr phKey);
内容的提问来源于stack exchange,提问作者t.probst
相关产品推荐
相关产品推荐

