在读卡器上找到智能卡上的证书

我使用Visual Studio 2013(C#)使用来自智能卡的证书对文档进行数字签名。 我无法识别当前插入读卡器的证书:(

Windows复制插入读卡器中的所有卡的证书,并将其保存在商店中。 我想在读卡器中使用卡片。

我使用的代码是

public static byte[] Sign(Stream inData, string certSubject) { // Access Personal (MY) certificate store of current user X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser); my.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); // Find the certificate we'll use to sign RSACryptoServiceProvider csp = null; foreach (X509Certificate2 cert in my.Certificates) { if (cert.Subject.Contains(certSubject)) { // We found it. // Get its associated CSP and private key if (cert.HasPrivateKey) { csp = (RSACryptoServiceProvider)cert.PrivateKey; if (csp.CspKeyContainerInfo.HardwareDevice) Console.WriteLine("hardware"); Console.WriteLine(cert.ToString()); } } } if (csp == null) { throw new Exception("No valid cert was found"); } // Hash the data SHA1Managed sha1 = new SHA1Managed(); byte[] hash = sha1.ComputeHash(inData); // Sign the hash return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1")); } 

但是当访问cert.PrivateKey用户被提示在读卡器中插入卡时。 如何检测并跳过此提示卡或检测到该证书与读卡器中当前的相应卡?

我只想使用目前读卡器中的智能卡证书。

我担心使用标准.NET API无法检测读卡器中是否存在包含特定X509Certificate2对象的卡。 我能想出的最好的东西(非常hackish)是这样的:

 public static X509Certificate2 GetDefaultCertificateStoredOnTheCard() { // Acquire public key stored in the default container of the currently inserted card CspParameters cspParameters = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider"); RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParameters); string pubKeyXml = rsaProvider.ToXmlString(false); // Find the certficate in the CurrentUser\My store that matches the public key X509Store x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser); x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); foreach (X509Certificate2 cert in x509Store.Certificates) { if ((cert.PublicKey.Key.ToXmlString(false) == pubKeyXml) && cert.HasPrivateKey) return cert; } return null; } 

但是,只有满足以下条件时,此方法才可靠:

  1. 您可以通过minidriver和Microsoft Base Smart Card Crypto Provider访问您的卡。
  2. 只有一台读卡器连接到您的计算机并且存在智能卡。
  3. 当前插入读卡器的卡上只有一个证书。

如果有多个读卡器连接了智能卡或卡上有多个证书,则无法确定此方法将返回哪一个。

请注意,还有其他可用的API可以访问智能卡。 这种API的一个例子是PKCS#11。 对于简单的操作来说,这可能是一种过度杀伤,但它可以让您完全控制您的卡和存储在其上的对象。 如果您有兴趣并且您的智能卡附带了PKCS#11库,您可以查看我的项目Pkcs11Interop ,它为.NET环境带来了PKCS#11 API的全部function。

希望这可以帮助 :)

编辑删除“单证书”限制:

我稍微修改了代码。 它现在使用非托管加密API来枚举Microsoft Base Smart Card Crypto Provider管理的所有容器的名称,然后在CurrentUser \ My store中搜索相应的X509Certificate2对象。 请注意,这种方法也非常糟糕,并且代码可能无法与市场上的所有卡/微型驱动程序可靠地工作。 通常,更好,更容易让用户从内置证书选择对话框中选择正确的证书。

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; namespace CSP { public static class BaseSmartCardCryptoProvider { private const string _providerName = "Microsoft Base Smart Card Crypto Provider"; private static class NativeMethods { public const uint PROV_RSA_FULL = 0x00000001; public const uint CRYPT_VERIFYCONTEXT = 0xF0000000; public const uint CRYPT_FIRST = 0x00000001; public const uint CRYPT_NEXT = 0x00000002; public const uint ERROR_NO_MORE_ITEMS = 0x00000103; public const uint PP_ENUMCONTAINERS = 0x00000002; [DllImport("advapi32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true, SetLastError = true)] public static extern bool CryptAcquireContext( ref IntPtr phProv, [MarshalAs(UnmanagedType.LPStr)] string pszContainer, [MarshalAs(UnmanagedType.LPStr)] string pszProvider, uint dwProvType, uint dwFlags); [DllImport("advapi32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true, SetLastError = true)] public static extern bool CryptGetProvParam( IntPtr hProv, uint dwParam, [MarshalAs(UnmanagedType.LPStr)] StringBuilder pbData, ref uint pdwDataLen, uint dwFlags); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool CryptReleaseContext( IntPtr hProv, uint dwFlags); } public static List GetCertificates() { List certs = new List(); X509Store x509Store = null; try { x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser); x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); List containers = GetKeyContainers(); foreach (string container in containers) { CspParameters cspParameters = new CspParameters((int)NativeMethods.PROV_RSA_FULL, _providerName, container); cspParameters.Flags = CspProviderFlags.UseExistingKey; string pubKeyXml = null; using (RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParameters)) pubKeyXml = rsaProvider.ToXmlString(false); foreach (X509Certificate2 cert in x509Store.Certificates) { if ((cert.PublicKey.Key.ToXmlString(false) == pubKeyXml) && cert.HasPrivateKey) certs.Add(cert); } } } finally { if (x509Store != null) { x509Store.Close(); x509Store = null; } } return certs; } private static List GetKeyContainers() { List containers = new List(); IntPtr hProv = IntPtr.Zero; try { if (!NativeMethods.CryptAcquireContext(ref hProv, null, _providerName, NativeMethods.PROV_RSA_FULL, NativeMethods.CRYPT_VERIFYCONTEXT)) throw new Win32Exception(Marshal.GetLastWin32Error()); uint pcbData = 0; uint dwFlags = NativeMethods.CRYPT_FIRST; if (!NativeMethods.CryptGetProvParam(hProv, NativeMethods.PP_ENUMCONTAINERS, null, ref pcbData, dwFlags)) throw new Win32Exception(Marshal.GetLastWin32Error()); StringBuilder sb = new StringBuilder((int)pcbData + 1); while (NativeMethods.CryptGetProvParam(hProv, NativeMethods.PP_ENUMCONTAINERS, sb, ref pcbData, dwFlags)) { containers.Add(sb.ToString()); dwFlags = NativeMethods.CRYPT_NEXT; } int err = Marshal.GetLastWin32Error(); if (err != NativeMethods.ERROR_NO_MORE_ITEMS) throw new Win32Exception(err); if (hProv != IntPtr.Zero) { if (!NativeMethods.CryptReleaseContext(hProv, 0)) throw new Win32Exception(Marshal.GetLastWin32Error()); hProv = IntPtr.Zero; } } catch { if (hProv != IntPtr.Zero) { if (!NativeMethods.CryptReleaseContext(hProv, 0)) throw new Win32Exception(Marshal.GetLastWin32Error()); hProv = IntPtr.Zero; } throw; } return containers; } } } 

只需调用提供的类的GetCertificates()方法,检查此代码是否适用于您的卡:

 List certs = CSP.BaseSmartCardCryptoProvider.GetCertificates(); 

我想知道为什么当你知道证书科目时,你会通过商店里的所有证书来预知。 我的建议是:

 public static byte[] Sign(Stream inData, string certSubject) { // Access Personal (MY) certificate store of current user X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser); my.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); var foundCerts = my.Certificates.Find(X509FindType.FindBySubjectName, certSubject, true); if (foundCerts.Count == 0) throw new Exception("No valid cert was found"); var cert = foundCerts[0]; RSACryptoServiceProvider csp = null; // let us assume that certSubject is unique if (cert.HasPrivateKey) { csp = (RSACryptoServiceProvider)cert.PrivateKey; if (csp.CspKeyContainerInfo.HardwareDevice) Console.WriteLine("hardware"); Console.WriteLine(cert.ToString()); } else { throw new Exception("No private key assigned to this certificate"); } // Hash the data SHA1Managed sha1 = new SHA1Managed(); byte[] hash = sha1.ComputeHash(inData); // Sign the hash return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1")); } 

如果您不知道确切的主题或希望找到有关此主题的其他证书,这可能对您不起作用。