2

I want to create a digital signature using pkcs11 standard. Lets suppose that I already has a public and private key pair that is stored on my smart card. This keys was generated by using next code:

byte[] ckaId = session.GenerateRandom(20);

// Prepare attribute template of new public key
var publicKeyAttributes = new List<ObjectAttribute>();
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, false));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, PKCS11Settings.ApplicationName));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ENCRYPT, true));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY, true));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY_RECOVER, true));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_WRAP, true));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_MODULUS_BITS, 1024));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PUBLIC_EXPONENT, new byte[] { 0x01, 0x00, 0x01 }));

// Prepare attribute template of new private key
var privateKeyAttributes = new List<ObjectAttribute>();
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, PKCS11Settings.ApplicationName));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SENSITIVE, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN_RECOVER, true));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_UNWRAP, true));

// Specify key generation mechanism
Mechanism mechanism = new Mechanism(CKM.CKM_RSA_PKCS_KEY_PAIR_GEN);

// Generate key pair
session.GenerateKeyPair(mechanism, publicKeyAttributes, privateKeyAttributes, out publicKeyHandle, out privateKeyHandle);

Now I can use these keys to sign some data. For example:

var mechanism = new Mechanism(CKM.CKM_RSA_PKCS);
byte[] byteContent = (ConvertUtils.Utf8StringToBytes("Hello World!!!"));
byte[] signature = session.Sign(mechanism, derivedKey, byteContent);

This code works perfect when you want to create keys and then use it in C_sign method

But how to get an access to the already existing keys to do the similar operation? As I understand I should derive a private key from existing one by using the C_Derrive() method and than use it in C_Sign() method. For this purpose I wrote next code:

// Prepare attribute template of new key
List<ObjectAttribute> objectAttributes = new List<ObjectAttribute>();
objectAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY));
objectAttributes.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_DES3));
objectAttributes.Add(new ObjectAttribute(CKA.CKA_ENCRYPT, true));
objectAttributes.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true));
objectAttributes.Add(new ObjectAttribute(CKA.CKA_DERIVE, true));
objectAttributes.Add(new ObjectAttribute(CKA.CKA_EXTRACTABLE, true));

// Specify key generation mechanism
Mechanism mechanism = new Mechanism(CKM.CKM_RSA_PKCS);

// Generate key
ObjectHandle baseKey = session.GenerateKey(mechanism, objectAttributes);

byte[] dt = session.GenerateRandom(24);

// Specify mechanism parameters
var mechanismParams = new CkKeyDerivationStringData(dt);

// Specify derivation mechanism with parameters
Mechanism mech = new Mechanism(CKM.CKM_RSA_PKCS, mechanismParams);

// Derive key
ObjectHandle derivedKey = session.DeriveKey(mech, baseKey, null);


byte[] byteContent = (ConvertUtils.Utf8StringToBytes("Hello World!"));
byte[] signature = session.Sign(mech, derivedKey, byteContent);

But when I run this code it will throw the next error:

Method C_GenerateKey returned CKR_MECHANISM_INVALID

Could anybody tell me what I'm doing wrong and how to solve this problem ?

jariq
  • 11,681
  • 3
  • 33
  • 52
Tequila
  • 726
  • 7
  • 23

2 Answers2

4

If you want to acquire ObjectHandle for an existing key you need to find the key by its attributes such as label etc. Key derivation is completely different cryptographic operation.

Following code sample searches for the keys you have generated by the code present in your question:

// Prepare attribute template that defines search criteria for public key
List<ObjectAttribute> publicKeyAttributes = new List<ObjectAttribute>();
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PUBLIC_KEY));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_RSA));
publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, PKCS11Settings.ApplicationName));

// Find all objects that match provided attributes
List<ObjectHandle> foundPublicKeys = session.FindAllObjects(publicKeyAttributes);
if (foundPublicKeys == null || foundPublicKeys.Count != 1)
    throw new Exception("Unable to find public key");

// Prepare attribute template that defines search criteria for private key
List<ObjectAttribute> privateKeyAttributes = new List<ObjectAttribute>();
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_RSA));
privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, PKCS11Settings.ApplicationName));

// Find all objects that match provided attributes
List<ObjectHandle> foundPrivateKeys = session.FindAllObjects(publicKeyAttributes);
if (foundPrivateKeys == null || foundPrivateKeys.Count != 1)
    throw new Exception("Unable to find private key");

// Use found object handles
ObjectHandle publicKeyHandle = foundPublicKeys[0];
ObjectHandle privateKeyHandle = foundPrivateKeys[0];
jariq
  • 11,681
  • 3
  • 33
  • 52
  • Thanks a lot! It helps me, but after adding additional key attributes (writed in your answer) in key generation code. – Tequila Oct 27 '16 at 05:26
0

First, I think I'll be referencing pkcs-11v2-20.pdf a few times, so grab it if don't already have a copy (it comes with helpful examples, too).

Second, I'm not a C# programmer, so anything below is unfortunately only pseudo code.

Let's first address the CKR_MECHANISM_INVALID issue: According to the standard, CKM.CKM_RSA_PKCS can't be used for C_DeriveKey (Chp. 12, Table 34).

And now the problem at hand: You already have a key pair on your smart card (and opened a session and logged in as required), you have to search what you need using C_FindObjectsInit, C_FindObjects and C_FindObjectsFinal (pg. 136 and following, also gives some example), where you provide C_FindObjectsInit with an attribute template of what kind of key you are looking for, e.g.

// look for key allowing signing and decrypting
var searchCriteria = new List<ObjectAttribute>();
searchCriteria.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true));
searchCriteria.Add(new ObjectAttribute(CKA.CKA_SIGN, true));

// initialize the search. The number is actually the number of search attributes.
session.FindObjectsInit(searchCriteria, 2);
...
session.FindObjects(out privateKeyHandle, ...);
... 
session.FindObjectsFinal();

// we found the requested private key, now sign the message
session.Sign(..., privateKeyHandle,...);

The key derivation you tried in the last snippet is meant for scenarios where the smart card and the application need to derive one or two shared secret key(s) (e.g. for secure messaging) by means of asymmetric cryptography.

Hope that helped.

ALe
  • 156
  • 1
  • 4