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?
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.