C#-在多个CMS签名中缓存PIN

C#-在多个CMS签名中缓存PIN,c#,cryptography,smartcard,C#,Cryptography,Smartcard,嗯,我在这里找到的大多数问题/答案都是关于不缓存智能卡PIN的,这与我正在寻找的情况正好相反 我们有一个对多个散列进行签名的控制台应用程序。为此,我们使用,因为我们需要在服务器端验证签名哈希 通常情况下,智能卡的PIN应该在每个进程的CSP中自动缓存,在Windows 7中也是如此,但如果我们在W10中运行代码,则不会。此外,我们还支持CNG和非CNG证书 我们用于签名的方法如下: 公共字符串签名X509(字符串数据、bool chkSignature、字符串时间戳服务器、X509Certifi

嗯,我在这里找到的大多数问题/答案都是关于不缓存智能卡PIN的,这与我正在寻找的情况正好相反

我们有一个对多个散列进行签名的控制台应用程序。为此,我们使用,因为我们需要在服务器端验证签名哈希

通常情况下,智能卡的PIN应该在每个进程的CSP中自动缓存,在Windows 7中也是如此,但如果我们在W10中运行代码,则不会。此外,我们还支持CNG和非CNG证书

我们用于签名的方法如下:

公共字符串签名X509(字符串数据、bool chkSignature、字符串时间戳服务器、X509Certificate2 selectedCertificate)
{
CmsSigner-oSigner=null;
SignedCms oSignedData=null;
string hashText=string.Empty;
尝试
{
if(CHK签名)
{
oSigner=新的CmsSigner();
oSigner.Certificate=所选证书;
字节[]arrDataHashed=HashSHA1(数据);
//散列要签名的文本
ContentInfo=newcontentinfo(arrDataHashed);
//将散列数据放入signedData对象
oSignedData=新签名的CMS(信息);
if(string.IsNullOrEmpty(timestampServer)){
添加(新的Pkcs9SigningTime(DateTime.Now));
}
否则{
TimeStampToken tsToken=GetTSAToken(arrDataHashed,timestampServer);
AsnEncodedData timeData=新的Pkcs9AttributeObject(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdAASigningCertificate.Id,tsToken.GetEncoded());
oSigner.UnsignedAttributes.Add(timeData);
添加(新的Pkcs9SigningTime(tsToken.TimeStampInfo.GenTime.ToLocalTime());
}
//签署数据
计算签名(oSigner,false);
hashText=Convert.ToBase64String(oSignedData.Encode());
}
其他的
{
//只需清除隐藏的哈希文本
hashText=String.Empty;
}
}
捕获(例外情况除外)
{
Console.WriteLine(“ERRNO[“+ex.Message+”]);
返回null;
}
返回hashText;
}
我们迄今为止所做的尝试:
  • 使用RSACryptServiceProvider在CSP中显式保留密钥
  • rsacyptoserviceprovider key=(rsacyptoserviceprovider)cmsSigner.Certificate.PrivateKey;
    key.PersistKeyInCsp=true;
    
    如果我们使用该方法,这是可行的,但正如我前面所说的,我们需要验证服务器端的签名数据,并且我们没有访问证书的权限,因此我们需要一个PKCS信封。如果我使用CMS代码设置这个bool和sign,行为是相同的

  • 以编程方式设置PIN
  • 另一次尝试是通过CryptoContext以编程方式设置PIN,基于以下内容:

    private void SetPinForPrivateKey(X509Certificate2证书,字符串pin){
    如果(证书==null)抛出新的ArgumentNullException(“证书”);
    var key=(rsacyptoserviceprovider)certificate.PrivateKey;
    var providerHandle=IntPtr.Zero;
    var pinBuffer=System.Text.Encoding.ASCII.GetBytes(pin);
    //当证书句柄被释放时,提供程序句柄被隐式释放。
    SafeNativeMethods.Execute(()=>SafeNativeMethods.CryptAcquireContext(参考providerHandle,
    key.CspKeyContainerInfo.KeyContainerName,
    key.CspKeyContainerInfo.ProviderName,
    key.CspKeyContainerInfo.ProviderType,
    SafeNativeMethods.CryptContextFlags.Silent);
    SafeNativeMethods.Execute(()=>SafeNativeMethods.CryptoSetProvParam(providerHandle,
    SafeNativeMethods.CryptParameter.KeyExchangePin,
    pinBuffer,0);
    SafeNativeMethods.Execute(()=>SafeNativeMethods.CertSetCertificateContextProperty(
    证书,句柄,
    安全方法.CertificateProperty.CryptoProviderHandle,
    0,providerHandle));
    }
    
    通过这种方法,我可以通过编程设置PIN来禁用PIN提示符。这里的问题是,我必须第一次读取PIN,以便在后续签名中进行设置

    我尝试使用dwParam PP_ADMIN_PIN和PP_KEYEXCHANGE_PIN从提示中读取PIN,但没有成功。我的两个猜测是:

  • 我没有在正确的时间或方式阅读
  • CMS在内部使用不同的处理程序

  • 问题1: 有没有办法读取Windows提示符中设置的PIN码

    问题2:
    如果无法读取PIN,是否有其他方法强制PIN缓存?

    现在才意识到这个问题仍然没有答案,尽管我们设法绕过了整个“从Windows提示符读取PIN”问题

    这个方法不能回答我的第一个问题,但我会回答第二个问题

    这是智能卡CSP提供程序中的一个错误,它禁用了对
    SignHash
    的所有请求的PIN缓存,即使这些请求是在同一过程中发出的

    智能卡提供商有一个SDK,该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