使用 Pkcs11Interop 创建签名,无需令牌密码

发布于 2025-01-17 12:13:12 字数 3062 浏览 4 评论 0原文

我将 Pkcs11Interop 与 U 盘上的证书结合使用来签署 pdf 文档。

执行以下步骤来签署文档:

  • 加载 pkcs11 库 (LoadPkcs11Library)
  • 获取所选智能卡/USB 令牌的插槽
  • 在插槽上打开会话(需要插槽引脚)
  • 在会话上执行登录
  • 搜索私钥句柄
  • 对文档进行签名(需要 PIN)

对应代码:

  private byte[] GetSignatureFromHashViaPkcs11(byte[] hash, string pin)
        {
            Pkcs11InteropFactories factories = new Pkcs11InteropFactories();
            using (IPkcs11Library pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, PKCS11LibPath, AppType.SingleThreaded))
            {
                ISlot slot = LoadSlot(pkcs11Library, CertificateToUse.BelongsToSmartCardSlot.TokenSerial);
                using (ISession session = slot.OpenSession(SessionType.ReadOnly))
                {
                    session.Login(CKU.CKU_USER, pin);

                    //Search the private key based on the pulblic key CKA_ID
                    IObjectHandle keyHandle = null;
                    var searchTemplate = new List<IObjectAttribute>()
                    {
                        //CKO_PRIVATE_KEY in order to get handle for the private key
                        session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
                        //CKA_ID searching for the private key which matches the public key
                        session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, CertificateToUse.PublicKeyCKAID.GetValueAsByteArray()),
                    };

                    //filter the search result. Since we search for a private key, only one is returned for each certificate
                    var search = session.FindAllObjects(searchTemplate);
                    keyHandle = search.FirstOrDefault();

                    if (keyHandle == null || (keyHandle.ObjectId == CK.CK_INVALID_HANDLE))
                    {
                        throw new Exception("Unable to read SmartCard KeyHandle. Make sure the correct PKCS11 Library is used");
                    }

                    try
                    {
                        //Create the signature (using the pin)
                        var pinAsByteArray = UTF8Encoding.UTF8.GetBytes(pin);
                        using (IMechanism mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_SHA256_RSA_PKCS))
                            return session.Sign(mechanism, keyHandle, pinAsByteArray, hash);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("Error creating the signature", ex);
                    }
                }
            }
        }

如果插槽 PIN 和私钥 PIN 相同,则此解决方案有效。 如果这些引脚不同,则上述代码将不起作用,因为只使用了一个引脚。

如果我在代码中手动管理插槽引脚和私钥引脚,则一切正常。

但似乎应该可以创建签名,而无需之前执行 OpenSession。 我的客户有一个旧工具,只需要私钥密码即可签署文档(这意味着在技术上可以在没有插槽密码的情况下进行签名)。

我的问题是我目前需要进行会话。使用插槽引脚登录才能获取私钥句柄。

问题:是否有另一种方法可以使用 Pkcs11Interop 签署文档,而无需先进行 session.Login。或者是否有另一种方法可以获取私钥句柄而无需先进行 session.Login?

I'm using the Pkcs11Interop in combination with a certificate on a usb stick to sign pdf documents.

The following steps are executed to sign a document:

  • Load the pkcs11 library (LoadPkcs11Library)
  • Get a slot of the selected smartcard/usb token
  • OpenSession on the slot (Requires the slot pin)
  • Perform login on the session
  • Search the private key handle
  • sign the document (Requires the pin)

Corresponding code:

  private byte[] GetSignatureFromHashViaPkcs11(byte[] hash, string pin)
        {
            Pkcs11InteropFactories factories = new Pkcs11InteropFactories();
            using (IPkcs11Library pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, PKCS11LibPath, AppType.SingleThreaded))
            {
                ISlot slot = LoadSlot(pkcs11Library, CertificateToUse.BelongsToSmartCardSlot.TokenSerial);
                using (ISession session = slot.OpenSession(SessionType.ReadOnly))
                {
                    session.Login(CKU.CKU_USER, pin);

                    //Search the private key based on the pulblic key CKA_ID
                    IObjectHandle keyHandle = null;
                    var searchTemplate = new List<IObjectAttribute>()
                    {
                        //CKO_PRIVATE_KEY in order to get handle for the private key
                        session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
                        //CKA_ID searching for the private key which matches the public key
                        session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, CertificateToUse.PublicKeyCKAID.GetValueAsByteArray()),
                    };

                    //filter the search result. Since we search for a private key, only one is returned for each certificate
                    var search = session.FindAllObjects(searchTemplate);
                    keyHandle = search.FirstOrDefault();

                    if (keyHandle == null || (keyHandle.ObjectId == CK.CK_INVALID_HANDLE))
                    {
                        throw new Exception("Unable to read SmartCard KeyHandle. Make sure the correct PKCS11 Library is used");
                    }

                    try
                    {
                        //Create the signature (using the pin)
                        var pinAsByteArray = UTF8Encoding.UTF8.GetBytes(pin);
                        using (IMechanism mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_SHA256_RSA_PKCS))
                            return session.Sign(mechanism, keyHandle, pinAsByteArray, hash);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("Error creating the signature", ex);
                    }
                }
            }
        }

This solution works if the slot pin and the private key pin are the same.
In case those pins are different the above code doesn't work since only one pin is used.

If i manage the slot pin and private key pin manually in code, everything works.

But it seems it should be possible to create a signature without having to perform an OpenSession before.
My customer has an old tool which does only require the private key pin in order to sign a document (Which means it is technically possible to sign without having the slot pin).

My problem is that i currently require to do the session.Login with the slot pin in order to get the private key handle.

Question: Is there another way of signing a document using Pkcs11Interop without having to first do a session.Login. Or is there another way of getting the private key handle without having to first do a session.Login?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

淑女气质 2025-01-24 12:13:12

如果客户不愿意信任您的插槽 pin,也许您可​​以构建一个中间适配器服务,该服务反过来可以执行签名并将此功能发布为具有某种身份验证的 api,您可以将有效负载传递到其中,它将签名并返回它。

然后,客户端可以自由地管理中间组件并使用插槽引脚对其进行初始化。

If the customer is not willing to trust you with the slot pin perhaps you can build an intermediate adaptor service which in turn can perform the signing and publishes this capability as an api with some authentication that you can pass the payload into and it will sign and return it.

The client would then be free to manage the intermediate component and initialise it with slot pin.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文