1

我正在尝试使用 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;
                }
            }
        }
4

1 回答 1

2

我通过反复试验提出了一个似乎可以正常运行的解决方案。我不确定这是否是正确的方法,因为它看起来很复杂,所以任何反馈都将不胜感激。我发现卡的内容包括各种对象,单个密钥对由三个对象组成:一个 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 对象来执行签名操作。

就像我说的,这似乎很迂回,但似乎让我找到了我特定工作流程所需的正确证书/密钥对。希望这个解释可能对某人有用,我也欢迎任何关于这种方法的问题和/或更好的处理方法的反馈。

于 2020-09-05T22:28:37.593 回答