如何通过编程提供PIN以访问智能卡?

6
我正在使用证书来保护客户端和服务器之间的通信(没有代码,只有端点配置)。证书目前存储在ACOS5智能卡中。除了每次WCF创建新通道访问服务器时,ACOS5驱动程序都会要求用户输入“用户PIN”外,一切都运作得非常好。不幸的是,这经常发生。
是否有任何方法可以配置驱动程序缓存用户已在当前进程中输入的PIN,至少在某段时间内,或者如何在同一会话中以编程方式缓存pin并在每次提供它时?
我在这个文章中找到了一些有用的信息:
这是因为在之前的Windows版本中,每个CSP都会缓存您输入的PIN码,但Windows 7实际上将PIN码转换为安全令牌并缓存它。不幸的是,只有一个全局令牌缓存,但CSP无法使用其他人生成的令牌,因此首先智能卡CSP提示您并缓存一个令牌,然后SSL提示您并缓存自己的令牌(覆盖第一个令牌),然后智能卡系统再次提示您(因为其缓存的令牌已经消失)。
但我不能使用作者提出的解决方案。那么我该怎么办?
2个回答

4

这是我们多年在主应用程序中发现并使用的一种方式:

static class X509Certificate2Extension
{
    public static void SetPinForPrivateKey(this X509Certificate2 certificate, string pin)
    {
        if (certificate == null) throw new ArgumentNullException("certificate");
        var key = (RSACryptoServiceProvider)certificate.PrivateKey;

        var providerHandle = IntPtr.Zero;
        var pinBuffer = 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));
    }
}

internal static class SafeNativeMethods
{
    internal enum CryptContextFlags
    {
        None = 0,
        Silent = 0x40
    }

    internal enum CertificateProperty
    {
        None = 0,
        CryptoProviderHandle = 0x1
    }

    internal enum CryptParameter
    {
        None = 0,
        KeyExchangePin = 0x20
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptAcquireContext(
        ref IntPtr hProv,
        string containerName,
        string providerName,
        int providerType,
        CryptContextFlags flags
        );

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool CryptSetProvParam(
        IntPtr hProv,
        CryptParameter dwParam,
        [In] byte[] pbData,
        uint dwFlags);

    [DllImport("CRYPT32.DLL", SetLastError = true)]
    internal static extern bool CertSetCertificateContextProperty(
        IntPtr pCertContext,
        CertificateProperty propertyId,
        uint dwFlags,
        IntPtr pvData
        );

    public static void Execute(Func<bool> action)
    {
        if (!action())
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}

完整的帖子和作者在这里:http://www.infinitec.de/post/2010/11/22/Setting-the-PIN-of-a-smartcard-programmatically.aspx

该文讲述了如何通过编程设置智能卡的PIN码。


作者将此答案归功于:https://dev59.com/qEjSa4cB1Zd3GeqPIMIj#3786832 - beppe9000

1
其实我已经找到了我的问题的答案:这种行为是由于Advanced Card Systems CSP v1.9中的错误引起的。切换到Alladin eToken后,应用程序就可以正常工作了。 所以我不能从代码中提供PIN,但在输入后CSP会记住它,不需要从代码中提供。更好的消息是:在这种情况下,用户可以在CSP熟悉的对话框中看到PIN请求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接