HSM技术精讲(3.6):通用属性详解——所有Object共享的“基因“

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:创建时就不能导出,永远不能改?

这些属性如何锁定密钥的安全边界?

下一节,密钥对象与安全属性。