从私钥读取属性

问题描述

我正在尝试使用pkcs11interop在C#应用程序中使用来自智能卡证书的私钥对消息签名。我们使用的智能卡包含多个证书-通常一个证书用于签名,一个证书用于身份验证。如果我使用的是X509Certificate2,则会根据要查找的X509KeyUsageFlags筛选证书。我正在努力弄清楚如何使用PKCS11来解决这个问题。

我从下面开始的代码如下。当我调用session.FindAllObjects时,结果中将获得2个证书(这是预期的,因为这是智能卡上有多少个证书。)

我尝试使用GetAttributeValue读取各种属性,并查看是否可以使用这些属性来标识正确的证书-奇怪的是,它们都返回null / 0值。查询CKA_SENSITIVE属性将返回True(再次,这是预期的),但是显然我无法从对象中读取其他属性

我在使用GetAttributeValue时做错了什么吗?还是有其他方法可以解决这个问题?

public byte[] SignMessage(byte[] message,string pin)
        {
            var factories = new pkcs11interopFactories();
            using (IPkcs11Library pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories,DriverPath,AppType.SingleThreaded))
            {
                ISlot slot = GetSlot(pkcs11Library);
                if (slot == null)
                {
                    return null;
                }

                using (ISession session = slot.OpenSession(SessionType.ReadWrite))
                {
                    session.Login(CKU.CKU_USER,pin);

                    var searchTemplate = new List<IObjectAttribute> {
                        factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS,CKO.CKO_PRIVATE_KEY),factories.ObjectAttributeFactory.Create(CKA.CKA_KEY_TYPE,CKK.CKK_RSA),factories.ObjectAttributeFactory.Create(CKA.CKA_SIGN,true),};

                    List<IObjectHandle> foundobjects = session.FindAllObjects(searchTemplate); // foundobjects.Count = 2!
                    IObjectHandle privateKey = foundobjects.FirstOrDefault();

                    var readResult = session.GetAttributeValue(privateKey,new List<CKA>() { CKA.CKA_LABEL });
                    var label = readResult[0].GetValueAsstring(); // label ends up being null!

                    byte[] result = null;

                    using (IMechanism signingMechanism = session.Factories.MechanismFactory.Create(CKM.CKM_SHA256_RSA_PKCS))
                    {
                        result = session.Sign(signingMechanism,privateKey,message);
                    }

                    session.DestroyObject(privateKey);

                    session.logout();

                    return result;
                }
            }
        }

解决方法

我通过反复试验提出了一个似乎可以正常运行的解决方案。我不确定这是否是正确的方法,因为这似乎很令人费解,因此我们将不胜感激。我发现卡的内容包括各种对象,一个密钥对包括三个对象:一个CKO_CERTIFICATE对象(似乎包含有关证书/密钥对的元数据首当其冲),一个CKO_PRIVATE_KEY对象和一个CKO_PUBLIC_KEY目的。每个对象都填充了CKA_ID属性,属于同一密钥对的对象应该具有相同的CKA_ID。

因此,我建立了一个CertificateWrapper包装器类来保存对三个对象中每个对象的引用。然后,我遍历了智能卡上的所有对象,并为每个唯一的密钥对构建了CertificateWrapper对象。

然后,我能够使用CKO_CERTIFICATE对象上的CKA_VALUE属性构造一个X509Certificate2对象。从那里,我能够使用我制作的所有X509Certificate2对象的数组构建X509Certificate2Collection对象。然后,我可以在X509Certificate2Collection上使用.Find方法(或我想要的任何其他方法)来筛选出我正在寻找的特定证书。

一旦有了我要查找的X509Certificate2对象,就可以通过将X509Certificate2的序列号与CKO_CERTIFICATE对象的CKA_SERIAL_NUMBER属性进行匹配,将其映射回CertificateWrapper对象。最终,我能够使用与该CKO_CERTIFICATE相关联的CKO_PRIVATE_KEY对象进行签名操作。

就像我说的那样,这似乎很简单,但是似乎可以让我找到特定工作流程所需的正确证书/密钥对。希望这种解释对某人有用,并且我也欢迎就此方法和/或更好的处理方法的问题提出反馈。