3.5 Object与属性:密码世界的“数据容器“
Object是什么?
在PKCS#11的世界里,所有密码资源都被抽象为“Object“(对象)。
Object可以是:
- 密钥:公钥、私钥、对称密钥
- 证书:X.509证书
- 数据:用户自定义数据
- 硬件特性:时钟、计数器
这个抽象的核心思想是:用统一的接口管理不同类型的密码资源。
CK_OBJECT_CLASS:Object的类型标识
PKCS#11定义了多种Object类型,用CK_OBJECT_CLASS常量标识:
/* Object类型定义(来自pkcs11.h) */
#define CKO_DATA 0x00000000 /* 数据对象 */
#define CKO_CERTIFICATE 0x00000001 /* 证书对象 */
#define CKO_PUBLIC_KEY 0x00000002 /* 公钥对象 */
#define CKO_PRIVATE_KEY 0x00000003 /* 私钥对象 */
#define CKO_SECRET_KEY 0x00000004 /* 对称密钥对象 */
#define CKO_HW_FEATURE 0x00000005 /* 硬件特性对象 */
#define CKO_DOMAIN_PARAMETERS 0x00000006 /* 域参数对象 */
#define CKO_MECHANISM 0x00000007 /* 机制对象 */
#define CKO_OTP_KEY 0x00000008 /* OTP密钥对象 */
#define CKO_PROFILE 0x00000009 /* Profile对象 */
每个Object都有一个必需属性:CKA_CLASS,它标识Object的类型。
CK_ATTRIBUTE:属性的通用容器
PKCS#11用CK_ATTRIBUTE结构来表示属性:
/* 属性结构定义(来自pkcs11.h) */
typedef struct CK_ATTRIBUTE {
CK_ATTRIBUTE_TYPE type; /* 属性类型(如CKA_VALUE) */
CK_VOID_PTR pValue; /* 属性值指针 */
CK_ULONG ulValueLen; /* 属性值长度 */
} CK_ATTRIBUTE;
这个结构非常通用:
type:标识是什么属性pValue:指向属性值ulValueLen:属性值的长度
属性模板(Attribute Template):
创建Object或查找Object时,使用“属性模板“——一个CK_ATTRIBUTE数组:
/* 属性模板示例:创建RSA公钥 */
CK_ATTRIBUTE publicKeyTemplate[] = {
{CKA_CLASS, &pubKeyClass, sizeof(pubKeyClass)}, /* Object类型 */
{CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)}, /* 密钥类型 */
{CKA_TOKEN, &bTrue, sizeof(bTrue)}, /* 存储在Token中 */
{CKA_PRIVATE, &bFalse, sizeof(bFalse)}, /* 公开对象 */
{CKA_MODULUS, modulus, modulusLen}, /* RSA模数 */
{CKA_PUBLIC_EXPONENT, exponent, exponentLen}, /* RSA公钥指数 */
{CKA_LABEL, "MyRSAKey", 8}, /* 标签 */
};
CK_OBJECT_HANDLE hPublicKey;
rv = C_CreateObject(hSession, publicKeyTemplate, 7, &hPublicKey);
Object的生命周期
Object的生命周期由属性决定:
Object生命周期:
创建 → 使用 → 销毁
│ │ │
│ │ │
C_CreateObject C_Sign等 C_DestroyObject
或 操作 或
C_GenerateKeyPair 自然消失(Session Object)
Session Object vs Token Object:
| 特性 | Session Object | Token Object |
|---|---|---|
| CKA_TOKEN | FALSE | TRUE |
| 持久性 | Session关闭后消失 | 永久存储 |
| 位置 | Session内存 | Token存储 |
| 用途 | 临时密钥、中间数据 | 长期密钥、证书 |
/* 创建Session Object(临时) */
CK_ATTRIBUTE sessionKeyTemplate[] = {
{CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)},
{CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)},
{CKA_TOKEN, &bFalse, sizeof(bFalse)}, /* FALSE = Session Object */
{CKA_VALUE, aesKey, 32},
};
/* 创建Token Object(持久) */
CK_ATTRIBUTE tokenKeyTemplate[] = {
{CKA_CLASS, &secretKeyClass, sizeof(secretKeyClass)},
{CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)},
{CKA_TOKEN, &bTrue, sizeof(bTrue)}, /* TRUE = Token Object */
{CKA_LABEL, "PermanentAES", 12},
};
Object的访问控制
Object的访问由属性控制:
CKA_PRIVATE:
- TRUE:私有对象,需要登录才能访问
- FALSE:公开对象,不需要登录
/* 公开对象(任何人可访问) */
CK_ATTRIBUTE publicCertTemplate[] = {
{CKA_CLASS, &certClass, sizeof(certClass)},
{CKA_PRIVATE, &bFalse, sizeof(bFalse)}, /* FALSE = 公开 */
{CKA_VALUE, certData, certLen},
};
/* 私有对象(需要登录) */
CK_ATTRIBUTE privateKeyTemplate[] = {
{CKA_CLASS, &privKeyClass, sizeof(privKeyClass)},
{CKA_PRIVATE, &bTrue, sizeof(bTrue)}, /* TRUE = 私有 */
{CKA_SENSITIVE, &bTrue, sizeof(bTrue)}, /* 敏感 */
};
访问规则:
| 对象状态 | 未登录Session | 已登录Session |
|---|---|---|
| 公开对象(CKA_PRIVATE=FALSE) | 可访问 | 可访问 |
| 私有对象(CKA_PRIVATE=TRUE) | 不可访问 | 可访问 |
查找Object
查找Object使用C_FindObjects系列函数:
/* 查找Object的完整流程 */
CK_OBJECT_HANDLE hFoundObjects[10];
CK_ULONG ulFoundCount;
CK_ATTRIBUTE findTemplate[] = {
{CKA_CLASS, &privKeyClass, sizeof(privKeyClass)}, /* 查找私钥 */
{CKA_KEY_TYPE, &rsaKeyType, sizeof(rsaKeyType)}, /* RSA密钥 */
};
// 1. 初始化查找
rv = C_FindObjectsInit(hSession, findTemplate, 2);
// 2. 执行查找
rv = C_FindObjects(hSession, hFoundObjects, 10, &ulFoundCount);
// 3. 结束查找
rv = C_FindObjectsFinal(hSession);
// 使用找到的Object
for (CK_ULONG i = 0; i < ulFoundCount; i++) {
printf("找到私钥:handle = %lu\n", hFoundObjects[i]);
}
Object的属性操作
读取属性:
/* 读取Object属性 */
CK_ATTRIBUTE getTemplate[] = {
{CKA_LABEL, NULL_PTR, 0}, /* 先获取长度 */
};
// 第一次调用:获取属性长度
rv = C_GetAttributeValue(hSession, hObject, getTemplate, 1);
CK_ULONG labelLen = getTemplate[0].ulValueLen;
// 分配内存
CK_CHAR label[labelLen + 1];
getTemplate[0].pValue = label;
// 第二次调用:获取属性值
rv = C_GetAttributeValue(hSession, hObject, getTemplate, 1);
printf("Label: %s\n", label);
修改属性:
/* 修改Object属性(如果允许) */
CK_CHAR newLabel[] = "UpdatedKey";
CK_ATTRIBUTE setTemplate[] = {
{CKA_LABEL, newLabel, sizeof(newLabel)},
};
rv = C_SetAttributeValue(hSession, hObject, setTemplate, 1);
注意:只有CKA_MODIFIABLE = TRUE的Object才能修改属性。
一个类比:保险箱内的物品
让我用保险箱类比来理解Object:
保险箱物品系统:
保险箱(Token)
├── 物品(Object)
│ ├── 类型标签(CKA_CLASS):是钥匙还是证书?
│ ├── 存放位置(CKA_TOKEN):临时存放还是永久存放?
│ ├── 访问权限(CKA_PRIVATE):需要授权才能取吗?
│ ├── 修改权限(CKA_MODIFIABLE):可以改变标签吗?
│ ├── 销毁权限(CKA_DESTROYABLE):可以销毁吗?
│ └── 物品编号(Handle):操作时用什么编号引用?
│
└── 操作
├── 存入物品(C_CreateObject)
├── 查找物品(C_FindObjects)
├── 查看物品信息(C_GetAttributeValue)
├── 更改物品标签(C_SetAttributeValue)
└── 取出销毁(C_DestroyObject)
本篇小结
Object是PKCS#11的“数据容器“,承载所有密码资源。
核心概念:
- CK_OBJECT_CLASS:Object类型标识
- CK_ATTRIBUTE:属性的通用容器(type, pValue, ulValueLen)
- 属性模板:创建/查找Object时的属性数组
Object分类:
- Session Object(临时):CKA_TOKEN = FALSE
- Token Object(持久):CKA_TOKEN = TRUE
访问控制:
- 公开对象:CKA_PRIVATE = FALSE,无需登录
- 私有对象:CKA_PRIVATE = TRUE,需要登录
Object操作:
- C_CreateObject/C_DestroyObject:创建/销毁
- C_FindObjects系列:查找
- C_GetAttributeValue/C_SetAttributeValue:读取/修改属性
下一节,我们将详细解析通用属性——CKA_CLASS、CKA_TOKEN、CKA_LABEL等基础属性的含义和用法。
【下集预告】
Object有几十种属性。
哪些是所有Object都有的“通用属性“?
CKA_CLASS、CKA_TOKEN、CKA_PRIVATE、CKA_LABEL、CKA_ID、CKA_MODIFIABLE、CKA_DESTROYABLE…
这些属性分别控制什么行为?
下一节,通用属性详解。