简体   繁体   中英

How to provide PIN to access smartcard programmatically?

I'm using certificates to secure my communications between client and server (no code, just endpoint configuration). Certificates are currently stored in ACOS5 smart cards. Everything works very well except that every time when WCF creates a new channel to access the server, the ACOS5 driver asks user to enter “User PIN”. Unfortunately, it happens quite often.

Is there any way to configure driver to cache PIN that user has already entered within current process at least for some time or how can I cache pin and provide it every time programmatically within same session?

I have found some useful in this article :

This is because in previous versions of Windows each CSP would cache the PIN you entered, but Windows 7 actually converts the PIN to a secure token and caches that. Unfortunately there's only one global token cache but the CSPs can't use tokens generated by others, so first the smart card CSP prompts you and caches a token, then SSL prompts you and caches its own token (overwriting the first one), then the smart card system prompts you again (because its cached token is gone).

But I can't use solution that was proposed by author. So what should I do?

Actually I have found answer on my question: the described behavior caused by bug in Advanced Card Systems CSP v1.9. After switching to Alladin eToken application works as it should. So I can't provide PIN from code but it is remembered by CSP after entering and providing from code is not required. More good news: user sees PIN request in familiar dialog from CSP in this case.

This is a way we found and use from many years in our main application:

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

The full post and author is here: http://www.infinitec.de/post/2010/11/22/Setting-the-PIN-of-a-smartcard-programmatically.aspx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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