c# - X509Certificate2:判断证书私钥是否属于硬件设备,是否需要PIN
问题描述
我正在编写一个程序来进行加密,使用存储在硬件设备(智能卡)上的证书。
- 该程序必须使用 .Net Framework 4.5.2 :( 无法更改。
- 我执行的加密货币没有问题:)
前言
据我了解,当包含一个或多个证书的智能卡连接到计算机时,证书会出现在 Windows 证书存储区(CurrentUser/My)中。
这是由必须安装的智能卡软件管理的。
由于我的软件必须适用于任何硬件设备,最简单的解决方案是弹出一个窗口,让用户选择要使用的证书。
第一个问题
我不想显示 CurrentUser/My 上的所有证书,而只显示那些将相关硬件连接到计算机的证书。
这或多或少是我正在使用的代码:
X509Store storeObject = new X509Store(StoreName.My, StoreLocation.CurrentUser);
storeObject.Open(OpenFlags.ReadOnly);
foreach (var certificate in storeObject.Certificates)
{
bool isValid = false;
if (certificate.HasPrivateKey)
{
try
{
var rsaKey = certificate.PrivateKey as RSACryptoServiceProvider;
if (rsaKey.CspKeyContainerInfo.HardwareDevice)
{
isValid = true;
}
}
catch (Exception ex)
{
// just log
}
}
}
尽管这个解决方案非常难看,但这是我想出的最好的。
如果满足以下条件,则证书属于智能卡:
- HasPrivateKey 是真的。
- 如果智能卡已连接,则将 PrivateKey 转换为 RSACryptoServiceProvider 不会引发异常。
- HardwareDevice 则为真。
问题出在 PrivateKey 演员表上:似乎很多事情都是由 CryptoApi 系统在此演员表期间执行的(当然包括对智能卡软件的调用)。
这非常慢,如果智能卡软件出现某种故障,则会显示无法控制的错误消息,...
问题
有没有其他方法可以确定证书是否属于硬件设备?并确定硬件当前是否已连接?
第二个问题
证书可能需要 PIN。
我必须(这是一个先决条件)避免 Windows 输入框要求输入 PIN。
收集 PIN 并在我的加密操作期间使用它对我来说不是问题。
我的问题是了解是否需要 PIN。
我在网上找到的任何示例都假定如果 rsaKey.CspKeyContainerInfo.HardwareDevice == true 则需要 PIN。
但我不确定。
问题
有什么方法可以确定证书是否需要 PIN 码?
结论
我也喜欢外部库,只要它们是免费的(最好是开源的)和/或来自 Microsoft。
非常感谢!
解决方案
在评论中,我提供了 Win32 互操作解决方案,它可能比延迟 OP 体验更快。提议的解决方案将扩展证书属性以从那里读取提供程序名称(我相信,.NET 在内部的某个地方也是如此?)。然后打开指定的提供者(CSP 或 KSP)并从提供者那里查询硬件能力。此解决方案不接触私钥,也不提示输入 PIN。
调用顺序:
- 调用CertGetCertificateContextProperty函数和查询
CERT_KEY_PROV_INFO_PROP_ID
属性。 - 函数返回一个指向CRYPT_KEY_PROV_INFO结构的指针。
- 结构包含
pwszProvName
存储提供者名称的字段。 - 调用NCryptOpenStorageProvider函数并将提供程序名称作为参数传递。
- 调用NCryptGetProperty函数并传递在上一步中获取的提供程序句柄和属性名称以进行检索。你需要
NCRYPT_IMPL_TYPE_PROPERTY
财产。 - 接收到的值将存储实现类型的按位标志组合。检查返回值(
pbOutput
参数将存储 4 个字节的整数值)是否NCRYPT_IMPL_HARDWARE_FLAG
启用了标志。
调用完成后释放所有非托管资源。虽然这看起来有点复杂,但根据我的经验,这是确定给定证书的密钥存储位置的最快方法。
重要说明
步骤 4 和 5 可能不适用于所有智能卡提供商(假设您使用旧版 CSP),但适用于我的。在这种情况下,将步骤 4 和 5 替换如下:
- 调用CryptAcquireContext函数以获取您的提供者的句柄(在步骤 3 中检索)
- 调用CryptGetProvParam函数来检索提供程序属性。查询
PP_IMPTYPE
属性。pbData
参数将存储一个 4 字节长的字节数组,表示实现类型的按位组合。检查此值是否CRYPT_IMPL_HARDWARE1
启用了标志。
推荐阅读
- javascript - 在新窗口或标签中打开 AJAX 链接
- vba - 使用正则表达式提取规定符号之间的所有子字符串
- macos - 安装 Homebrew 错误:(35)错误:1407742E:SSL
- javascript - JavaScript:必须单击登录按钮两次才能显示电子邮件
- sql - 使用单个存储过程将不同的数据导入到不同的表中
- rest - 迭代到同一个请求时使用哪种状态?
- symfony - 您是否可能忘记为此注释添加“使用”语句?
- android - React-Native Native Android 模块 - Toast 示例
- c - 为什么 C 中的这个数组总是给自己分配垃圾值?
- ms-access - 使用左连接和分组访问更新查询