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.5 Slot管理模块:Token的“注册中心“

一个问题:Slot如何被管理?

假设你刚刚读完第三章,了解了PKCS#11的Slot概念:

  • Slot是“逻辑插槽“
  • Token是“插入的令牌“
  • 一个Slot可以有(或没有)Token

现在你打开SoftHSM2源码,想找到Slot管理的代码。

但你可能会困惑:

  • 软件HSM没有物理插槽,Slot是什么?
  • Slot如何被创建?
  • Token如何与Slot绑定?

更深层的问题是:

软件HSM如何“模拟“硬件HSM的Slot机制?


“注册中心“的设计意图

SlotManager就像政府注册中心:

注册中心类比:

政府注册中心(SlotManager)
│
├── 职责:
│   ├── 管理所有注册号(Slot ID)
│   ├── 登录新企业(创建Token)
│   ├── 注销企业(销毁Token)
│   ├── 查询企业列表(GetSlotList)
│   └── 检查企业状态(Token是否存在)
│   │
│   ├── 注册号(Slot ID):
│   │   ├── 每个企业有一个编号
│   │   ├── 编号是逻辑概念
│   │   └── 不代表物理位置
│   │
│   ├── 企业(Token):
│   │   ├── 可能"已注册"(Token存在)
│   │   ├── 可能"未注册"(Token不存在)
│   │   └── 企业有档案信息
│   │
│   └── 软件注册 vs 硬件注册:
│   │   ├── 软件注册:数据库记录即可
│   │   ├── 硬件注册:需要物理检测
│   │   └── 软件注册可以"凭空"创建
│   │
│   └── 车载理解:
│   │   ├── 注册号 = HSM芯片编号
│   │   ├── 企业 = HSM芯片本身
│   │   ├── 多个注册号 = 多个HSM芯片
│   │   └── 注册中心 = 系统的Slot管理模块
│   │
└── 关键设计:
    ├── Slot是逻辑概念(注册号)
    ├── Token是数据实体(企业档案)
    ├── Slot可以没有Token(空注册号)
    └── SoftHSM2用文件/数据库模拟Token

SlotManager的地位

SlotManager管理PKCS#11的Slot和Token:

Slot管理架构:

PKCS#11 Slot概念:
┌─────────────────────────────────────┐
│ Slot = 逻辑插槽                      │
│                                     │
│ ├─ 可以插入Token                    │
│ ├─ 每个Slot有Slot ID                │
│ ├─ 每个Slot有Slot信息               │
│ └─ Token存在时才有Token信息          │
│                                     │
│ 车载理解:                           │
│ Slot = HSM芯片的通信端口            │
│ Token = HSM芯片本身                 │
│ 多个Slot = 多个HSM芯片              │
└─────────────────────────────────────┘

SlotManager职责:
┌─────────────────────────────────────┐
│ 软件HSM vs 硬件HSM                   │
│                                     │
│ 软件HSM(SoftHSM2):                │
│ ├─ Slot = 配置文件定义              │
│ ├─ Token = 数据库/文件              │
│ ├─ 可动态创建                       │
│ └─ 无真实硬件                       │
│                                     │
│ 硬件HSM:                            │
│ ├─ Slot = SPI/I2C端口               │
│ ├─ Token = 检测到芯片               │
│ ├─ 不可动态创建                     │
│ └─ 有真实硬件                       │
└─────────────────────────────────────┘

SlotManager类设计

SlotManager是Slot管理的核心类:

/* SlotManager类结构 */

class SlotManager {
public:
    // Slot列表
    // 教学简化:实际源码使用std::map<CK_SLOT_ID, Slot*>而非std::vector
    std::vector<Slot*> slots;
    
    // 初始化(注册中心开业)
    CK_RV init();
    
    // Slot操作(注册号查询)
    CK_RV getSlotList(CK_BBOOL tokenPresent,  // 只返回有Token的Slot
                      CK_SLOT_ID_PTR pSlotList, 
                      CK_ULONG_PTR pulCount);
    
    Slot* getSlot(CK_SLOT_ID slotID);        // 获取指定Slot
    bool isValidSlot(CK_SLOT_ID slotID);     // 验证Slot ID
    
    // Token操作(企业查询)
    bool isTokenPresent(CK_SLOT_ID slotID);  // Token是否存在
    Token* getToken(CK_SLOT_ID slotID);      // 获取Token
    
    // Slot事件(企业变动通知)
    CK_RV waitForSlotEvent(CK_FLAGS flags, 
                           CK_SLOT_ID_PTR pSlot, 
                           CK_VOID_PTR pReserved);
    
private:
    // 加载配置(导入已有企业)
    CK_RV loadSlotsFromConfig();
    
    // 创建Slot(分配注册号)
    CK_RV createSlot(CK_SLOT_ID slotID);
};

SlotManager初始化

SlotManager初始化时加载已有Slot:

CK_RV SlotManager::init()
{
    // 1. 清空Slot列表(清空注册簿)
    slots.clear();
    
    // 2. 从配置加载Slot(导入已有企业)
    CK_RV rv = loadSlotsFromConfig();
    if (rv != CKR_OK) {
        return rv;
    }
    
    // 3. 如果没有Slot,创建默认Slot(分配默认注册号)
    if (slots.empty()) {
        Slot* slot = new Slot(0);  // Slot ID = 0
        slots.push_back(slot);
    }
    
    return CKR_OK;
}

CK_RV SlotManager::loadSlotsFromConfig()
{
    // 从配置文件加载Slot定义
    
    // 获取配置中的Slot数量
    CK_ULONG slotCount = Configuration::i()->getSlotCount();
    
    for (CK_ULONG i = 0; i < slotCount; i++) {
        // 创建Slot
        Slot* slot = new Slot(i);
        
        // 初始化Slot信息
        slot->init();
        
        // 加载Token(如果存在)
        slot->loadToken();
        
        slots.push_back(slot);
    }
    
    return CKR_OK;
}

配置文件示例

# softhsm2.conf 配置文件

# Token存储目录
directories.tokendir = /var/lib/softhsm2/tokens

# Slot数量(注册号数量)
# SoftHSM2可以动态创建,这个值不是固定限制

Slot类设计

Slot类代表一个逻辑插槽:

class Slot {
public:
    Slot(CK_SLOT_ID slotID);
    ~Slot();
    
    // Slot ID(注册号)
    CK_SLOT_ID getSlotID();
    
    // Slot信息(注册号信息)
    CK_RV getSlotInfo(CK_SLOT_INFO_PTR pInfo);
    
    // Token操作(企业操作)
    bool isTokenPresent();
    Token* getToken();
    CK_RV getTokenInfo(CK_TOKEN_INFO_PTR pInfo);
    
    // Token生命周期(企业生命周期)
    CK_RV initToken(CK_UTF8CHAR_PTR pPin, 
                    CK_ULONG ulPinLen,
                    CK_UTF8CHAR_PTR pLabel);
    
    CK_RV loadToken();        // 加载已有Token
    CK_RV destroyToken();     // 销毁Token
    
    // 机制查询(服务能力查询)
    CK_RV getMechanismList(...);
    CK_RV getMechanismInfo(...);
    
private:
    CK_SLOT_ID slotID;        // Slot ID
    SlotInfo* slotInfo;        // Slot信息
    Token* token;              // Token实例(可能为NULL)
    bool tokenPresent;         // Token是否存在
};

Slot信息结构

CK_RV Slot::getSlotInfo(CK_SLOT_INFO_PTR pInfo)
{
    // 填充Slot信息(注册号信息)
    
    // Slot描述(注册号描述)
    strncpy(pInfo->slotDescription, 
            "SoftHSM2 Slot", 
            sizeof(pInfo->slotDescription));
    
    // 制造商ID(注册中心名称)
    strncpy(pInfo->manufacturerID, 
            "OpenDNSSEC", 
            sizeof(pInfo->manufacturerID));
    
    // Slot标志(注册号标志)
    pInfo->flags = CKF_TOKEN_PRESENT;  // Token存在
    
    // 硬件版本(无关,软件HSM)
    pInfo->hardwareVersion.major = 1;
    pInfo->hardwareVersion.minor = 0;
    
    // 固件版本(无关,软件HSM)
    pInfo->firmwareVersion.major = 1;
    pInfo->firmwareVersion.minor = 0;
    
    return CKR_OK;
}

Token类设计

Token类代表一个密码令牌:

class Token {
public:
    Token(Slot* slot);
    ~Token();
    
    // Token信息(企业信息)
    CK_RV getTokenInfo(CK_TOKEN_INFO_PTR pInfo);
    CK_RV setTokenLabel(const std::string& label);
    
    // PIN管理(密码管理)
    CK_RV initPIN(CK_USER_TYPE userType, 
                  CK_UTF8CHAR_PTR pPin, 
                  CK_ULONG ulPinLen);
    
    CK_RV setPIN(CK_USER_TYPE userType, 
                 CK_UTF8CHAR_PTR pOldPin, 
                 CK_ULONG ulOldLen,
                 CK_UTF8CHAR_PTR pNewPin, 
                 CK_ULONG ulNewLen);
    
    CK_RV verifyPIN(CK_USER_TYPE userType, 
                    CK_UTF8CHAR_PTR pPin, 
                    CK_ULONG ulPinLen);
    
    // 登录状态(登录状态)
    bool isLoggedIn();
    CK_USER_TYPE getLoggedInUser();
    
    // Session管理(窗口管理)
    CK_RV openSession(...);
    CK_RV closeSession(...);
    CK_RV closeAllSessions();
    
    // Object管理(档案管理)
    ObjectStore* getObjectStore();
    
private:
    Slot* slot;                 // 所属Slot
    TokenInfo* tokenInfo;       // Token信息
    ObjectStore* objectStore;   // Object存储
    SessionManager* sessions;   // Session管理
    
    // PIN存储
    ByteString soPINBlob;      // SO PIN(加密存储)
    ByteString userPINBlob;    // User PIN(加密存储)
    
    // 登录状态
    bool loggedIn;
    CK_USER_TYPE loggedInUser;
};

Token初始化流程

Token初始化创建新的Token:

CK_RV Slot::initToken(CK_UTF8CHAR_PTR pPin, 
                       CK_ULONG ulPinLen,
                       CK_UTF8CHAR_PTR pLabel)
{
    // 1. 检查Slot状态(检查注册号状态)
    if (token != NULL) {
        // Token已存在,需要先销毁
        destroyToken();
    }
    
    // 2. 创建Token(新企业注册)
    token = new Token(this);
    
    // 3. 设置Token Label(企业名称)
    token->setTokenLabel(std::string(pLabel, 32));
    
    // 4. 初始化SO PIN(管理员密码)
    CK_RV rv = token->initPIN(CKU_SO, pPin, ulPinLen);
    if (rv != CKR_OK) {
        delete token;
        token = NULL;
        return rv;
    }
    
    // 5. 初始化Object存储(企业档案库)
    token->getObjectStore()->init();
    
    // 6. 标记Token存在
    tokenPresent = true;
    
    return CKR_OK;
}

Token初始化流程图

Token初始化流程(企业注册流程):

检查Slot状态
    │
    │ Token已存在? → 先销毁
    │
    ▼ Token不存在
创建Token对象
    │
    ▼ 创建成功
设置Token Label
    │
    ▼ Label设置完成
初始化SO PIN
    │
    │ 失败? → 清理并返回错误
    │
    ▼ PIN设置完成
初始化Object存储
    │
    ▼ 存储初始化完成
标记Token存在
    │
    ▼ 完成
返回CKR_OK

对应softhsm2-util命令:
softhsm2-util --init-token --slot 0 --label "MyToken" --so-pin 12345678

getSlotList实现

getSlotList返回Slot列表:

CK_RV SlotManager::getSlotList(CK_BBOOL tokenPresent,
                                 CK_SLOT_ID_PTR pSlotList,
                                 CK_ULONG_PTR pulCount)
{
    // 1. 计算Slot数量(统计注册号)
    CK_ULONG count = 0;
    
    if (tokenPresent) {
        // 只统计有Token的Slot(只返回有企业的注册号)
        for (Slot* slot : slots) {
            if (slot->isTokenPresent()) {
                count++;
            }
        }
    } else {
        // 统计所有Slot(返回所有注册号)
        count = slots.size();
    }
    
    // 2. 返回数量(返回统计结果)
    *pulCount = count;
    
    // 3. 如果pSlotList不为NULL,填充列表(返回注册号列表)
    if (pSlotList != NULL) {
        CK_ULONG index = 0;
        
        for (Slot* slot : slots) {
            if (tokenPresent) {
                if (slot->isTokenPresent()) {
                    pSlotList[index] = slot->getSlotID();
                    index++;
                }
            } else {
                pSlotList[index] = slot->getSlotID();
                index++;
            }
        }
    }
    
    return CKR_OK;
}

使用示例

/* 获取Slot列表示例 */

CK_SLOT_ID slotList[10];
CK_ULONG slotCount;

// 只获取有Token的Slot
rv = C_GetSlotList(CK_TRUE, slotList, &slotCount);

// slotCount = 实际Slot数量
// slotList = Slot ID数组

// 典型SoftHSM2配置:slotCount = 1,slotList[0] = 0

waitForSlotEvent:Slot变动通知

waitForSlotEvent等待Slot状态变化:

CK_RV SlotManager::waitForSlotEvent(CK_FLAGS flags,
                                     CK_SLOT_ID_PTR pSlot,
                                     CK_VOID_PTR pReserved)
{
    // 软件HSM通常不支持Slot事件
    // 真实HSM可以检测硬件插入/拔出
    
    // SoftHSM2返回CKR_NO_EVENT
    return CKR_NO_EVENT;
}

硬件HSM vs 软件HSM

Slot事件对比:

硬件HSM:
├── 支持waitForSlotEvent
├── 可以检测Token插入
├── 可以检测Token拔出
├── 有物理事件触发
└── 车载理解:检测HSM芯片连接状态

软件HSM(SoftHSM2):
├── 不支持waitForSlotEvent
├── 返回CKR_NO_EVENT
├── 无物理事件
├── Token状态由软件控制
└── 无硬件检测机制

一个类比:政府注册中心

政府注册中心类比:

SlotManager(注册中心)
│
├── 注册号管理(Slot管理)
│   ├── getSlotList:列出所有注册号
│   ├── getSlot:查询指定注册号
│   ├── isValidSlot:验证注册号有效性
│   └── waitForSlotEvent:等待注册变动(不支持)
│   │
│   ├── 软件注册中心(SoftHSM2):
│   │   ├── 注册号在配置文件定义
│   │   ├── 企业在数据库注册
│   │   ├── 可以凭空创建
│   │   └── 无物理限制
│   │
│   └── 硬件注册中心(真实HSM):
│   │   ├── 注册号对应物理端口
│   │   ├── 企业对应物理芯片
│   │   ├── 创建需要物理检测
│   │   └── 支持硬件事件
│   │
│   └── 车载理解:
│   │   ├── 注册号 = HSM芯片编号
│   │   ├── 注册中心 = SlotManager
│   │   ├── 企业档案 = Token数据
│   │   ├── 企业窗口 = Session
│   │   └── 企业档案库 = ObjectStore
│   │
│   └── 注册流程:
│   │   ├── 1. 分配注册号(分配Slot ID)
│   │   ├── 2. 企业注册(initToken)
│   │   ├── 3. 设置企业名称(setLabel)
│   │   ├── 4. 设置管理员密码(initPIN)
│   │   ├── 5. 创建档案库(ObjectStore)
│   │   └── 6. 企业开业(Token可用)
│   │
│   └── 查询流程:
│   │   ├── 1. 查询注册号列表(getSlotList)
│   │   ├── 2. 查询企业是否存在(isTokenPresent)
│   │   ├── 3. 查询企业信息(getTokenInfo)
│   │   └── 4. 打开企业窗口(openSession)
│   │
└── 关键理解:
    ├── Slot是逻辑概念(注册号)
    ├── Token是数据实体(企业档案)
    ├── 注册号可以空置(无Token)
    └── 软件HSM用数据库模拟注册

本篇小结

SlotManager是Token的“注册中心“:

SlotManager职责

  • 管理Slot列表
  • 查询Slot信息
  • 加载已有Token
  • 监听Slot事件(软件HSM不支持)

Slot类

  • Slot ID:注册号
  • Slot信息:注册号信息
  • Token关联:企业档案
  • 机制查询:服务能力

Token类

  • Token信息:企业信息
  • PIN管理:密码管理
  • Session管理:窗口管理
  • Object存储:档案存储

初始化流程

  • 检查Slot状态
  • 创建Token
  • 设置Label
  • 初始化SO PIN
  • 初始化ObjectStore

软件vs硬件

  • 软件:Slot在配置定义,Token在数据库
  • 硬件:Slot对应物理端口,Token对应芯片

下一节,我们将分析Session管理模块——Session如何被创建和管理。

【下集预告】

  • SessionManager如何工作?

  • Session如何被创建?

  • Session状态如何管理?

  • 登录状态如何共享?

下一节,Session管理模块。