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

5.3 安全存储与密钥管理:内存模拟设计

hsm-lite的存储方式

hsm-lite使用内存数组存储密钥,不涉及文件系统:

hsm-lite存储设计:

存储方式:
├── 内存数组存储(静态分配)
├── 不持久化(Session关闭后消失)
├── 固定容量限制(HSM_MAX_OBJECTS = 16)
└── 无文件系统依赖

简化特点:
├── 教学目的,展示概念
├── 单文件实现
├── 无安全属性检查(CKA_SENSITIVE等)
└── 密钥值可直接读取

密钥数据结构

实际hsm-lite中的密钥结构:

/* AES密钥对象(实际源码) */

typedef struct {
    CK_OBJECT_HANDLE    handle;      // 对象句柄(1, 2, 3...)
    CK_BYTE             key[32];     // AES-256密钥值
    CK_ULONG            key_len;     // 密钥长度
    CK_BBOOL            in_use;      // 是否正在使用
} hsm_key_t;

/* 全局密钥池 */
static struct {
    ...
    hsm_key_t           keys[HSM_MAX_OBJECTS];  // 密钥数组(16个)
    CK_ULONG            key_count;             // 当前密钥数量
    CK_OBJECT_HANDLE    next_key_handle;       // 下一个句柄
} g_ctx;

简化之处

与真实HSM对比:

真实HSM:
├── 密钥加密存储
├── CKA_SENSITIVE控制读取
├── Token持久化
├── 复杂属性管理
└── 多种密钥类型

hsm-lite(简化):
├── 密钥明文存储
├── 无属性控制
├── Session关闭后消失
├── 只支持AES密钥
└── 固定密钥长度

密钥查找机制

hsm-lite使用简单的数组遍历查找:

static hsm_key_t *find_key(CK_OBJECT_HANDLE hKey)
{
    // 遍历密钥数组
    for (CK_ULONG i = 0; i < HSM_MAX_OBJECTS; i++) {
        // 检查是否在使用且句柄匹配
        if (g_ctx.keys[i].in_use && 
            g_ctx.keys[i].handle == hKey) {
            return &g_ctx.keys[i];  // 找到
        }
    }
    return NULL;  // 未找到
}

查找效率

查找效率分析:

hsm-lite:
├── O(n)线性查找
├── n = HSM_MAX_OBJECTS (16)
├── 教学目的,简单直观

真实HSM:
├── Hash表或Tree查找
├── O(1)或O(log n)
├── 性能优化
└── 大量密钥场景

Handle分配机制

句柄从1开始递增分配:

CK_RV C_GenerateKey(...)
{
    ...
    for (CK_ULONG i = 0; i < HSM_MAX_OBJECTS; i++) {
        if (!g_ctx.keys[i].in_use) {
            // 找到空闲槽位
            
            g_ctx.keys[i].in_use = CK_TRUE;
            g_ctx.keys[i].handle = g_ctx.next_key_handle++;  // 分配句柄
            g_ctx.keys[i].key_len = HSM_AES_KEY_SIZE;
            
            // 生成密钥值
            get_random_bytes(g_ctx.keys[i].key, HSM_AES_KEY_SIZE);
            
            g_ctx.key_count++;
            *phKey = g_ctx.keys[i].handle;
            
            return CKR_OK;
        }
    }
    return CKR_GENERAL_ERROR;  // 无空闲槽位
}

Handle分配规则

Handle分配规则:

1. 从1开始(0保留为无效)
2. 递增分配(不回收)
3. 数组索引与Handle无关
4. 查找时遍历匹配

示例:
├── 第一次生成:handle = 1, 记录在keys[0]
├── 第二次生成:handle = 2, 存在keys[3]
├── 销毁handle=1后,keys[0].in_use = FALSE
├── 第三次生成:handle = 3(不是复用1)
└── 不回收Handle的原因:简化实现

Object管理

C_CreateObject

CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
                      CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
                      CK_OBJECT_HANDLE_PTR phObject)
{
    // 解析属性模板
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_AES;
    CK_ULONG value_len = HSM_AES_KEY_SIZE;
    CK_BYTE_PTR value = NULL;
    
    for (CK_ULONG i = 0; i < ulCount; i++) {
        switch (pTemplate[i].type) {
        case CKA_CLASS:
            class = *((CK_OBJECT_CLASS *)pTemplate[i].pValue);
            break;
        case CKA_KEY_TYPE:
            key_type = *((CK_KEY_TYPE *)pTemplate[i].pValue);
            break;
        case CKA_VALUE:
            value = (CK_BYTE_PTR)pTemplate[i].pValue;
            value_len = pTemplate[i].ulValueLen;
            break;
        }
    }
    
    // 验证对象类型(只支持AES密钥)
    if (class != CKO_SECRET_KEY || key_type != CKK_AES) {
        return CKR_ATTRIBUTE_VALUE_INVALID;
    }
    
    // 创建密钥对象
    // ...查找空闲槽位,复制密钥值
    
    return CKR_OK;
}

C_DestroyObject

CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
                       CK_OBJECT_HANDLE hObject)
{
    // 查找密钥
    hsm_key_t *key = find_key(hObject);
    if (!key) {
        return CKR_OBJECT_HANDLE_INVALID;
    }
    
    // 清零密钥值(安全清理)
    memset(key->key, 0, HSM_AES_KEY_SIZE);
    
    // 标记为未使用
    key->in_use = CK_FALSE;
    g_ctx.key_count--;
    
    printf("[hsm-lite] DestroyObject: handle=%lu\n", hObject);
    return CKR_OK;
}

属性读取

C_GetAttributeValue

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
                           CK_OBJECT_HANDLE hObject,
                           CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
    if (!g_ctx.initialized) {
        return CKR_FUNCTION_NOT_INITIALIZED;
    }
    
    hsm_session_t *sess = find_session(hSession);
    if (!sess) {
        return CKR_SESSION_HANDLE_INVALID;
    }
    hsm_key_t *key = find_key(hObject);
    if (!key) {
        return CKR_OBJECT_HANDLE_INVALID;
    }
    
    // 填充请求的属性
    for (CK_ULONG i = 0; i < ulCount; i++) {
        switch (pTemplate[i].type) {
        case CKA_CLASS:
            *((CK_OBJECT_CLASS *)pTemplate[i].pValue) = CKO_SECRET_KEY;
            pTemplate[i].ulValueLen = sizeof(CK_OBJECT_CLASS);
            break;
            
        case CKA_KEY_TYPE:
            *((CK_KEY_TYPE *)pTemplate[i].pValue) = CKK_AES;
            pTemplate[i].ulValueLen = sizeof(CK_KEY_TYPE);
            break;
            
        case CKA_VALUE_LEN:
            *((CK_ULONG *)pTemplate[i].pValue) = key->key_len;
            pTemplate[i].ulValueLen = sizeof(CK_ULONG);
            break;
            
        case CKA_VALUE:
            // 注意:hsm-lite允许读取密钥值(简化)
            if (pTemplate[i].pValue) {
                memcpy(pTemplate[i].pValue, key->key, key->key_len);
                pTemplate[i].ulValueLen = key->key_len;
            } else {
                pTemplate[i].ulValueLen = key->key_len;  // 只返回长度
            }
            break;
            
        default:
            // 不支持的属性
            pTemplate[i].ulValueLen = CK_UNAVAILABLE_INFORMATION;
            break;
        }
    }
    
    return CKR_OK;
}

两步读取模式

PKCS#11属性读取模式:

第一步:获取长度
├── pValue = NULL
├── 返回ulValueLen = 实际长度
└── 应用程序分配内存

第二步:获取值
├── pValue = 分配的缓冲区
├── 返回属性值
└── ulValueLen = 实际长度

示例:
CK_ATTRIBUTE template = {CKA_VALUE, NULL, 0};
C_GetAttributeValue(hSession, hKey, &template, 1);  // 获取长度
CK_BYTE *value = malloc(template.ulValueLen);
template.pValue = value;
C_GetAttributeValue(hSession, hKey, &template, 1);  // 获取值

安全属性简化

hsm-lite不支持真实的安全属性:

安全属性对比:

真实PKCS#11:
├── CKA_SENSITIVE:密钥值不可读取
├── CKA_EXTRACTABLE:密钥不可导出
├── CKA_ALWAYS_SENSITIVE:始终敏感
├── CKA_NEVER_EXTRACTABLE:从不可导出
├── 属性不可逆(安全等级不可降低)
└── C_WrapKey加密导出

hsm-lite(简化):
├── 无CKA_SENSITIVE检查
├── 无CKA_EXTRACTABLE检查
├── CKA_VALUE可直接读取
├── 密钥明文存储
└── 教学目的,展示概念

为什么不实现安全属性?

简化设计原因:

教学目的:
├── 展示PKCS#11接口流程
├── 不需要真实安全特性
├── 代码简洁易懂
└── 约615行实现

如需安全属性:
├── 需增加属性检查代码
├── 需实现属性不可逆逻辑
├── 需加密密钥存储
└── 代码量增加200+行

一个类比:教室储物柜

教室储物柜类比:

hsm-lite密钥存储(教室储物柜)
│
├── 存储方式
│   ├── 固定数量(16个格子)
│   ├── 内存存储(柜子在教室里)
│   ├── 无锁(任何人可查看)
│   └── 课后清空(Session关闭)
│
├── Handle(编号)
│   ├── 从1开始编号
│   ├── 递增分配
│   ├── 与格子位置无关
│   └── 查找时遍历匹配
│
├── 简化之处
│   ├── 无锁(真实HSM有加密)
│   ├── 无标签(真实HSM有CKA_LABEL)
│   ├── 无权限(真实HSM有CKA_PRIVATE)
│   └── 无持久化(真实HSM有Token对象)
│
└── 学习价值
    ├── 理解对象管理概念
    ├── 理解Handle机制
    ├── 理解属性读取流程
    └── 为真实设计打基础

本篇小结

hsm-lite的存储设计非常简化:

存储方式

  • 内存数组存储
  • 固定容量(16个)
  • 无持久化

密钥结构

  • hsm_key_t:handle, key[32], key_len, in_use
  • keys[]数组存储所有密钥

Handle机制

  • 从1递增分配
  • 不回收复用
  • 数组遍历查找

Object管理

  • C_CreateObject:解析属性模板创建
  • C_DestroyObject:清零密钥值,标记未使用
  • C_GetAttributeValue:支持CKA_CLASS/KEY_TYPE/VALUE

安全属性

  • 不支持CKA_SENSITIVE等
  • 密钥值可直接读取
  • 教学简化设计

简化之处

  • 无加密存储
  • 无属性检查
  • 无持久化
  • 无多种密钥类型

下一节,我们将分析测试程序的设计和运行。

【下集预告】

  • 测试程序如何设计?

  • 四个测试案例详解?

  • 如何编译运行?

  • 多架构支持?

下一节,测试与编译。