jjzjj

c# - 在读卡器当前的智能卡上找到证书

coder 2024-05-23 原文

我正在使用 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 对象的卡。我能想到的最好的事情(非常骇人听闻)是这样的:

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. 您的卡可通过微型驱动程序和 Microsoft Base Smart Card Crypto Provider 访问。
  2. 只有一个读卡器连接到您的计算机并带有智能卡。
  3. 当前插入读卡器的卡上只有一张证书。

当有多个读卡器连接了智能卡或卡上存在多个证书时,您无法确定通过此方法返回哪一个。

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

希望这有帮助:)

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

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

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<X509Certificate2> GetCertificates()
        {
            List<X509Certificate2> certs = new List<X509Certificate2>();

            X509Store x509Store = null;

            try
            {
                x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
                x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

                List<string> 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<string> GetKeyContainers()
        {
            List<string> containers = new List<string>();

            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<X509Certificate2> certs = CSP.BaseSmartCardCryptoProvider.GetCertificates();

关于c# - 在读卡器当前的智能卡上找到证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22236116/

有关c# - 在读卡器当前的智能卡上找到证书的更多相关文章

  1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  2. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  3. ruby-on-rails - capybara ::ElementNotFound:无法找到 xpath "/html" - 2

    我正在学习http://ruby.railstutorial.org/chapters/static-pages上的RubyonRails教程并遇到以下错误StaticPagesHomepageshouldhavethecontent'SampleApp'Failure/Error:page.shouldhave_content('SampleApp')Capybara::ElementNotFound:Unabletofindxpath"/html"#(eval):2:in`text'#./spec/requests/static_pages_spec.rb:7:in`(root)'

  4. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. ruby - 如何找到调用当前方法的方法 - 2

    如何找到调用此方法的位置?defto_xml(options={})binding.pryoptions=options.to_hifoptions&&options.respond_to?(:to_h)serializable_hash(options).to_xml(options)end 最佳答案 键入caller。这将返回当前调用堆栈。文档:Kernel#caller.例子[0]%rspecspec10/16|===================================================62=====

  7. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  8. python - 帮我找到合适的 ruby​​/python 解析器生成器 - 2

    我使用的第一个解析器生成器是Parse::RecDescent,它的指南/教程很棒,但它最有用的功能是它的调试工具,特别是tracing功能(通过将$RD_TRACE设置为1来激活)。我正在寻找可以帮助您调试其规则的解析器生成器。问题是,它必须用python或ruby​​编写,并且具有详细模式/跟踪模式或非常有用的调试技术。有人知道这样的解析器生成器吗?编辑:当我说调试时,我并不是指调试python或ruby​​。我指的是调试解析器生成器,查看它在每一步都在做什么,查看它正在读取的每个字符,它试图匹配的规则。希望你明白这一点。赏金编辑:要赢得赏金,请展示一个解析器生成器框架,并说明它的

  9. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

  10. ruby-on-rails - 通过 has_many 找到 Rails :through - 2

    我正在寻找一种方法来通过关联查询基于has_many中的子项的模型。我有3个模型:classConversation我需要找到参与者匹配一组ID的对话。这是我目前拥有的(不工作):Conversation.includes(:participants).where(participants:params[:participants]) 最佳答案 听起来你只是想要对话,如果是这样你可以加入。Conversation.joins(:participants).where(:users=>{:id=>params[:participant

随机推荐