4.4 P11Objects与P11Attributes:对象的“DNA“
一个问题:密钥对象如何被表示?
假设你正在阅读SoftHSM2源码,理解了SoftHSM.cpp的大管家角色。
现在你想知道:
密钥对象在内部是怎么存储的?
你可能会想:
- 密钥有类型(RSA、AES)、值、权限等属性
- 这些属性怎么被管理?
- 不同类型的密钥有什么不同?
更深层的问题是:
PKCS#11的Object概念,在C++代码中怎么实现?
“DNA“的设计意图
Object的属性就像生物的DNA:
DNA类比:
生物DNA:
├── 决定物种类型(人类、猫、狗)
├── 决定个体特征(眼睛颜色、身高)
├── 决定行为能力(是否可以生育)
├── 决定生命周期(寿命)
└── 一套基因编码定义整个生物
Object属性(DNA):
├── 决定对象类型(公钥、私钥、证书)
├── 决定对象特征(密钥长度、算法类型)
├── 决定行为能力(是否可签名、是否可加密)
├── 决定生命周期(Token对象 vs Session对象)
└── 一套属性定义整个对象
关键设计:
├── 属性是对象的"基因"
├── 不同对象有不同的"基因组合"
├── P11Attribute是"基因片段"
└── P11Object是"完整生物体"
对象系统的地位
P11Objects和P11Attributes是SoftHSM2对象管理的核心:
对象系统架构:
PKCS#11对象概念:
┌─────────────────────────────────────┐
│ Object(对象) │
│ │
│ ├─ CK_OBJECT_CLASS(对象类) │
│ ├─ CK_KEY_TYPE(密钥类型) │
│ └─ Attributes(属性集合) │
│ │
│ 属性是对象的"DNA": │
│ ├─ 定义对象类型 │
│ ├─ 定义对象行为 │
│ ├─ 定义对象权限 │
│ └─ 定义对象值 │
└─────────────────────────────────────┘
SoftHSM2实现:
┌─────────────────────────────────────┐
│ P11Object(对象类) │
│ │
│ ├─ P11Attributes(属性管理) │
│ ├─ 类型识别 │
│ ├─ 属性验证 │
│ └─ 属性访问 │
│ │
│ 继承体系: │
│ P11Object │
│ ├─ P11PublicKeyObj │
│ ├─ P11PrivateKeyObj │
│ ├─ P11SecretKeyObj │
│ ├─ P11CertificateObj │
│ └─ P11DataObj │
└─────────────────────────────────────┘
P11Object基类设计
P11Object是所有对象的基类:
/* P11Object基类 */
class P11Object {
public:
// 对象标识(生物编号)
CK_OBJECT_HANDLE handle; // Handle编号
CK_OBJECT_CLASS objectClass; // 对象类型(物种)
// 属性存储(基因库)
// 实际源码使用std::map存储P11Attribute派生类实例
std::map<CK_ATTRIBUTE_TYPE, P11Attribute*> attributes;
// 构造和析构
P11Object();
virtual ~P11Object();
// 属性操作(基因读取和修改)
CK_RV getAttributeValue(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);
CK_RV setAttributeValue(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount);
// 属性验证(基因有效性检查)
CK_RV validateAttributes();
// 对象类型检查(物种识别)
virtual bool isKeyObject() { return false; }
virtual bool isPublicKey() { return false; }
virtual bool isPrivateKey() { return false; }
virtual bool isSecretKey() { return false; }
// 对象生命周期(生命周期判断)
bool isTokenObject(); // 是否是持久对象
bool isPrivateObject(); // 是否是私有对象
bool isDestroyable(); // 是否可销毁
protected:
// 初始化属性(设置默认基因)
void initializeCommonAttributes();
};
P11Attribute:单个属性的抽象
P11Attribute是单个属性的抽象:
/* P11Attribute基类 */
class P11Attribute {
public:
P11Attribute(OSObject* parent, CK_ATTRIBUTE_TYPE type);
virtual ~P11Attribute();
// 属性类型(基因编号)
CK_ATTRIBUTE_TYPE getType();
// 属性值操作(基因读写)
virtual CK_RV getValue(CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen);
virtual CK_RV setValue(CK_VOID_PTR pValue, CK_ULONG ulValueLen);
// 属性验证(基因有效性)
virtual CK_RV validate();
// 属性特性(基因特性)
virtual bool isSensitive(); // 是否敏感(不可读取)
virtual bool isModifiable(); // 是否可修改
protected:
OSObject* parent; // 父对象(所属生物体)
CK_ATTRIBUTE_TYPE type; // 属性类型(基因编号)
};
属性类型分类:
P11Attribute派生类:
基础属性(所有对象共享):
├── P11Class CKA_CLASS(对象类)
├── P11Token CKA_TOKEN(是否Token对象)
├── P11Private CKA_PRIVATE(是否私有)
├── P11Modifiable CKA_MODIFIABLE(是否可修改)
└── P11Label CKA_LABEL(标签)
密钥属性(密钥对象共享):
├── P11KeyType CKA_KEY_TYPE(密钥类型)
├── P11KeyId CKA_ID(密钥ID)
├── P11StartDate CKA_START_DATE(生效日期)
├── P11EndDate CKA_END_DATE(失效日期)
└── P11Derive CKA_DERIVE(是否可派生)
RSA密钥属性:
├── P11Modulus CKA_MODULUS(模数N)
├── P11PublicExponent CKA_PUBLIC_EXPONENT(公钥指数E)
├── P11PrivateExponent CKA_PRIVATE_EXPONENT(私钥指数D)
├── P11Prime1 CKA_PRIME_1(素数P)
├── P11Prime2 CKA_PRIME_2(素数Q)
└── ...
AES密钥属性:
├── P11ValueLen CKA_VALUE_LEN(密钥长度)
└── P11Value CKA_VALUE(密钥值)
使用属性:
├── P11Encrypt CKA_ENCRYPT(是否可加密)
├── P11Decrypt CKA_DECRYPT(是否可解密)
├── P11Sign CKA_SIGN(是否可签名)
├── P11Verify CKA_VERIFY(是否可验证)
├── P11Wrap CKA_WRAP(是否可包装)
└── P11Unwrap CKA_UNWRAP(是否可解包)
安全属性:
├── P11Sensitive CKA_SENSITIVE(是否敏感)
├── P11Extractable CKA_EXTRACTABLE(是否可导出)
├── P11AlwaysSensitive CKA_ALWAYS_SENSITIVE(是否一直敏感)
└── P11NeverExtractable CKA_NEVER_EXTRACTABLE(是否从不导出)
P11PublicKeyObj:公钥对象
P11PublicKeyObj是公钥对象的实现:
class P11PublicKeyObj : public P11Object {
public:
P11PublicKeyObj(OSObject* object);
virtual ~P11PublicKeyObj();
// 类型识别
virtual bool isKeyObject() { return true; }
virtual bool isPublicKey() { return true; }
// RSA公钥属性
ByteString getModulus(); // 获取模数N
ByteString getPublicExponent(); // 获取公钥指数E
CK_ULONG getModulusBits(); // 获取密钥长度
// EC公钥属性
ByteString getEcPoint(); // 获取EC点Q
// 初始化属性
void initializeRSAAttributes();
void initializeECAttributes();
protected:
// RSA公钥特定属性
P11Modulus* modulusAttr;
P11PublicExponent* exponentAttr;
};
ByteString P11PublicKeyObj::getModulus()
{
// 从属性中获取模数值
CK_ATTRIBUTE attr = {CKA_MODULUS, NULL_PTR, 0};
getAttributeValue(&attr, 1);
// 分配内存并读取值
ByteString modulus(attr.ulValueLen);
attr.pValue = modulus.data();
getAttributeValue(&attr, 1);
return modulus;
}
P11PrivateKeyObj:私钥对象
P11PrivateKeyObj是私钥对象的实现:
class P11PrivateKeyObj : public P11Object {
public:
P11PrivateKeyObj(OSObject* object);
virtual ~P11PrivateKeyObj();
// 类型识别
virtual bool isKeyObject() { return true; }
virtual bool isPrivateKey() { return true; }
// RSA私钥属性
ByteString getPrivateExponent(); // 获取私钥指数D
ByteString getPrime1(); // 获取素数P
ByteString getPrime2(); // 获取素数Q
// 安全属性检查
bool isSensitive(); // 检查CKA_SENSITIVE
bool isExtractable(); // 检查CKA_EXTRACTABLE
// 私钥值是否可读取
bool canReadPrivateKey(); // 综合检查
protected:
// RSA私钥特定属性
P11PrivateExponent* privateExponentAttr;
P11Prime1* prime1Attr;
P11Prime2* prime2Attr;
};
bool P11PrivateKeyObj::canReadPrivateKey()
{
// 检查是否敏感
if (isSensitive()) {
return false; // 私钥值不可读取
}
// 检查是否可导出
if (!isExtractable()) {
return false; // 私钥值不可导出
}
return true; // 可以读取私钥值
}
安全属性的作用:
私钥安全属性:
CKA_SENSITIVE:
├── TRUE:私钥值不可通过C_GetAttributeValue读取
├── 设置后不可改为FALSE
└── 保护私钥不被泄露
CKA_EXTRACTABLE:
├── TRUE:私钥可以通过C_WrapKey导出
├── FALSE:私钥不可导出
├── 设置后不可改为TRUE
└── 控制私钥导出权限
CKA_ALWAYS_SENSITIVE:
├── TRUE:对象创建时就是敏感的
├── 记录对象的历史
└── 防止"先不敏感,后来敏感"的情况
CKA_NEVER_EXTRACTABLE:
├── TRUE:对象创建时就不可以导出
├── 记录对象的历史
└── 防止"先可导出,后来不可导出"的情况
安全等级不可逆转:
├── 可以从"不安全"变为"安全"
├── 不能从"安全"变为"不安全"
└── 这是PKCS#11的安全设计原则
P11SecretKeyObj:对称密钥对象
P11SecretKeyObj是对称密钥对象的实现:
class P11SecretKeyObj : public P11Object {
public:
P11SecretKeyObj(OSObject* object);
virtual ~P11SecretKeyObj();
// 类型识别
virtual bool isKeyObject() { return true; }
virtual bool isSecretKey() { return true; }
// 密钥值
ByteString getValue(); // 获取密钥值
CK_ULONG getValueLen(); // 获取密钥长度
// 密钥类型检查
bool isAESKey(); // 是否是AES密钥
bool isDESKey(); // 是否是DES密钥
// 密钥用途检查
bool canEncrypt(); // 是否可加密
bool canDecrypt(); // 是否可解密
bool canSign(); // 是否可签名(MAC)
bool canVerify(); // 是否可验证(MAC)
protected:
P11Value* valueAttr; // 密钥值属性
P11ValueLen* valueLenAttr; // 密钥长度属性
};
ByteString P11SecretKeyObj::getValue()
{
// 检查敏感属性
if (isSensitive()) {
return ByteString(); // 返回空,不能读取
}
// 读取密钥值
CK_ATTRIBUTE attr = {CKA_VALUE, NULL_PTR, 0};
getAttributeValue(&attr, 1);
ByteString value(attr.ulValueLen);
attr.pValue = value.data();
getAttributeValue(&attr, 1);
return value;
}
属性初始化:设置“默认基因“
创建对象时,需要初始化默认属性:
void P11Object::initializeCommonAttributes()
{
// 所有对象都有这些基本属性
// 对象类(物种)
attributes[CKA_CLASS] = new P11Class(this);
// 是否Token对象(是否持久)
attributes[CKA_TOKEN] = new P11Token(this);
attributes[CKA_TOKEN]->setValue(&bFalse, sizeof(bFalse));
// 是否私有对象(是否需要登录才能访问)
attributes[CKA_PRIVATE] = new P11Private(this);
attributes[CKA_PRIVATE]->setValue(&bFalse, sizeof(bFalse));
// 是否可修改
attributes[CKA_MODIFIABLE] = new P11Modifiable(this);
attributes[CKA_MODIFIABLE]->setValue(&bTrue, sizeof(bTrue));
// 标签(空)
attributes[CKA_LABEL] = new P11Label(this);
}
void P11SecretKeyObj::initializeSecretKeyAttributes()
{
// 对称密钥的特定属性
// 密钥类型
attributes[CKA_KEY_TYPE] = new P11KeyType(this);
// 密钥长度
attributes[CKA_VALUE_LEN] = new P11ValueLen(this);
// 密钥值
attributes[CKA_VALUE] = new P11Value(this);
// 使用属性
attributes[CKA_ENCRYPT] = new P11Encrypt(this);
attributes[CKA_DECRYPT] = new P11Decrypt(this);
attributes[CKA_SIGN] = new P11Sign(this);
attributes[CKA_VERIFY] = new P11Verify(this);
attributes[CKA_WRAP] = new P11Wrap(this);
attributes[CKA_UNWRAP] = new P11Unwrap(this);
attributes[CKA_DERIVE] = new P11Derive(this);
// 安全属性
attributes[CKA_SENSITIVE] = new P11Sensitive(this);
attributes[CKA_EXTRACTABLE] = new P11Extractable(this);
attributes[CKA_ALWAYS_SENSITIVE] = new P11AlwaysSensitive(this);
attributes[CKA_NEVER_EXTRACTABLE] = new P11NeverExtractable(this);
}
属性验证:检查“基因有效性“
属性设置需要验证:
CK_RV P11Sensitive::validate()
{
// 检查CKA_SENSITIVE的合法性
CK_BBOOL value;
getValue(&value, sizeof(value));
// 必须是TRUE或FALSE
if (value != CK_TRUE && value != CK_FALSE) {
return CKR_ATTRIBUTE_VALUE_INVALID;
}
// 检查历史状态
CK_BBOOL alwaysSensitive = parent->getBooleanValue(CKA_ALWAYS_SENSITIVE);
// 如果之前是敏感的,现在不能改为不敏感
if (alwaysSensitive == CK_TRUE && value == CK_FALSE) {
return CKR_ATTRIBUTE_VALUE_INVALID;
}
return CKR_OK;
}
CK_RV P11Extractable::validate()
{
// 检查CKA_EXTRACTABLE的合法性
CK_BBOOL value;
getValue(&value, sizeof(value));
// 必须是TRUE或FALSE
if (value != CK_TRUE && value != CK_FALSE) {
return CKR_ATTRIBUTE_VALUE_INVALID;
}
// 检查历史状态
CK_BBOOL neverExtractable = parent->getBooleanValue(CKA_NEVER_EXTRACTABLE);
// 如果之前不可导出,现在不能改为可导出
if (neverExtractable == CK_TRUE && value == CK_TRUE) {
return CKR_ATTRIBUTE_VALUE_INVALID;
}
return CKR_OK;
}
getAttributeValue实现
读取属性值的实现:
CK_RV P11Object::getAttributeValue(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
CK_RV rv = CKR_OK;
for (CK_ULONG i = 0; i < ulCount; i++) {
CK_ATTRIBUTE_TYPE type = pTemplate[i].type;
// 查找属性
auto it = attributes.find(type);
if (it == attributes.end()) {
// 属性不存在
pTemplate[i].ulValueLen = CK_UNAVAILABLE_INFORMATION;
rv = CKR_ATTRIBUTE_TYPE_INVALID;
continue;
}
P11Attribute* attr = it->second;
// 检查是否敏感
if (attr->isSensitive()) {
// 敏感属性不能读取
pTemplate[i].ulValueLen = CK_UNAVAILABLE_INFORMATION;
rv = CKR_ATTRIBUTE_SENSITIVE;
continue;
}
// 读取属性值
if (pTemplate[i].pValue == NULL_PTR) {
// 只返回长度
attr->getValue(NULL_PTR, &pTemplate[i].ulValueLen);
} else {
// 返回值
rv = attr->getValue(pTemplate[i].pValue, &pTemplate[i].ulValueLen);
// 检查缓冲区是否足够
if (rv == CKR_BUFFER_TOO_SMALL) {
// 缓冲区不够,返回所需长度
continue;
}
}
}
return rv;
}
一个类比:DNA与生物体
DNA与生物体类比:
P11Object(生物体)
│
├── 生物类型(对象类)
│ ├── P11PublicKeyObj(人类)
│ ├── P11PrivateKeyObj(猫)
│ ├── P11SecretKeyObj(狗)
│ ├── P11CertificateObj(鸟类)
│ └── P11DataObj(鱼类)
│
├── DNA库(属性集合)
│ │
│ ├── 基本基因(所有生物都有)
│ │ ├── CKA_CLASS(物种编号)
│ │ ├── CKA_TOKEN(寿命长短)
│ │ ├── CKA_PRIVATE(是否需要保护)
│ │ └── CKA_LABEL(名字)
│ │
│ ├── 密钥基因(所有密钥都有)
│ │ ├── CKA_KEY_TYPE(密钥种类)
│ │ ├── CKA_ENCRYPT(是否可加密)
│ │ ├── CKA_SIGN(是否可签名)
│ │ └── ...
│ │
│ ├── RSA基因(RSA密钥特有)
│ │ ├── CKA_MODULUS(模数N)
│ │ ├── CKA_PUBLIC_EXPONENT(指数E)
│ │ └── ...
│ │
│ └── AES基因(AES密钥特有)
│ │ ├── CKA_VALUE_LEN(密钥长度)
│ │ └── CKA_VALUE(密钥值)
│ │
│ └── 安全基因(私钥安全属性)
│ │ ├── CKA_SENSITIVE(是否敏感)
│ │ ├── CKA_EXTRACTABLE(是否可导出)
│ │ └── ...
│ │
│ └── P11Attribute(单个基因片段)
│ │ ├── getValue(读取基因值)
│ │ ├── setValue(修改基因值)
│ │ ├── validate(检查基因有效性)
│ │ └── isSensitive(是否是隐秘基因)
│ │
│ └── 基因不可逆变化
│ │ ├── 可以从"公开"变为"隐秘"
│ │ ├── 不能从"隐秘"变为"公开"
│ │ └── 这是安全设计原则
│ │
└── 对象生命周期
├── Token对象:长寿生物(持久存储)
├── Session对象:短命生物(Session关闭后消失)
└── Private对象:需要认证才能访问的生物
本篇小结
P11Objects和P11Attributes是对象系统的“DNA“:
P11Object基类:
- 所有对象的基类
- 属性存储:std::map<CK_ATTRIBUTE_TYPE, P11Attribute*>
- 类型识别:isKeyObject、isPublicKey等
P11Attribute抽象:
- 单个属性的抽象
- getValue/setValue:属性读写
- validate:属性验证
- isSensitive:敏感属性检查
派生类:
- P11PublicKeyObj:公钥对象
- P11PrivateKeyObj:私钥对象
- P11SecretKeyObj:对称密钥对象
- P11CertificateObj:证书对象
属性分类:
- 基础属性:CKA_CLASS、CKA_TOKEN等
- 密钥属性:CKA_KEY_TYPE、CKA_VALUE等
- 使用属性:CKA_ENCRYPT、CKA_SIGN等
- 安全属性:CKA_SENSITIVE、CKA_EXTRACTABLE等
安全设计:
- CKA_SENSITIVE:私钥值不可读取
- CKA_EXTRACTABLE:私钥可否导出
- 安全等级不可逆转
下一节,我们将分析Slot管理模块——Slot如何被初始化和管理。
【下集预告】
SlotManager如何工作?
Slot如何与Token绑定?
Slot初始化流程?
多Slot如何管理?
下一节,Slot管理模块。