【问题标题】:How to reuse existing private key to generate a digital signature using smartcard and PKCS#11如何使用智能卡和 PKCS#11 重用现有私钥生成数字签名
【发布时间】:2017-03-07 11:03:54
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    如果您想为现有密钥获取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];
    

    【讨论】:

    • 非常感谢!它对我有帮助,但是在密钥生成代码中添加了额外的关键属性(写在你的答案中)之后。
    【解决方案2】:

    首先,我想我会多次引用 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,...);
    

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

    希望有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 2017-07-24
      相关资源
      最近更新 更多