简体   繁体   English

在多个 CMS 签名中缓存 PIN

[英]Caching PIN in multiple CMS Signatures

Well, most of the questions/answers I've found here are regarding not caching a Smartcard PIN which is the opposite case of what I'm looking for.好吧,我在这里找到的大多数问题/答案都是关于不缓存智能卡 PIN,这与我正在寻找的情况相反。

We have a console application that signs multiple hashes.我们有一个控制台应用程序,可以对多个哈希进行签名。 For this we use Pkcs.CmsSigner because we need to validate the signed hashes server-side.为此,我们使用Pkcs.CmsSigner,因为我们需要在服务器端验证签名的哈希值。

Normally a Smartcard's PIN should be cached automatically in the CSP per process and it does in Windows 7, but if we run our code in W10 it does not.通常,智能卡的 PIN 应自动缓存在每个进程的 CSP 中,在 Windows 7 中也是如此,但如果我们在 W10 中运行我们的代码,则不会。 Also we support both CNG and non-CNG certificates.我们还支持 CNG 和非 CNG 证书。

The method we use to sign is the following:我们用来签名的方法如下:

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;
}

What we've tried so far:到目前为止我们尝试过的:

  1. Using RSACryptoServiceProvider to explicitly persist the key in the CSP使用 RSACryptoServiceProvider 在 CSP 中显式持久化密钥
RSACryptoServiceProvider key = (RSACryptoServiceProvider)cmsSigner.Certificate.PrivateKey;
key.PersistKeyInCsp = true; 

This works if we use the SignHash method but as I've said before, we need to verify the signed data server-side and we do not have access to the certificate, therefore we need a PKCS envelope.如果我们使用SignHash方法,这会起作用,但正如我之前所说,我们需要验证已签名的数据服务器端,而我们无权访问证书,因此我们需要一个 PKCS 信封。 If I set this bool and sign using the CMS code the behaviour is the same.如果我设置这个 bool 并使用 CMS 代码签名,则行为是相同的。

  1. Setting the PIN programmatically以编程方式设置 PIN

Another try was setting the PIN programmatically via CryptoContext, based on this answer :另一种尝试是根据以下答案通过 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));
}

With this approach I am able to disable the PIN prompt by setting the PIN programmatically .通过这种方法,我可以通过以编程方式设置 PIN 来禁用 PIN 提示 The problem here is that I have to read the PIN the first time so I can set it in the subsequent signatures.这里的问题是我必须第一次读取 PIN 码,以便我可以在后续签名中设置它。

I've tried to read the PIN from the prompt using CryptoGetProvParam with the dwParam PP_ADMIN_PIN and PP_KEYEXCHANGE_PIN but without luck.我尝试使用CryptoGetProvParam和 dwParam PP_ADMIN_PIN 和 PP_KEYEXCHANGE_PIN 从提示中读取 PIN 但没有运气。 My two guesses here are:我的两个猜测是:

  1. I'm not reading in the right time or way我没有在正确的时间或方式阅读
  2. CMS uses a different handler internally CMS 在内部使用不同的处理程序

Question 1:问题 1:

Is there any way to read the PIN set in the Windows prompt?有什么方法可以读取 Windows 提示中设置的 PIN 码?

W10提示

Question 2:问题2:

If reading the PIN is not possible, is there any other way to force PIN caching?如果无法读取 PIN,还有其他方法可以强制 PIN 缓存吗?

Only now realized this question is still without an answer although we managed to bypass the whole 'read the PIN from Windows prompt' question.现在才意识到这个问题仍然没有答案,尽管我们设法绕过了整个“从 Windows 提示中读取 PIN”的问题。

This method does not answer my first question but I'll be answering the second question.这种方法不能回答我的第一个问题,但我会回答第二个问题。

There was a bug in the smartcard CSP provider that disabled the PIN cache for all requests to SignHash even though they were made in the same process.智能卡 CSP 提供程序中存在一个错误,该错误会禁用对SignHash所有请求的 PIN 缓存,即使它们是在同一进程中发出的。

The smartcard provider has a SDK that exposes some smartcard operations, being one of those an operation to validate the smartcard PIN.智能卡提供商有一个 SDK,它公开一些智能卡操作,其中之一是验证智能卡 PIN 的操作。

What we ended up doing was to create a simple WPF window that requests the user's PIN and uses the SDK to validate the PIN.我们最终做的是创建一个简单的 WPF 窗口,该窗口请求用户的 PIN 并使用 SDK 来验证 PIN。 If it is correct we use the method that I posted in the original question to force the PIN cache:如果正确,我们使用我在原始问题中发布的方法来强制 PIN 缓存:

Another try was setting the PIN programmatically via CryptoContext, based on this answer:另一个尝试是根据以下答案通过 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)); }

With this we are able to request the PIN only one time when signing multiple hashes until the smartcard provider fixes the bug on their side.有了这个,我们只能在签署多个哈希时请求 PIN 一次,直到智能卡提供商修复他们这边的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM