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.3 SoftHSM.cpp深度解析:密码库的“心脏“

一个问题:13830行代码如何组织?

假设你刚读完4.2节,了解了PKCS#11头文件和函数导出机制。

现在你打开SoftHSM.cpp文件——

398KB,13830行代码。

你可能会感到困惑:

  • 这13830行代码如何组织?
  • 一个类能装这么多代码吗?
  • 所有PKCS#11函数都在这里?

更深层的问题是:

为什么要把所有函数放在一个文件里?


“心脏“的设计意图

SoftHSM.cpp的设计像人体的心脏:

心脏类比:

人体心脏:
├── 位于胸腔中央
├── 连接所有器官(肺、肝、脑、四肢)
├── 控制血液循环
└── 是生命的核心

SoftHSM.cpp:
├── 位于项目中央
├── 连接所有模块(Slot、Session、Object、Crypto)
├── 控制数据流动
└── 是密码库的核心

为什么需要"心脏"?
├── 统一入口:所有请求经过同一处
├── 协调全局:管理模块间的协作
├── 状态管理:维护全局初始化状态
└── 错误处理:统一的异常捕获

SoftHSM.cpp的地位

SoftHSM.cpp是整个项目的核心实现文件:

SoftHSM.cpp核心地位:

文件规模:
├─ 398KB(约13830行代码)
├─ 实现所有PKCS#11函数
├─ 管理所有内部模块
└─ 协调所有操作流程

架构角色:
┌─────────────────────────────────────┐
│ 应用程序                            │
│                                     │
│ PKCS#11函数调用                     │
│     │                               │
│     ▼                               │
│ SoftHSM.cpp(心脏)                 │
│     │                               │
│     ├─ SlotManager(肺)            │
│     ├─ SessionManager(肝脏)       │
│     ├─ ObjectStore(器官)          │
│     ├─ CryptoFactory(肌肉)        │
│     └─ HandleManager(神经)        │
│                                     │
│ 是整个系统的协调中心                 │
└─────────────────────────────────────┘

类结构设计:一个“大管家“

SoftHSM类像一个“大管家“,管理所有部门:

/* SoftHSM类结构 */

class SoftHSM {
public:
    // 单例访问(只有一个管家)
    static SoftHSM* i();
    
    // PKCS#11函数实现(管家要做的所有事情)
    // 初始化类
    CK_RV C_Initialize(CK_VOID_PTR pInitArgs);   // 开门营业
    CK_RV C_Finalize(CK_VOID_PTR pReserved);     // 关门休息
    
    // Session类
    CK_RV C_OpenSession(...);   // 接待客人
    CK_RV C_CloseSession(...);  // 送走客人
    CK_RV C_Login(...);         // 核验身份
    CK_RV C_Logout(...);        // 注销身份
    
    // Object类
    CK_RV C_CreateObject(...);      // 创建档案
    CK_RV C_DestroyObject(...);     // 销毁档案
    CK_RV C_FindObjectsInit(...);   // 开始搜索
    CK_RV C_FindObjects(...);       // 搜索档案
    
    // 密钥类
    CK_RV C_GenerateKey(...);       // 生成钥匙
    CK_RV C_GenerateKeyPair(...);   // 生成钥匙对
    CK_RV C_DeriveKey(...);         // 派生钥匙
    CK_RV C_WrapKey(...);           // 包装钥匙
    CK_RV C_UnwrapKey(...);         // 解包钥匙
    
    // 加密类
    CK_RV C_EncryptInit(...);       // 准备加密
    CK_RV C_Encrypt(...);           // 执行加密
    CK_RV C_DecryptInit(...);       // 准备解密
    CK_RV C_Decrypt(...);           // 执行解密
    
    // 签名类
    CK_RV C_SignInit(...);          // 准备签名
    CK_RV C_Sign(...);              // 执行签名
    CK_RV C_VerifyInit(...);        // 准备验证
    CK_RV C_Verify(...);            // 执行验证
    
    // ... 所有其他函数(约60个)
    
private:
    // 管理器实例(各部门负责人)
    SlotManager* slotManager;       // 区域经理
    SessionManager* sessionManager; // 窗口经理
    ObjectStore* objectStore;       // 档案经理
    CryptoFactory* cryptoFactory;   // 车间经理
    HandleManager* handleManager;   // 编号经理
    
    // 内部状态(管家的工作状态)
    bool isInitialized;             // 是否营业
    CK_INFO cryptokiInfo;           // 库信息
};

为什么用单例模式?

想象一个服务中心有两个管家:

  • 管家A说:“Session 1在窗口A”
  • 管家B说:“Session 1在窗口B”
  • 客户不知道找谁
  • 数据不一致

必须只有一个管家:

  • 统一管理所有状态
  • 避免数据冲突
  • 协调各部门

C_Initialize:开门营业

C_Initialize是“开门营业“的第一步:

CK_RV SoftHSM::C_Initialize(CK_VOID_PTR pInitArgs)
{
    // 1. 检查是否已经营业
    if (isInitialized) {
        return CKR_CRYPTOKI_ALREADY_INITIALIZED;
    }
    
    // 2. 解析初始化参数(检查营业规则)
    CK_C_INITIALIZE_ARGS* initArgs = NULL;
    if (pInitArgs != NULL) {
        initArgs = (CK_C_INITIALIZE_ARGS*)pInitArgs;
        // 检查参数有效性
        if (initArgs->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) {
            // 库不能创建线程
        }
    }
    
    // 3. 初始化各部门(组织各部门上班)
    
    // 初始化SlotManager(通知区域经理上班)
    slotManager = new SlotManager();
    CK_RV rv = slotManager->init();
    if (rv != CKR_OK) {
        return rv;  // 区域经理上班失败
    }
    
    // 初始化SessionManager(通知窗口经理上班)
    sessionManager = new SessionManager();
    rv = sessionManager->init();
    if (rv != CKR_OK) {
        delete slotManager;
        return rv;  // 窗口经理上班失败
    }
    
    // 初始化ObjectStore(通知档案经理上班)
    objectStore = new ObjectStore(Configuration::i()->getStorePath());
    rv = objectStore->init();
    if (rv != CKR_OK) {
        delete slotManager;
        delete sessionManager;
        return rv;  // 档案经理上班失败
    }
    
    // 初始化CryptoFactory(通知车间经理上班)
    cryptoFactory = new CryptoFactory();
    rv = cryptoFactory->init();
    if (rv != CKR_OK) {
        delete slotManager;
        delete sessionManager;
        delete objectStore;
        return rv;  // 车间经理上班失败
    }
    
    // 初始化HandleManager(通知编号经理上班)
    handleManager = new HandleManager();
    
    // 4. 设置营业状态
    isInitialized = true;
    
    // 5. 填充库信息(设置告示牌)
    cryptokiInfo.libraryDescription = "SoftHSM v2";
    cryptokiInfo.libraryVersion.major = 2;
    cryptokiInfo.libraryVersion.minor = 0;
    
    return CKR_OK;  // 开门营业成功
}

初始化流程图

C_Initialize流程(开门营业):

检查是否已营业
    │
    │ 已营业? → CKR_CRYPTOKI_ALREADY_INITIALIZED
    │
    ▼ 未营业
解析初始化参数
    │
    │ 检查参数有效性
    │
    ▼ 参数有效
初始化SlotManager
    │
    │ 失败? → 返回错误,清理已初始化的
    │
    ▼ 成功
初始化SessionManager
    │
    │ 失败? → 返回错误,清理已初始化的
    │
    ▼ 成功
初始化ObjectStore
    │
    │ 失败? → 返回错误,清理已初始化的
    │
    ▼ 成功
初始化CryptoFactory
    │
    │ 失败? → 返回错误,清理已初始化的
    │
    ▼ 成功
初始化HandleManager
    │
    ▼ 成功
设置营业状态
    │
    ▼ isInitialized = true
填充库信息
    │
    ▼ 完成
返回CKR_OK

C_Finalize:关门休息

C_Finalize是“关门休息“的最后一步:

CK_RV SoftHSM::C_Finalize(CK_VOID_PTR pReserved)
{
    // 1. 检查是否正在营业
    if (!isInitialized) {
        return CKR_CRYPTOKI_NOT_INITIALIZED;
    }
    
    // 2. 检查保留参数(必须为NULL)
    if (pReserved != NULL) {
        return CKR_ARGUMENTS_BAD;
    }
    
    // 3. 关闭所有Session(送走所有客人)
    sessionManager->closeAllSessions();
    
    // 4. 清理各部门(各部门下班)
    
    // 清理HandleManager(编号经理下班)
    if (handleManager != NULL) {
        handleManager->clean();
        delete handleManager;
        handleManager = NULL;
    }
    
    // 清理CryptoFactory(车间经理下班)
    if (cryptoFactory != NULL) {
        cryptoFactory->finalize();
        delete cryptoFactory;
        cryptoFactory = NULL;
    }
    
    // 清理ObjectStore(档案经理下班)
    if (objectStore != NULL) {
        objectStore->finalize();
        delete objectStore;
        objectStore = NULL;
    }
    
    // 清理SessionManager(窗口经理下班)
    if (sessionManager != NULL) {
        sessionManager->finalize();
        delete sessionManager;
        sessionManager = NULL;
    }
    
    // 清理SlotManager(区域经理下班)
    if (slotManager != NULL) {
        slotManager->finalize();
        delete slotManager;
        slotManager = NULL;
    }
    
    // 5. 设置休息状态
    isInitialized = false;
    
    return CKR_OK;  // 关门休息成功
}

为什么清理顺序是反向的?

清理顺序(下班顺序):

初始化顺序(上班顺序):
Slot → Session → Object → Crypto → Handle

清理顺序(下班顺序):
Handle → Crypto → Object → Session → Slot

为什么反向?
├── Handle依赖其他模块,先清理它
├── Crypto依赖Object,先清理它
├── Object依赖Session,先清理它
├── Session依赖Slot,先清理它
└── Slot是最基础的,最后清理

类比:
├── 员工下班后,部门经理才能下班
├── 部门经理下班后,总经理才能下班
└── 总经理最后关门

C_OpenSession:接待客人

C_OpenSession是“接待客人“的流程:

CK_RV SoftHSM::C_OpenSession(CK_SLOT_ID slotID,
                              CK_FLAGS flags,
                              CK_VOID_PTR pApplication,
                              CK_NOTIFY Notify,
                              CK_SESSION_HANDLE_PTR phSession)
{
    // 1. 检查是否正在营业
    if (!isInitialized) {
        return CKR_CRYPTOKI_NOT_INITIALIZED;
    }
    
    // 2. 验证Slot编号(检查区域是否存在)
    Slot* slot = slotManager->getSlot(slotID);
    if (slot == NULL) {
        return CKR_SLOT_ID_INVALID;
    }
    
    // 3. 验证Token是否存在(检查保险箱是否存在)
    Token* token = slot->getToken();
    if (token == NULL) {
        return CKR_TOKEN_NOT_PRESENT;
    }
    
    // 4. 验证Session数量限制(检查窗口是否已满)
    if (sessionManager->getSessionCount() >= token->getMaxSessionCount()) {
        return CKR_SESSION_COUNT;
    }
    
    // 5. 创建Session(分配窗口)
    Session* session = new Session();
    
    // 6. 设置Session属性(配置窗口)
    session->setSlotID(slotID);
    session->setRW((flags & CKF_RW_SESSION) != 0);
    session->setSerial((flags & CKF_SERIAL_SESSION) != 0);
    session->setApplication(pApplication);
    session->setNotify(Notify);
    
    // 7. 分配Session Handle(分配窗口编号)
    CK_SESSION_HANDLE hSession = handleManager->newSessionHandle();
    session->setHandle(hSession);
    
    // 8. 注册Session(窗口经理登记)
    sessionManager->addSession(hSession, session);
    
    // 9. 返回Session Handle(给客人窗口编号)
    *phSession = hSession;
    
    return CKR_OK;  // 接待成功
}

C_Sign:执行签名

C_Sign是“盖章“的流程:

CK_RV SoftHSM::C_Sign(CK_SESSION_HANDLE hSession,
                       CK_BYTE_PTR pData,
                       CK_ULONG ulDataLen,
                       CK_BYTE_PTR pSignature,
                       CK_ULONG_PTR pulSignatureLen)
{
    // 1. 检查是否正在营业
    if (!isInitialized) {
        return CKR_CRYPTOKI_NOT_INITIALIZED;
    }
    
    // 2. 获取Session(找到窗口)
    Session* session = sessionManager->getSession(hSession);
    if (session == NULL) {
        return CKR_SESSION_HANDLE_INVALID;
    }
    
    // 3. 检查签名操作是否已初始化(检查是否准备好印章)
    if (!session->isSignInit()) {
        return CKR_OPERATION_NOT_INITIALIZED;
    }
    
    // 4. 获取签名密钥(找到印章)
    CK_OBJECT_HANDLE hKey = session->getSignKey();
    Object* keyObject = objectStore->getObject(hKey);
    if (keyObject == NULL) {
        return CKR_KEY_HANDLE_INVALID;
    }
    
    // 5. 获取签名机制(确定盖章方式)
    CK_MECHANISM_TYPE mechType = session->getSignMechanism();
    
    // 6. 获取密码算法实例(调用车间)
    AsymmetricAlgorithm* algo = cryptoFactory->getAsymmetricAlgorithm(mechType);
    if (algo == NULL) {
        return CKR_MECHANISM_INVALID;
    }
    
    // 7. 执行签名(盖章)
    PrivateKey* privateKey = keyObject->getPrivateKey();
    ByteString data(pData, ulDataLen);
    ByteString signature;
    
    CK_RV rv = algo->sign(privateKey, data, signature, mechType);
    
    // 8. 返回签名结果(给客户盖章结果)
    if (rv == CKR_OK) {
        if (pSignature != NULL) {
            memcpy(pSignature, signature.c_str(), signature.size());
        }
        *pulSignatureLen = signature.size();
    }
    
    // 9. 清除签名操作状态(印章收回)
    session->clearSignOperation();
    
    // 10. 回收密码算法实例(车间设备归位)
    cryptoFactory->recycleAsymmetricAlgorithm(algo);
    
    return rv;
}

签名流程图

C_Sign流程(盖章流程):

检查营业状态
    │
    ▼ 营业中
获取Session(找到窗口)
    │
    ▼ 窗口存在
检查签名已初始化(检查印章准备好)
    │
    ▼ 已准备好
获取签名密钥(找到印章)
    │
    ▼ 印章存在
获取签名机制(确定盖章方式)
    │
    ▼ 机制有效
获取密码算法实例(调用车间)
    │
    ▼ 车间响应
执行签名(盖章)
    │
    ▼ 盖章完成
返回签名结果(给客户)
    │
    ▼ 结果返回
清除签名操作状态(收回印章)
    │
    ▼ 印章收回
回收密码算法实例(车间设备归位)
    │
    ▼ 设备归位
返回结果

错误处理:统一的“异常捕获“

SoftHSM.cpp使用统一的错误处理机制:

/* main.cpp中的异常捕获 */

extern "C" CK_RV C_Sign(...)
{
    try {
        // 调用SoftHSM单例
        return SoftHSM::i()->C_Sign(...);
    } catch (std::exception& e) {
        // 记录日志
        ERROR_MSG("C_Sign exception: %s", e.what());
        FatalException();
    } catch (...) {
        // 未知异常
        ERROR_MSG("C_Sign unknown exception");
        FatalException();
    }
    return CKR_FUNCTION_FAILED;
}

为什么用try-catch?

异常捕获的原因:

问题:
├── C++代码可能抛出异常
├── PKCS#11标准要求C接口
├── C语言没有异常机制
└── 异常不能传给应用程序

解决方案:
├── 在main.cpp捕获所有异常
├── 调用FatalException()处理
├── 返回CKR_FUNCTION_FAILED
├── 记录日志便于调试
└── 应用程序只看到错误码

类比:
├── 车间设备故障(异常)
├── 管家捕获故障(try-catch)
├── 转为"服务故障"通知(CKR_FUNCTION_FAILED)
└── 客户不知道内部故障细节

一个类比:服务中心的“大管家“

服务中心大管家类比:

SoftHSM.cpp(大管家)
│
├── 职责:
│   ├── 接待客人(C_OpenSession)
│   ├── 核验身份(C_Login)
│   ├── 分配窗口(Session管理)
│   ├── 管理档案(Object管理)
│   ├── 调用车间(Crypto操作)
│   └── 关门休息(C_Finalize)
│
├── 上班流程(C_Initialize):
│   ├── 通知区域经理上班(SlotManager)
│   ├── 通知窗口经理上班(SessionManager)
│   ├── 通知档案经理上班(ObjectStore)
│   ├── 通知车间经理上班(CryptoFactory)
│   └── 设置营业状态
│
├── 下班流程(C_Finalize):
│   ├── 送走所有客人(关闭Session)
│   ├── 编号经理下班(HandleManager)
│   ├── 车间经理下班(CryptoFactory)
│   ├── 档案经理下班(ObjectStore)
│   ├── 窗口经理下班(SessionManager)
│   ├── 区域经理下班(SlotManager)
│   └── 设置休息状态
│
├── 盖章流程(C_Sign):
│   ├── 找到窗口(获取Session)
│   ├── 检查印章准备(检查SignInit)
│   ├── 找到印章(获取密钥)
│   ├── 确定盖章方式(获取机制)
│   ├── 调用车间盖章(执行签名)
│   ├── 返回盖章结果
│   ├── 收回印章(清除操作状态)
│   └── 车间设备归位
│
└── 为什么只有一个管家?
    ├── 统一入口,避免混乱
    ├── 统一状态,避免冲突
    ├── 统一错误处理
    └── 协调各部门

本篇小结

SoftHSM.cpp是密码库的“心脏“:

核心地位

  • 398KB,13830行代码
  • 实现所有PKCS#11函数
  • 协调所有内部模块

类结构设计

  • SoftHSM类:单例模式
  • 管理器实例:SlotManager、SessionManager等
  • 内部状态:isInitialized、cryptokiInfo

C_Initialize

  • 检查是否已初始化
  • 初始化所有管理器
  • 设置营业状态

C_Finalize

  • 检查营业状态
  • 关闭所有Session
  • 清理所有管理器(反向顺序)

C_Sign

  • 获取Session和密钥
  • 获取密码算法实例
  • 执行签名
  • 清除操作状态

错误处理

  • main.cpp捕获所有异常
  • 调用FatalException()处理
  • 返回CKR_FUNCTION_FAILED
  • 记录日志

下一节,我们将分析P11Objects与P11Attributes——Object如何被表示,属性如何被管理。

【下集预告】

  • Object是什么?

  • P11PublicKeyObj如何设计?

  • P11Attribute如何管理属性值?

  • 属性如何被读取和设置?

下一节,P11Objects与P11Attributes。