2

我想使用 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

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

4

2 回答 2

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];
于 2016-10-25T22:10:58.320 回答
0

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

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

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

现在手头的问题:您的智能卡上已经有一个密钥对(并根据需要打开了一个会话并登录),您必须使用 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,...);

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

希望有帮助。

于 2016-10-25T22:19:13.660 回答