如何使用智能卡和PKCS#11

时间:2016-10-25 11:54:04

标签: rsa digital-signature smartcard pkcs#11 pkcs11interop

我想使用pkcs11标准创建数字签名。 让我们假设我已经有一个存储在我的智能卡上的公钥和私钥对。 此密钥是使用下一个代码生成的:

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);

现在我可以使用这些键来签名一些数据。例如:

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

当您想要创建密钥然后在C_sign方法

中使用它时,此代码非常有用

但是如何访问已存在的密钥来执行类似的操作? 据我所知,我应该使用C_Derrive()方法从现有的私钥中派生私钥,而不是在C_Sign()方法中使用它。 为此我写了下一个代码:

// 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);

但是当我运行这段代码时,它会抛出下一个错误:

  

方法C_GenerateKey返回CKR_MECHANISM_INVALID

有人能告诉我我做错了什么以及如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

如果您想获取现有密钥的ObjectHandle,您需要通过标签等属性查找密钥。密钥派生是完全不同的加密操作。

以下代码示例搜索您在问题中出现的代码生成的密钥:

// 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];

答案 1 :(得分:0)

首先,我想我会多次引用pkcs-11v2-20.pdf,所以如果没有副本就抓住它(它也附带有用的示例)。

其次,我不是C#程序员,所以下面的任何内容都是伪代码。

让我们首先解决CKR_MECHANISM_INVALID问题: 根据标准,CKM.CKM_RSA_PKCS不能用于C_DeriveKey(表12中的第12章)。

现在手头的问题: 您已经在智能卡上有一个密钥对(并打开一个会话并根据需要登录),您必须使用您的搜索 C_FindObjectsInit,C_FindObjects和C_FindObjectsFinal(第136页及以下,也给出了一些例子), 在哪里为C_FindObjectsInit提供一个属性模板,其中包含您要查找的密钥类型,例如:

// 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,...);

您在最后一个代码段中尝试的密钥派生适用于智能卡和应用程序需要通过非对称加密方法派生一个或两个共享密钥(例如,用于安全消息传递)的情况。

希望有所帮助。