3.8 证书对象:数字世界的"身份证明"
📚 本文内容摘自本人的开源书《HSM技术书 - 从思想实验到安全基石》
🔗 在线阅读/下载:hsm-book
bash
git clone https://github.com/Lularible/hsm-book.git
⭐ 如果对您有帮助,欢迎 Star 支持,也欢迎通过 GitHub Issues 交流讨论。
证书对象概述
PKCS#11规范第4.6节定义了证书对象(CKO_CERTIFICATE),用于存储公钥证书和属性证书。
证书对象分类:
CKO_CERTIFICATE(证书对象类)
├── CKC_X_509 X.509公钥证书(最常用)
├── CKC_WTLS WTLS公钥证书(移动设备)
└── CKC_X_509_ATTR_CERT X.509属性证书
Cryptoki的态度:规范明确指出------"Cryptoki不赋予证书任何特殊意义"。证书对象只是一个存储容器,PKCS#11不负责验证证书有效性、不检查证书链、不解析证书内容。这些工作由应用程序完成。
这体现了PKCS#11"只提供机制,不提供策略"的设计哲学。
证书对象的通用属性
所有证书对象共享以下属性:
| 属性 | 数据类型 | 含义 |
|---|---|---|
| CKA_CERTIFICATE_TYPE | CK_CERTIFICATE_TYPE | 证书类型(CKC_X_509等) |
| CKA_TRUSTED | CK_BBOOL | 证书是否可信(仅SO可设置) |
| CKA_CERTIFICATE_CATEGORY | CK_CERTIFICATE_CATEGORY | 证书类别(用户证书/CA证书/其他) |
| CKA_CHECK_VALUE | Byte array | 校验值(SHA-1的前3字节) |
| CKA_START_DATE | CK_DATE | 证书起始日期(仅参考) |
| CKA_END_DATE | CK_DATE | 证书终止日期(仅参考) |
| CKA_PUBLIC_KEY_INFO | Byte array | SubjectPublicKeyInfo的DER编码 |
关键约束:
- CKA_CERTIFICATE_TYPE创建后不可修改
- CKA_TRUSTED只能由SO或Token初始化程序设置为CK_TRUE,可信证书不可修改
- CKA_CERTIFICATE_CATEGORY创建后不可修改
- CKA_CHECK_VALUE = SHA-1(CKA_VALUE)的前3字节
CKA_CERTIFICATE_CATEGORY:证书的三种角色
这个属性标识证书的"身份角色":
证书类别:
CK_CERTIFICATE_CATEGORY_UNSPECIFIED 未指定(默认)
CK_CERTIFICATE_CATEGORY_TOKEN_USER 用户证书(对应私钥在Token上)
CK_CERTIFICATE_CATEGORY_AUTHORITY CA证书(签发者证书)
CK_CERTIFICATE_CATEGORY_OTHER_ENTITY 其他实体证书
用途:帮助应用程序区分证书用途。
- "Token User"表示该证书对应的私钥存在于此Token(可用于签名/解密)
- "Authority"表示这是一个CA证书(可用于验证其他证书)
- "Other Entity"表示第三方证书(没有对应私钥)
X.509公钥证书对象
最常用的证书类型(CKC_X_509),存储标准的X.509公钥证书。
专属属性:
| 属性 | 数据类型 | 含义 |
|---|---|---|
| CKA_SUBJECT | Byte array | 证书主体的DER编码(必须) |
| CKA_ID | Byte array | 密钥标识符(关联公私钥) |
| CKA_ISSUER | Byte array | 签发者的DER编码 |
| CKA_SERIAL_NUMBER | Byte array | 序列号 |
| CKA_VALUE | Byte array | 证书BER编码(必须) |
| CKA_URL | RFC2279 string | 证书URL(VALUE为空时使用) |
| CKA_HASH_OF_SUBJECT_PUBLIC_KEY | Byte array | 主体公钥的哈希 |
| CKA_HASH_OF_ISSUER_PUBLIC_KEY | Byte array | 签发者公钥的哈希 |
创建示例:
c
CK_OBJECT_CLASS class = CKO_CERTIFICATE;
CK_CERTIFICATE_TYPE certType = CKC_X_509;
CK_UTF8CHAR label[] = "Server Certificate";
CK_BYTE subject[] = {...}; // DER编码的主体名
CK_BYTE id[] = {0x01}; // 与公钥/私钥共享的ID
CK_BYTE certificate[] = {...}; // BER编码的完整证书
CK_BBOOL true = CK_TRUE;
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &class, sizeof(class)},
{CKA_CERTIFICATE_TYPE, &certType, sizeof(certType)},
{CKA_TOKEN, &true, sizeof(true)},
{CKA_LABEL, label, sizeof(label)-1},
{CKA_SUBJECT, subject, sizeof(subject)},
{CKA_ID, id, sizeof(id)},
{CKA_VALUE, certificate, sizeof(certificate)}
};
CK_OBJECT_HANDLE hCert;
rv = C_CreateObject(hSession, template, 7, &hCert);
CKA_ID:密钥与证书的"纽带"
CKA_ID是连接公钥、私钥、证书三者的关键属性。
理想关联关系:
┌───────────────────┐
│ X.509证书对象 │ CKA_ID = 0x01
│ CKA_SUBJECT │
│ CKA_VALUE │
└───────────────────┘
│
│ CKA_ID相同
▼
┌───────────────────┐
│ 公钥对象 │ CKA_ID = 0x01
│ CKA_CLASS = │
│ CKO_PUBLIC_KEY │
└───────────────────┘
│
│ CKA_ID相同
▼
┌───────────────────┐
│ 私钥对象 │ CKA_ID = 0x01
│ CKA_CLASS = │
│ CKO_PRIVATE_KEY │
│ CKA_SENSITIVE=TRUE│
└───────────────────┘
规范建议:同一主体的公钥、私钥、证书应该使用相同的CKA_ID值,方便应用程序查找关联对象。
实际约束:Cryptoki不强制这种关联,也不强制CKA_ID的唯一性。应用程序需要自行维护这种关联关系。
CKA_URL:移动设备的轻量存储
当证书较大时,可以选择存储URL而非证书本身:
c
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &class, sizeof(class)},
{CKA_CERTIFICATE_TYPE, &certType, sizeof(certType)},
{CKA_SUBJECT, subject, sizeof(subject)},
{CKA_URL, "https://ca.example.com/certs/server.der", 36},
{CKA_HASH_OF_SUBJECT_PUBLIC_KEY, hash, sizeof(hash)}
};
适用场景:
- 移动设备存储空间有限
- 证书需要动态更新
- 证书存储在外部服务器
约束:CKA_VALUE和CKA_URL不能同时为空,至少要有一个。
X.509属性证书对象
属性证书(CKC_X_509_ATTR_CERT)不是公钥证书,而是绑定属性到主体的证书。
公钥证书 vs 属性证书:
X.509公钥证书(CKC_X_509)
├── 主体:Subject DN
├── 公钥:RSA/ECDSA等
├── 签发者:Issuer DN
└── 用途:证明"我是谁"(身份认证)
X.509属性证书(CKC_X_509_ATTR_CERT)
├── 主体:Owner(不同于Subject)
├── 属性:角色、权限、组等
├── 签发者:AC Issuer
└── 用途:证明"我能做什么"(授权)
专属属性:
| 属性 | 数据类型 | 含义 |
|---|---|---|
| CKA_OWNER | Byte array | 属性证书主体的DER编码(必须) |
| CKA_AC_ISSUER | Byte array | 属性签发者的DER编码 |
| CKA_SERIAL_NUMBER | Byte array | 序列号 |
| CKA_ATTR_TYPES | Byte array | 属性类型OID序列 |
| CKA_VALUE | Byte array | 属性证书BER编码(必须) |
注意:CKA_OWNER与CKA_SUBJECT使用不同的ASN.1语法,这是规范明确区分的。
WTLS证书对象
WTLS证书(CKC_WTLS)来自无线传输层安全协议,用于移动设备环境。
WTLS vs X.509:
WTLS证书特点:
- 编码格式更紧凑(适合移动设备)
- Subject/Issuer使用WTLS特有的Identifier编码
- 与X.509证书的ASN.1语法不同
适用场景:
- WAP移动设备
- 资源受限环境
CKA_TRUSTED:信任锚点
CKA_TRUSTED = CK_TRUE表示此证书被信任为根证书(Trust Anchor)。
安全约束:
- 应用程序不能将CKA_TRUSTED设置为CK_TRUE
- 只有SO(Security Officer)或Token初始化程序可以设置
- 可信证书不可被修改
c
/* 应用程序尝试设置CKA_TRUSTED会失败 */
CK_BBOOL trusted = CK_TRUE;
CK_ATTRIBUTE template[] = {
{CKA_TRUSTED, &trusted, sizeof(trusted)}
};
rv = C_CreateObject(hSession, template, 1, &hCert);
// 返回CKR_ATTRIBUTE_READ_ONLY或CKR_USER_NOT_LOGGED_IN
设计意图:防止恶意应用程序植入"假信任锚点"。只有Token管理员才能决定哪些证书是可信的。
证书对象在车载HSM中的应用
车载HSM常见的证书对象配置:
车载证书存储方案:
┌─────────────────────────────────────┐
│ Token(HSM安全存储区) │
│ │
│ ┌───────────────┐ │
│ │ CA根证书 │ CKA_TRUSTED=TRUE│
│ │ CKA_CATEGORY │ = AUTHORITY │
│ │ CKA_ID = 0xFF │ (不可修改) │
│ └───────────────┘ │
│ │
│ ┌───────────────┐ │
│ │ 设备证书 │ CKA_TRUSTED=FALSE│
│ │ CKA_CATEGORY │ = TOKEN_USER │
│ │ CKA_ID = 0x01 │ (关联私钥) │
│ └───────────────┘ │
│ │ │
│ │ CKA_ID关联 │
│ ▼ │
│ ┌───────────────┐ │
│ │ 设备私钥 │ CKA_ID = 0x01 │
│ │ CKA_SENSITIVE │ = TRUE │
│ │ CKA_SIGN │ = TRUE │
│ └───────────────┘ │
└─────────────────────────────────────┘
典型应用场景:
- 安全启动:Boot ROM验证固件签名证书链
- 车载通信:V2X通信使用设备证书签名消息
- 诊断认证:UDS 27服务验证诊断仪证书
小结:证书对象的设计智慧
证书对象的设计体现了PKCS#11的核心哲学:
设计要点回顾:
1. "只存储,不验证"
- Cryptoki不验证证书有效性
- 不检查证书链
- 不解析证书内容
- 应用程序负责这些工作
2. "信任锚点需要管理员授权"
- CKA_TRUSTED只能由SO设置
- 防止应用程序植入假信任锚
- 体现硬件安全模块的"信任根"定位
3. "灵活的存储方式"
- 可以存储完整证书(CKA_VALUE)
- 可以只存储URL(CKA_URL)
- 适应不同设备的存储约束
4. "清晰的关联机制"
- CKA_ID连接公钥、私钥、证书
- 不强制,但建议应用程序维护关联
证书对象为PKCS#11提供了"身份证明"的存储能力,而真正使用证书进行安全操作(签名、验证),则需要对应的密钥对象。
下一节,我们将了解硬件特性对象------看时钟、计数器等特殊对象。
【下集预告】
HSM有哪些特殊对象?
时钟对象有什么用?单调计数器是什么?
用户界面对象怎么工作?
下一节,硬件特征对象。