3.6 通用属性详解:所有Object共享的"基因"
📚 本文内容摘自本人的开源书《HSM技术书 - 从思想实验到安全基石》
🔗 在线阅读/下载:hsm-book
bash
git clone https://github.com/Lularible/hsm-book.git
⭐ 如果对您有帮助,欢迎 Star 支持,也欢迎通过 GitHub Issues 交流讨论。
什么是通用属性?
PKCS#11规范第4.2节定义了一组"通用属性"------所有Object都必须或可能拥有的属性。
这些属性就像Object的"基因",决定了Object的基本行为。
必需属性与可选属性
必需属性(Required):Object必须拥有这些属性,否则创建失败。
可选属性(Optional):Object可以拥有这些属性,也可以不拥有。
通用属性分类:
必需属性(创建时必须提供):
├── CKA_CLASS Object类型(必须)
└── 其他(取决于Object类型)
可选属性(可以提供,也可以不提供):
├── CKA_TOKEN 是否存储在Token中
├── CKA_PRIVATE 是否私有
├── CKA_MODIFIABLE 是否可修改属性
├── CKA_DESTROYABLE 是否可销毁
├── CKA_LABEL 用户标签
├── CKA_ID 用户ID
├── CKA_UNIQUE_ID Token内的唯一ID(由Token生成)
└── CKA_COPYABLE 是否可复制
CKA_CLASS:Object的类型标识
定义:Object的类型。
取值:CK_OBJECT_CLASS常量(CKO_PUBLIC_KEY、CKO_PRIVATE_KEY等)
必需性:所有Object都必须拥有。
c
CK_OBJECT_CLASS objClass = CKO_PUBLIC_KEY;
CK_ATTRIBUTE classAttr = {CKA_CLASS, &objClass, sizeof(objClass)};
意义:CKA_CLASS决定了Object是什么:
- CKO_PUBLIC_KEY → 公钥对象
- CKO_PRIVATE_KEY → 私钥对象
- CKO_SECRET_KEY → 对称密钥对象
- CKO_CERTIFICATE → 证书对象
- CKO_DATA → 数据对象
Token根据CKA_CLASS来决定Object需要哪些其他属性。
CKA_TOKEN:持久性的开关
定义:Object是否存储在Token中(持久化)。
取值:CK_BBOOL(TRUE或FALSE)
默认值:FALSE
意义:
| CKA_TOKEN | Object类型 | 生命周期 | 存储位置 |
|---|---|---|---|
| FALSE | Session Object | Session关闭后消失 | Session内存 |
| TRUE | Token Object | 永久存储 | Token存储 |
c
/* 创建Session Object(临时) */
CK_BBOOL bToken = FALSE;
CK_ATTRIBUTE tokenAttr = {CKA_TOKEN, &bToken, sizeof(bToken)};
// Session关闭后,Object消失
/* 创建Token Object(持久) */
CK_BBOOL bToken = TRUE;
CK_ATTRIBUTE tokenAttr = {CKA_TOKEN, &bToken, sizeof(bToken)};
// Session关闭后,Object仍然存在
// 下次打开Session,Object仍然可用
使用场景:
- Session Object:临时密钥(如协商出的会话密钥)、一次性数据
- Token Object:长期密钥(如CA私钥)、证书、配置数据
CKA_PRIVATE:访问控制的闸门
定义:Object是否私有(需要登录才能访问)。
取值:CK_BBOOL(TRUE或FALSE)
默认值:依赖Token默认行为,通常是TRUE
意义:
| CKA_PRIVATE | 未登录Session | 已登录Session |
|---|---|---|
| FALSE | 可访问 | 可访问 |
| TRUE | 不可访问 | 可访问 |
c
/* 创建公开对象 */
CK_BBOOL bPrivate = FALSE;
CK_ATTRIBUTE privateAttr = {CKA_PRIVATE, &bPrivate, sizeof(bPrivate)};
// 不需要C_Login,可以直接访问
/* 创建私有对象 */
CK_BBOOL bPrivate = TRUE;
CK_ATTRIBUTE privateAttr = {CKA_PRIVATE, &bPrivate, sizeof(bPrivate)};
// 必须C_Login后才能访问
安全意义:
- 公开对象:公钥、证书(本意就是要公开)
- 私有对象:私钥、敏感数据(需要保护)
CKA_MODIFIABLE:属性的锁
定义:Object的属性是否可以修改。
取值:CK_BBOOL(TRUE或FALSE)
默认值:TRUE
意义:
| CKA_MODIFIABLE | C_SetAttributeValue | 备注 |
|---|---|---|
| TRUE | 可以修改属性 | 可以更新标签等 |
| FALSE | 不能修改属性 | 属性锁死 |
c
/* 可修改属性的对象 */
CK_BBOOL bModifiable = TRUE;
CK_ATTRIBUTE modAttr = {CKA_MODIFIABLE, &bModifiable, sizeof(bModifiable)};
// 之后可以修改标签等属性
rv = C_SetAttributeValue(hSession, hObject, setTemplate, 1);
/* 不可修改属性的对象 */
CK_BBOOL bModifiable = FALSE;
CK_ATTRIBUTE modAttr = {CKA_MODIFIABLE, &bModifiable, sizeof(bModifiable)};
// 之后不能修改任何属性(除了特殊属性)
安全意义:
设置CKA_MODIFIABLE = FALSE可以防止属性被篡改,增强安全性。
CKA_DESTROYABLE:销毁的开关
定义:Object是否可以被销毁(C_DestroyObject)。
取值:CK_BBOOL(TRUE或FALSE)
默认值:TRUE
意义:
| CKA_DESTROYABLE | C_DestroyObject | 备注 |
|---|---|---|
| TRUE | 可以销毁 | 可以删除密钥 |
| FALSE | 不能销毁 | 密钥永存 |
c
/* 可销毁的对象 */
CK_BBOOL bDestroyable = TRUE;
CK_ATTRIBUTE destroyAttr = {CKA_DESTROYABLE, &bDestroyable, sizeof(bDestroyable)};
// 之后可以销毁
rv = C_DestroyObject(hSession, hObject);
/* 不可销毁的对象 */
CK_BBOOL bDestroyable = FALSE;
CK_ATTRIBUTE destroyAttr = {CKA_DESTROYABLE, &bDestroyable, sizeof(bDestroyable)};
// 之后不能销毁
rv = C_DestroyObject(hSession, hObject); // 返回CKR_ACTION_PROHIBITED
安全意义:
设置CKA_DESTROYABLE = FALSE可以防止密钥被意外或恶意删除。
CKA_LABEL与CKA_ID:用户的标记
CKA_LABEL:用户定义的标签(字符串)。
CKA_ID:用户定义的ID(字节数组)。
这两个属性都是可选的,用于方便用户识别Object。
c
/* 设置标签和ID */
CK_CHAR label[] = "MySigningKey";
CK_BYTE id[] = {0x01, 0x02, 0x03};
CK_ATTRIBUTE labelAttr = {CKA_LABEL, label, sizeof(label)};
CK_ATTRIBUTE idAttr = {CKA_ID, id, sizeof(id)};
使用场景:
c
/* 通过标签查找密钥 */
CK_CHAR label[] = "MySigningKey";
CK_ATTRIBUTE findTemplate[] = {
{CKA_LABEL, label, sizeof(label)},
};
rv = C_FindObjectsInit(hSession, findTemplate, 1);
rv = C_FindObjects(hSession, &hKey, 1, &ulCount);
rv = C_FindObjectsFinal(hSession);
Label与ID的区别:
| 属性 | 类型 | 用途 |
|---|---|---|
| CKA_LABEL | 字符串 | 人可读的描述 |
| CKA_ID | 字节数组 | 程序可用的标识 |
通常,公钥和私钥使用相同的CKA_ID,便于关联密钥对。
CKA_COPYABLE:复制的开关
定义:Object是否可以被复制(C_CopyObject)。
取值:CK_BBOOL(TRUE或FALSE)
默认值:TRUE
c
/* 可复制的对象 */
CK_BBOOL bCopyable = TRUE;
CK_ATTRIBUTE copyAttr = {CKA_COPYABLE, &bCopyable, sizeof(bCopyable)};
// 之后可以复制
CK_OBJECT_HANDLE hNewObject;
rv = C_CopyObject(hSession, hObject, newTemplate, 1, &hNewObject);
/* 不可复制的对象 */
CK_BBOOL bCopyable = FALSE;
CK_ATTRIBUTE copyAttr = {CKA_COPYABLE, &bCopyable, sizeof(bCopyable)};
// 之后不能复制
CKA_UNIQUE_ID:Token生成的唯一标识符
定义 :PKCS#11 v3.1标准通用属性(0x0000010B),由Token为每个Object自动生成的唯一标识符。
特点:
- 由Token自动生成,不能手动设置
- 保证Token内唯一
- 用于内部管理
c
/* 读取UNIQUE_ID */
CK_ATTRIBUTE uniqueIdAttr = {CKA_UNIQUE_ID, NULL_PTR, 0};
// 先获取长度
rv = C_GetAttributeValue(hSession, hObject, &uniqueIdAttr, 1);
// 分配内存,读取值
CK_BYTE uniqueId[uniqueIdAttr.ulValueLen];
uniqueIdAttr.pValue = uniqueId;
rv = C_GetAttributeValue(hSession, hObject, &uniqueIdAttr, 1);
通用属性的完整列表
根据PKCS#11 v3.1规范第4.2节:
| 属性 | 类型 | 必需性 | 默认值 | 描述 |
|---|---|---|---|---|
| CKA_CLASS | CK_OBJECT_CLASS | 必需 | - | Object类型 |
| CKA_TOKEN | CK_BBOOL | 可选 | FALSE | 是否存储在Token中 |
| CKA_PRIVATE | CK_BBOOL | 可选 | Token默认 | 是否私有 |
| CKA_MODIFIABLE | CK_BBOOL | 可选 | TRUE | 是否可修改属性 |
| CKA_DESTROYABLE | CK_BBOOL | 可选 | TRUE | 是否可销毁 |
| CKA_LABEL | CK_UTF8CHAR\[\] | 可选 | 空 | 用户标签 |
| CKA_ID | CK_BYTE\[\] | 可选 | 空 | 用户ID |
| CKA_UNIQUE_ID | CK_BYTE\[\] | Token生成 | - | 唯一标识 |
| CKA_COPYABLE | CK_BBOOL | 可选 | TRUE | 是否可复制 |
属性的组合效应
这些通用属性可以组合,产生不同的效果:
组合一:最高安全密钥
最高安全密钥属性组合:
CKA_CLASS = CKO_PRIVATE_KEY 私钥对象
CKA_TOKEN = TRUE 持久存储
CKA_PRIVATE = TRUE 需要登录
CKA_MODIFIABLE = FALSE 属性不可修改
CKA_DESTROYABLE = FALSE 不可销毁
CKA_COPYABLE = FALSE 不可复制
CKA_SENSITIVE = TRUE 密钥值不可读取(密钥对象属性)
CKA_EXTRACTABLE = FALSE 密钥不可导出(密钥对象属性)
效果:
- 密钥永久存在
- 需要登录才能使用
- 属性不可修改
- 密钥不可销毁
- 密钥不可复制
- 密钥值不可读取
- 密钥不可导出
= 密钥"锁死"在Token中
组合二:临时会话密钥
临时会话密钥属性组合:
CKA_CLASS = CKO_SECRET_KEY 对称密钥对象
CKA_TOKEN = FALSE 不持久(Session Object)
CKA_PRIVATE = TRUE 需要登录
CKA_MODIFIABLE = TRUE 可修改
CKA_DESTROYABLE = TRUE 可销毁
CKA_COPYABLE = TRUE 可复制
效果:
- Session关闭后密钥消失
- 需要登录才能使用
- 可以修改标签等
- 可以销毁
- 可以复制
= 临时密钥,灵活使用
组合三:公开证书
公开证书属性组合:
CKA_CLASS = CKO_CERTIFICATE 证书对象
CKA_TOKEN = TRUE 持久存储
CKA_PRIVATE = FALSE 公开(不需要登录)
CKA_MODIFIABLE = FALSE 属性不可修改
CKA_DESTROYABLE = TRUE 可销毁
效果:
- 证书永久存储
- 不需要登录就能查看
- 属性不可修改(防止篡改)
- 可以销毁(可以删除证书)
= 公开但防篡改的证书
本篇小结
通用属性是所有Object共享的"基因",定义了Object的基本行为。
必需属性:
- CKA_CLASS:Object类型,所有Object必须有
可选属性(控制生命周期、访问、操作):
- CKA_TOKEN:持久性开关(FALSE=临时,TRUE=永久)
- CKA_PRIVATE:访问闸门(FALSE=公开,TRUE=私有)
- CKA_MODIFIABLE:属性锁(FALSE=不可修改)
- CKA_DESTROYABLE:销毁开关(FALSE=不可销毁)
- CKA_COPYABLE:复制开关(FALSE=不可复制)
用户标记属性:
- CKA_LABEL:人可读标签
- CKA_ID:程序可用ID
Token生成属性:
- CKA_UNIQUE_ID:Token内的唯一标识
下一节,我们将深入密钥对象的专属属性------CKA_SENSITIVE、CKA_EXTRACTABLE、CKA_ALWAYS_SENSITIVE、CKA_NEVER_EXTRACTABLE等安全属性。
【下集预告】
密钥对象有专属的安全属性。
CKA_SENSITIVE:密钥值是否可读取?
CKA_EXTRACTABLE:密钥是否可导出?
CKA_ALWAYS_SENSITIVE:创建时就敏感,永远不能改?
CKA_NEVER_EXTRACTABLE:创建时就不能导出,永远不能改?
这些属性如何锁定密钥的安全边界?
下一节,密钥对象与安全属性。