3. 密钥对象属性概述:CKA_CLASS、CKA_KEY_TYPE

好,咱们进入第三章。说实话,密钥对象属性的理解,直接决定了你写出来的 PKCS #11 代码是“能用”还是“随时会崩”。我见过太多人,CKA_CLASS 和 CKA_KEY_TYPE 没搞清楚就开始干活,结果到了密钥导出或者加解密环节,整个逻辑乱成一锅粥。

这一章我们就来把这些核心属性掰开揉碎。

3.1 属性是干什么的?

PKCS #11 里的每个密钥,都有一堆属性。你可以把属性想象成密钥的“身份证信息”。
它告诉 :“我是个什么类型的对象?我能干什么?我该存在哪里?”

没有属性的密钥,是不存在的。我习惯把属性分成两类:

  • 必备属性:创建时就必须填好,少一个都不行。
  • 可选属性:不填也能用,但填了能让密钥行为更精确。

CKA_CLASS 和 CKA_KEY_TYPE,就是最顶层的那两个“必备”属性。

3.2 CKA_CLASS:你是谁?

CKA_CLASS 决定了密钥的基本身份。它的取值其实不多,但在咱们密钥操作里,最常见的是下面三个:

CKA_CLASS 取值 含义
CKO_PUBLIC_KEY 公钥对象,通常可以自由读取,不加密
CKO_PRIVATE_KEY 私钥对象,敏感信息, 会严格控制读写权限
CKO_SECRET_KEY 对称密钥对象,用于加解密、HMAC 等对称操作

你想想看,如果你把 CKA_CLASS 填错了, 会直接拒绝创建操作。我刚开始做 HSM 集成时,就因为把对称密钥的 CLASS 填成了 CKO_PRIVATE_KEY,结果那个对象一直无法用于加解密,搞了整整一个下午才排查出来。

注意:CKA_CLASS 是创建时固定死的,之后大部分 不允许修改。所以,创建前一定要确认好。

3.3 CKA_KEY_TYPE:你具体是哪种?

光知道“我是公钥”还不够, 还要知道“你是 RSA 公钥还是 EC 公钥”。这就是 CKA_KEY_TYPE 的作用。

常见的 CKA_KEY_TYPE 有:

  • CKK_RSA – RSA 密钥对
  • CKK_EC – 椭圆曲线密钥对
  • CKK_AES – AES 对称密钥
  • CKK_GENERIC_SECRET – 通用对称密钥(比如用于 HMAC)
  • CKK_HMAC – HMAC 专用密钥

个人经验:CKA_KEY_TYPE 和 CKA_CLASS 要配对使用。比如 CKA_CLASS 是 CKO_PUBLIC_KEY + CKA_KEY_TYPE 是 CKK_RSA,这就告诉 :我要创建一个 RSA 公钥对象。

如果 CKA_CLASS 是 CKO_SECRET_KEY 但 CKA_KEY_TYPE 填了 CKK_RSA,那 会直接报错。我见过有人踩过这个坑,代码跑了半天才发现类型不匹配。

3.4 其他重要属性(我常用的几个)

除了这两个身份属性,下面几个属性在密钥管理中也很常用:

属性名称 作用
CKA_ID 密钥的标识符,用于关联公钥/私钥对
CKA_LABEL 可读的名称,方便人找密钥
CKA_ 是否存储在 上(还是临时会话密钥)
CKA_PRIVATE 是否需要登录才能访问
CKA_MODIFIABLE 密钥创建后能否修改属性
CKA_EXTRACTABLE 密钥是否允许被导出(敏感操作)

嗯,这里要特别说一下 CKA_EXTRACTABLE。我曾经在一个项目中,为了“方便测试”,把所有的对称密钥都设成了可导出。结果安全审计时直接被亮了红灯。所以,生产环境里,这个属性一定要谨慎设置。

3.5 不同对象类的固定属性对比

不同的 CKA_CLASS,它们“必须有”的属性组合也不同。我整理了下面这张表,平时写代码的时候经常回头看一眼:

对象类 必须有的属性(部分)
CKO_PUBLIC_KEY CKA_CLASS, CKA_KEY_TYPE, CKA_ID, CKA_MODULUS (RSA) 或 CKA_EC_POINT (EC)
CKO_PRIVATE_KEY CKA_CLASS, CKA_KEY_TYPE, CKA_ID, CKA_PRIVATE_EXPONENT (RSA) 或 CKA_VALUE (EC)
CKO_SECRET_KEY CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE_LEN, CKA_VALUE (可选,看 )

公钥和私钥里,CKA_ID 保持一致,才能在操作时让 知道它们是一对。你创建密钥对时, 会自动帮我们处理这个关联关系。

3.6 核心知识结构图

下面是我自己梳理的一张图,把属性和对象类之间的关系串了起来。你看一眼就能明白整个逻辑链条:

密钥对象属性核心结构 密钥对象属性 CKA_CLASS 标识对象大类 CKA_KEY_TYPE 标识具体算法类型 公钥类 私钥类 对称密钥类 RSA / EC / ... RSA / EC / ... AES / HMAC / ... 常用其他属性:CKA_ID, CKA_LABEL, CKA_, CKA_PRIVATE, CKA_EXTRACTABLE 共同构成完整的密钥对象描述

从这张图可以看出,CKA_CLASS 和 CKA_KEY_TYPE 是定位一个密钥对象的“坐标轴”。你只有把这两个属性设置正确了,后续的其他属性才能正确附着上去。

3.7 举个代码例子(习惯看代码的话)

在创建 RSA 公钥时,属性模板通常是这么组织的:

CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
CK_KEY_TYPE keyType = CKK_RSA;
CK_ATTRIBUTE template[] = {
    {CKA_CLASS,     &class,   sizeof(class)},
    {CKA_KEY_TYPE,  &keyType, sizeof(keyType)},
    {CKA_ID,        id,       idLen},
    {CKA_MODULUS,   modulus,  modulusLen},
    {CKA_PUBLIC_EXPONENT, exp, expLen},
    // ... 其他属性
};

看到了吧?前两行就是 CKA_CLASS 和 CKA_KEY_TYPE。我习惯在创建模板时,把这两个属性写在最前面,算是一种“防呆”写法。

总结一下:CKA_CLASS 和 CKA_KEY_TYPE 共同决定了密钥的大类和具体算法。它们是所有密钥操作的起点。搞清楚了这两个属性,你就能避免至少一半的常见错误。