c# - 在多个 CMS 签名中缓存 PIN
问题描述
好吧,我在这里找到的大多数问题/答案都是关于不缓存智能卡 PIN 的,这与我正在寻找的情况相反。
我们有一个对多个哈希进行签名的控制台应用程序。为此,我们使用Pkcs.CmsSigner因为我们需要在服务器端验证签名的哈希。
通常,智能卡的 PIN 应该在每个进程的 CSP 中自动缓存,并且在 Windows 7 中会这样做,但如果我们在 W10 中运行我们的代码,则不会。我们还支持 CNG 和非 CNG 证书。
我们用来签名的方法如下:
public string SignX509(string data, bool chkSignature, string timestampServer, X509Certificate2 selectedCertificate)
{
CmsSigner oSigner = null;
SignedCms oSignedData = null;
string hashText = String.Empty;
try
{
if (chkSignature)
{
oSigner = new CmsSigner();
oSigner.Certificate = selectedCertificate;
byte[] arrDataHashed = HashSHA1(data);
// hash the text to sign
ContentInfo info = new ContentInfo(arrDataHashed);
// put the hashed data into the signedData object
oSignedData = new SignedCms(info);
if (string.IsNullOrEmpty(timestampServer)) {
oSigner.SignedAttributes.Add(new Pkcs9SigningTime(DateTime.Now));
}
else {
TimeStampToken tsToken = GetTSAToken(arrDataHashed, timestampServer);
AsnEncodedData timeData = new Pkcs9AttributeObject(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdAASigningCertificate.Id, tsToken.GetEncoded());
oSigner.UnsignedAttributes.Add(timeData);
oSigner.SignedAttributes.Add(new Pkcs9SigningTime(tsToken.TimeStampInfo.GenTime.ToLocalTime()));
}
// sign the data
oSignedData.ComputeSignature(oSigner, false);
hashText = Convert.ToBase64String(oSignedData.Encode());
}
else
{
// just clean the hidden hash text
hashText = String.Empty;
}
}
catch (Exception ex)
{
Console.WriteLine("ERRNO [" + ex.Message + " ]");
return null;
}
return hashText;
}
到目前为止,我们已经尝试过:
- 使用 RSACryptoServiceProvider 将密钥显式保存在 CSP 中
RSACryptoServiceProvider key = (RSACryptoServiceProvider)cmsSigner.Certificate.PrivateKey;
key.PersistKeyInCsp = true;
如果我们使用SignHash方法,这是可行的,但正如我之前所说,我们需要在服务器端验证签名数据,并且我们无权访问证书,因此我们需要一个 PKCS 信封。如果我设置此布尔值并使用 CMS 代码签名,则行为是相同的。
- 以编程方式设置 PIN
另一种尝试是通过 CryptoContext 以编程方式设置 PIN,基于此答案:
private void SetPinForPrivateKey(X509Certificate2 certificate, string pin) {
if (certificate == null) throw new ArgumentNullException("certificate");
var key = (RSACryptoServiceProvider)certificate.PrivateKey;
var providerHandle = IntPtr.Zero;
var pinBuffer = System.Text.Encoding.ASCII.GetBytes(pin);
// provider handle is implicitly released when the certificate handle is released.
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(ref providerHandle,
key.CspKeyContainerInfo.KeyContainerName,
key.CspKeyContainerInfo.ProviderName,
key.CspKeyContainerInfo.ProviderType,
SafeNativeMethods.CryptContextFlags.Silent));
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(providerHandle,
SafeNativeMethods.CryptParameter.KeyExchangePin,
pinBuffer, 0));
SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty(
certificate.Handle,
SafeNativeMethods.CertificateProperty.CryptoProviderHandle,
0, providerHandle));
}
使用这种方法,我可以通过以编程方式设置 PIN 来禁用 PIN 提示。这里的问题是我必须在第一次读取 PIN 时才能在后续签名中设置它。
我尝试使用CryptoGetProvParam与 dwParam PP_ADMIN_PIN 和 PP_KEYEXCHANGE_PIN 从提示中读取 PIN,但没有运气。我的两个猜测是:
- 我没有在正确的时间或方式阅读
- CMS 在内部使用不同的处理程序
问题一:
有什么方法可以读取 Windows 提示符中设置的 PIN 码吗?
问题2:
如果无法读取 PIN,是否有其他方法可以强制 PIN 缓存?
解决方案
直到现在才意识到这个问题仍然没有答案,尽管我们设法绕过了整个“从 Windows 提示符读取 PIN”问题。
这种方法没有回答我的第一个问题,但我将回答第二个问题。
智能卡 CSP 提供程序中存在一个错误,它禁用了所有请求的 PIN 缓存,SignHash
即使它们是在同一进程中发出的。
智能卡提供商有一个 SDK,它公开了一些智能卡操作,是验证智能卡 PIN 的操作之一。
我们最终做的是创建一个简单的 WPF 窗口,该窗口请求用户的 PIN 并使用 SDK 来验证 PIN。如果正确,我们使用我在原始问题中发布的方法来强制 PIN 缓存:
另一种尝试是通过 CryptoContext 以编程方式设置 PIN,基于此答案:
private void SetPinForPrivateKey(X509Certificate2 certificate, string pin) { if (certificate == null) throw new ArgumentNullException("certificate"); var key = (RSACryptoServiceProvider)certificate.PrivateKey; var providerHandle = IntPtr.Zero; var pinBuffer = System.Text.Encoding.ASCII.GetBytes(pin); // provider handle is implicitly released when the certificate handle is released. SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(ref providerHandle, key.CspKeyContainerInfo.KeyContainerName, key.CspKeyContainerInfo.ProviderName, key.CspKeyContainerInfo.ProviderType, SafeNativeMethods.CryptContextFlags.Silent)); SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(providerHandle, SafeNativeMethods.CryptParameter.KeyExchangePin, pinBuffer, 0)); SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty( certificate.Handle, SafeNativeMethods.CertificateProperty.CryptoProviderHandle, 0, providerHandle)); }
有了这个,我们可以在签署多个哈希时只请求一次 PIN,直到智能卡提供商修复他们这边的错误。
推荐阅读
- kubernetes - kubernetes 持久卷声明,大小不受限制
- c - TLB、CPUID 和大页面?
- java - 印刷机用空格制作换行符
- google-apps-script - 将按钮添加到谷歌工作表上的每一行
- jquery - 尝试使用 jquery 禁用提交按钮时出现 UnsatisfiedServletRequestParameterException
- kubernetes - 在 cronjob 和 pod 之间共享数据
- keras - 使用 flow_from_directory 进行训练和验证,无需扩充
- sql-server - 安装 CU14 后,SQL Server 为父节点返回不正确的 XML 值
- jquery - 如何从预先确定的订单中订购控件
- python-requests - Python 客户端证书请求在 Ubuntu 上失败