Pkcs11Interop / Pkcs11Interop.X509Store

Easy to use PKCS#11 based X.509 certificate store
Apache License 2.0
32 stars 18 forks source link

Pkcs11Interop 5.1.0 and Pkcs11Interop.X509Store v 0.2.0 Can't Integrate #11

Closed TreezyZhou closed 4 years ago

TreezyZhou commented 4 years ago

Hi, currently I want to get the specific certificate based on the Key Usage of the certificate. Basically what I try to do is--

  1. Using Pkcs11Interop.X509Store to get the Pkcs11X509Certificate's raw data, then convert it to the X509Certificate2, use the X509Certificate2.Extensions to get the Key Usage and find the specific cert.
  2. Use Reflection to get the PrivateKeyHandle in the Pkcs11X509Certificate (which is proved to work on my side).
  3. Try to use this PrivateKeyHandle and Pkcs11Interop to do the Encryption.

The first problem is, Pkcs11Interop 5.1.0 can't integrate with Pkcs11Interop.X509Store v 0.2.0. "Could not load type 'Net.Pkcs11Interop.HighLevelAPI.Pkcs11' from assembly 'Pkcs11Interop, Version=5.1.0.0, Culture=neutral, PublicKeyToken=c10e9c2d8c006d2a'." I have this error message when trying to load library using Pkcs11Interop 5.1.0 and Pkcs11Interop.X509Store v 0.2.0. Only Pkcs11Interop version 4.0.0 seems can work along with Pkcs11Interop.X509Store. But I don't have the documentation on how to use Pkcs11Interop v 4.0.0, like loading the library seems to be quite different from v 5.1.0. Is there a way to use Pkcs11Interop v 5.1.0 and pkcs11Interop.X509Store v 0.2.0 at the same time?

The second problem is, in the third step, to use two libraries, I need to call it separately. Code is like below. Open Pkcs11X509Store: using (var store = new Pkcs11X509Store(libraryPath, pinProvider)) { ....//Get PrivateKeyHandle } Open Pkcs11Interop : using (IPkcs11Library pkcs11Library = Settings.Factories.Pkcs11LibraryFactory.LoadPkcs11Library(Settings.Factories, Settings.Pkcs11LibraryPath, Settings.AppType)) { // Find first slot with token present ISlot slot = Helpers.GetUsableSlot(pkcs11Library);

            // Open RW session
            using (ISession session = slot.OpenSession(SessionType.ReadWrite))
        {
            ...//Use PrivateKeyHandle 
        }
     }

My question is, will the PrivateKeyHandle refer to the same key under these two separate bracket{}? My concern is I open two separate sessions, so will the object handle of the same key be different in these two session? This is the part I'm not sure.

Thank you.

TreezyZhou commented 4 years ago

Actuall I also try to use another method. In this method, I only use one Pkcs11Interop instead of using both Pkcs11Interop and Pkcs11Interop.X509Store. I can successfully get the cert based on the key usage. But then I don't know how to deal with this cert's object handle, using it to encrypt or decrypt. Because the session.Encrypt() and session.Decrypt() requires the object handle of public key or private key, but the object handle I have now is just the cert object handle. Is there any method to do encryption or decryption, or get the private key handle, by using the cert object handle?

Code is below---

using (IPkcs11Library pkcs11Library = Settings.Factories.Pkcs11LibraryFactory.LoadPkcs11Library(Settings.Factories, Settings.Pkcs11LibraryPath, Settings.AppType)) { // Find first slot with token present ISlot slot = Helpers.GetUsableSlot(pkcs11Library);

            // Open RW session
            using (ISession session = slot.OpenSession(SessionType.ReadWrite))
            {
                // Login as normal user
                session.Login(CKU.CKU_USER, Settings.NormalUserPin);                  

                // List certificates in smart card
                List<IObjectAttribute> certificateTemplate = new List<IObjectAttribute>();
                certificateTemplate.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE));
                certificateTemplate.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true));
                certificateTemplate.Add(session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CERTIFICATE_CATEGORY, CKO.CKO_CERTIFICATE));
                List<IObjectHandle> foundCertificatesResult = session.FindAllObjects(certificateTemplate);

                // Attribute template of certificate
                List<CKA> attributeTemplate = new List<CKA>();
                attributeTemplate.Add(CKA.CKA_VALUE);

                // Find the certificate to use (GE or NR)
                int certIndex = -1; // Index of certificate to use

                for (int i = 0; i < foundCertificatesResult.Count; i++)
                {
                    // Get key usage of certificate
                    List<IObjectAttribute> certAttributes = session.GetAttributeValue(foundCertificatesResult[i], attributeTemplate);
                    X509Certificate2 x509Cert2 = new X509Certificate2(certAttributes[0].GetValueAsByteArray());

                    foreach (X509Extension x509Ext in x509Cert2.Extensions)
                    {
                        if ("Key Usage" == x509Ext.Oid.FriendlyName)
                        {
                            X509KeyUsageExtension usageExt = (X509KeyUsageExtension)x509Ext;

                            if (usageExt.KeyUsages == (X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DataEncipherment)) // GE cert
                                //if (usageExt.KeyUsages == (X509KeyUsageFlags.NonRepudiation)) // NR cert
                            {
                                Console.WriteLine("Found certificate at index: " + i);

                                // Update index
                                if (-1 == certIndex)
                                {
                                    certIndex = i;
                                }
                            }
                        }
                    }
                }

                // Check if a cert was found
                if (-1 != certIndex)
                {                  
                    List<ObjectHandle> foundCerts = foundCertificatesResult.ConvertAll(x => (ObjectHandle)x);
                    ObjectHandle geCert = foundCerts[certIndex];                                                                     
                }
                else
                {
                    Console.WriteLine("No valid certificate found in smart card.");
                }               
                session.Logout();
            }
        }
jariq commented 4 years ago

Hello @TreezyZhou,

mixing Pkcs11Interop.X509Store with Pkcs11Interop is very bad idea. Pkcs11Interop.X509Store is high level convenience library but its functionality is intentionally limited (to keep it simple) and it was not designed to be interoperable with low level Pkcs11Interop at all. If you're missing functionality in Pkcs11Interop.X509Store you either add it there or you don't use it at all.

Is there a way to use Pkcs11Interop v 5.1.0 and pkcs11Interop.X509Store v 0.2.0 at the same time?

Pkcs11Interop.X509Store 0.2.0 works only with Pkcs11Interop 4.0.0. Version that supports Pkcs11Interop 5 (which contains API breaking changes) has not been released yet.

I don't have the documentation on how to use Pkcs11Interop v 4.0.0

https://github.com/Pkcs11Interop/Pkcs11Interop/tree/4.0.0/doc

My question is, will the PrivateKeyHandle refer to the same key under these two separate bracket{}?

No. PKCS#11 specification does not require object handles to be the same between library loads.

In this method, I only use one Pkcs11Interop instead of using both Pkcs11Interop and Pkcs11Interop.X509Store.

That's a correct way to go.

Is there any method to do encryption or decryption, or get the private key handle, by using the cert object handle?

Certificate object, private key object and public object are three distinct objects and PKCS#11 specification does not enforce any relation between them. However they all usually have CKA_ID attribute with the same value and that's how Pkcs11Interop.X509Store "pairs" them.

TreezyZhou commented 4 years ago

Thank you very much! Now I use session.GetAttributeValue to get the CKA_ID and process the rest with it.