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

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管理模块。