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 是定位一个密钥对象的“坐标轴”。你只有把这两个属性设置正确了,后续的其他属性才能正确附着上去。
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 共同决定了密钥的大类和具体算法。它们是所有密钥操作的起点。搞清楚了这两个属性,你就能避免至少一半的常见错误。