Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 ObjectToken Object
CKA_TOKENFALSETRUE
持久性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…

这些属性分别控制什么行为?

下一节,通用属性详解。