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.8 Object存储架构:密钥的“永久家园“

存储架构概览

SoftHSM2的Object存储分为两层:

Object存储架构:

┌─────────────────────────────────────────────────────────────┐
│                      ObjectStore                            │
│                    (存储管理器)                             │
│                                                             │
│  管理所有Token,提供Token创建/销毁接口                        │
└─────────────────────────────────────────────────────────────┘
                           │
                           │ 管理
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                      OSToken                                │
│                    (Token存储)                             │
│                                                             │
│  一个Token对应一个目录                                       │
│  管理Token内的所有Object                                    │
└─────────────────────────────────────────────────────────────┘
                           │
           ┌───────────────┼───────────────┐
           │               │               │
           ▼               ▼               ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│   ObjectFile    │ │  SessionObject  │ │   DBObject      │
│  (文件存储)    │ │  (内存存储)    │ │  (数据库存储)  │
│                 │ │                 │ │                 │
│ Token Object    │ │ Session Object  │ │ 可选的后端       │
│ 持久化存储      │ │ 临时存储        │ │                 │
└─────────────────┘ └─────────────────┘ └─────────────────┘

两种Object类型

类型存储位置生命周期对应类
Token Object文件系统持久(Session关闭后仍存在)ObjectFile
Session Object内存临时(Session关闭后消失)SessionObject

ObjectStore:存储管理器

ObjectStore是顶层存储管理器:

class ObjectStore
{
public:
    ObjectStore(std::string inStorePath, int umask);
    virtual ~ObjectStore();

    size_t getTokenCount();
    ObjectStoreToken* getToken(size_t whichToken);
    ObjectStoreToken* newToken(const ByteString& label);
    bool destroyToken(ObjectStoreToken* token);
    bool isValid();

private:
    std::vector<ObjectStoreToken*> tokens;
    std::vector<ObjectStoreToken*> allTokens;
    std::string storePath;
    int umask;
    bool valid;
    Mutex* storeMutex;
};

职责

  1. 管理Token列表:枚举存储目录中的所有Token
  2. 创建新Token:创建Token目录和初始化文件
  3. 销毁Token:删除Token目录及其内容
  4. 线程安全:Mutex保护并发访问

初始化流程

ObjectStore::ObjectStore(std::string inStorePath, int umask)
{
    storePath = inStorePath;
    this->umask = umask;
    
    // 创建存储目录(如果不存在)
    Directory dir(storePath);
    if (!dir.isValid()) {
        valid = false;
        return;
    }
    
    // 枚举目录中的所有Token
    std::list<std::string> subdirs = dir.getSubdirs();
    for (const std::string& subdir : subdirs) {
        OSToken* token = OSToken::accessToken(storePath, subdir, umask);
        if (token != NULL && token->isValid()) {
            tokens.push_back(token);
        }
    }
    
    valid = true;
}

OSToken:Token存储

OSToken管理一个Token的所有Object:

class OSToken : public ObjectStoreToken
{
public:
    OSToken(const std::string tokenPath, int umask);
    static OSToken* createToken(...);
    static OSToken* accessToken(...);
    
    bool setSOPIN(const ByteString& soPINBlob);
    bool getSOPIN(ByteString& soPINBlob);
    bool setUserPIN(ByteString userPINBlob);
    bool getUserPIN(ByteString& userPINBlob);
    
    bool getTokenFlags(CK_ULONG& flags);
    bool setTokenFlags(const CK_ULONG flags);
    bool getTokenLabel(ByteString& label);
    bool getTokenSerial(ByteString& serial);
    
    std::set<OSObject*> getObjects();
    OSObject* createObject();
    bool deleteObject(OSObject* object);
    
    bool isValid();
    void invalidate();
    bool clearToken();
    bool resetToken(const ByteString& label);

private:
    bool index(bool isFirstTime = false);
    
    bool valid;
    std::string tokenPath;
    std::set<OSObject*> objects;
    std::set<OSObject*> allObjects;
    std::set<std::string> currentFiles;
    ObjectFile* tokenObject;
    Generation* gen;
    Directory* tokenDir;
    int umask;
    Mutex* tokenMutex;
};

Token目录结构

Token目录结构:

/var/lib/softhsm2/tokens/
│
├── 1234567890abcdef/          ← Token目录(UUID命名)
│   ├── token.object           ← Token元数据文件
│   │
│   ├── 00123456.object        ← Object文件(UUID命名)
│   ├── 00123457.object
│   ├── 00123458.object
│   │
│   ├── 00123456.lock          ← Object锁文件(事务用)
│   ├── 00123457.lock
│   │
│   └── generation             ← Generation文件(版本控制)
│
├── 234567890abcdef/           ← 另一个Token
│   ├── token.object
│   ├── ...
│
└── ...

OSObject:Object抽象接口

OSObject是Object的抽象接口:

class OSObject
{
public:
    virtual ~OSObject() { }
    
    virtual bool attributeExists(CK_ATTRIBUTE_TYPE type) = 0;
    virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type) = 0;
    virtual bool getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val) = 0;
    virtual unsigned long getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val) = 0;
    virtual ByteString getByteStringValue(CK_ATTRIBUTE_TYPE type) = 0;
    
    virtual CK_ATTRIBUTE_TYPE nextAttributeType(CK_ATTRIBUTE_TYPE type) = 0;
    
    virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute) = 0;
    virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type) = 0;
    
    virtual bool isValid() = 0;
    
    enum Access { ReadOnly, ReadWrite };
    virtual bool startTransaction(Access access = ReadWrite) = 0;
    virtual bool commitTransaction() = 0;
    virtual bool abortTransaction() = 0;
    
    virtual bool destroyObject() = 0;
};

两个实现

  1. ObjectFile:文件存储,持久化
  2. SessionObject:内存存储,临时

ObjectFile:文件存储实现

ObjectFile实现持久化存储:

class ObjectFile : public OSObject
{
public:
    ObjectFile(OSToken* parent, const std::string inPath, int inUmask, 
               const std::string inLockpath, bool isNew = false);
    virtual ~ObjectFile();
    
    virtual bool attributeExists(CK_ATTRIBUTE_TYPE type);
    virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type);
    virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute);
    virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type);
    
    virtual bool isValid();
    void invalidate();
    
    std::string getFilename() const;
    std::string getLockname() const;
    
    virtual bool startTransaction(Access access);
    virtual bool commitTransaction();
    virtual bool abortTransaction();
    
    virtual bool destroyObject();

private:
    void refresh(bool isFirstTime = false);
    void store(bool isCommit = false);
    bool writeAttributes(File &objectFile);
    void discardAttributes();
    
    std::string path;
    int umask;
    Generation* gen;
    std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> attributes;
    bool valid;
    OSToken* token;
    Mutex* objectMutex;
    bool inTransaction;
    File* transactionLockFile;
    std::string lockpath;
};

Object文件格式

Object文件格式(Binary格式):

┌─────────────────────────────────────────────────────────────┐
│                    Object File                              │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Header                                              │   │
│  │  ├── Magic Number: "SHSMOBJ"                         │   │
│  │  ├── Version: 1                                      │   │
│  │  └── Attribute Count                                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Attributes                                          │   │
│  │                                                      │   │
│  │  ├── Attribute 1                                     │   │
│  │  │   ├── Type (CK_ATTRIBUTE_TYPE)                   │   │
│  │  │   ├── Length                                      │   │
│  │  │   └── Value                                       │   │
│  │  │                                                   │   │
│  │  ├── Attribute 2                                     │   │
│  │  │   ├── Type                                        │   │
│  │  │   ├── Length                                      │   │
│  │  │   └── Value                                       │   │
│  │  │                                                   │   │
│  │  ├── ...                                             │   │
│  │  │                                                   │   │
│  │  └── Attribute N                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

属性编码

bool ObjectFile::writeAttributes(File &objectFile)
{
    for (auto& attrPair : attributes) {
        CK_ATTRIBUTE_TYPE type = attrPair.first;
        OSAttribute* attr = attrPair.second;
        
        // 写入属性类型
        objectFile.write((CK_BYTE*)&type, sizeof(CK_ATTRIBUTE_TYPE));
        
        // 写入属性值
        if (attr->isBooleanAttribute()) {
            CK_BBOOL value = attr->getBooleanValue();
            CK_ULONG len = sizeof(CK_BBOOL);
            objectFile.write((CK_BYTE*)&len, sizeof(CK_ULONG));
            objectFile.write((CK_BYTE*)&value, len);
        } else if (attr->isUnsignedLongAttribute()) {
            CK_ULONG value = attr->getUnsignedLongValue();
            CK_ULONG len = sizeof(CK_ULONG);
            objectFile.write((CK_BYTE*)&len, sizeof(CK_ULONG));
            objectFile.write((CK_BYTE*)&value, len);
        } else if (attr->isByteStringAttribute()) {
            ByteString value = attr->getByteStringValue();
            CK_ULONG len = value.size();
            objectFile.write((CK_BYTE*)&len, sizeof(CK_ULONG));
            objectFile.write(value.byte_str(), len);
        }
    }
    return true;
}

Generation:文件版本控制

Generation类用于检测文件变化:

class Generation
{
public:
    Generation(const std::string path);
    virtual ~Generation();
    
    bool isValid();
    bool isChanged();
    void commit();
    void reset();
    
private:
    std::string genPath;
    time_t lastModTime;
    bool valid;
};

用途

  1. 多实例同步:多个SoftHSM2实例共享同一存储
  2. 缓存失效:检测文件是否被其他实例修改
  3. 版本跟踪:记录文件的最后修改时间
bool ObjectFile::refresh(bool isFirstTime)
{
    // 检查文件是否被修改
    if (!isFirstTime && gen != NULL && !gen->isChanged()) {
        return true;  // 未修改,无需刷新
    }
    
    // 文件被修改,重新加载
    File objectFile(path);
    if (!objectFile.isValid()) {
        valid = false;
        return false;
    }
    
    // 丢弃旧属性
    discardAttributes();
    
    // 读取新属性
    readAttributes(objectFile);
    
    // 更新Generation
    if (gen != NULL) {
        gen->commit();
    }
    
    valid = true;
    return true;
}

SessionObjectStore:临时对象存储

SessionObjectStore管理Session Object:

class SessionObjectStore
{
public:
    SessionObjectStore();
    virtual ~SessionObjectStore();
    
    int getObjectCount();
    void getObjects(std::set<OSObject*> &inObjects);
    void getObjects(CK_SLOT_ID slotID, std::set<OSObject*> &inObjects);
    
    SessionObject* createObject(CK_SLOT_ID slotID, CK_SESSION_HANDLE hSession, bool isPrivate = false);
    bool deleteObject(SessionObject* object);
    
    void sessionClosed(CK_SESSION_HANDLE hSession);
    void allSessionsClosed(CK_SLOT_ID slotID);
    void tokenLoggedOut(CK_SLOT_ID slotID);
    
    void clearStore();

private:
    std::set<SessionObject*> objects;
    std::set<SessionObject*> allObjects;
    Mutex* storeMutex;
};

生命周期管理

void SessionObjectStore::sessionClosed(CK_SESSION_HANDLE hSession)
{
    MutexLocker lock(storeMutex);
    
    // 遍历所有Session Object
    for (auto it = objects.begin(); it != objects.end(); ) {
        SessionObject* obj = *it;
        
        // 如果Object绑定到被关闭的Session,删除它
        if (obj->removeOnSessionClose(hSession)) {
            it = objects.erase(it);
        } else {
            ++it;
        }
    }
}

void SessionObjectStore::tokenLoggedOut(CK_SLOT_ID slotID)
{
    MutexLocker lock(storeMutex);
    
    // 遍历所有Session Object
    for (auto it = objects.begin(); it != objects.end(); ) {
        SessionObject* obj = *it;
        
        // 如果Object是私有的且属于该Token,删除它
        if (obj->removeOnTokenLogout(slotID)) {
            it = objects.erase(it);
        } else {
            ++it;
        }
    }
}

SessionObject:临时对象实现

SessionObject实现内存存储:

class SessionObject : public OSObject
{
public:
    SessionObject(SessionObjectStore* inParent, CK_SLOT_ID inSlotID, 
                  CK_SESSION_HANDLE inHSession, bool inIsPrivate = false);
    virtual ~SessionObject();
    
    virtual bool attributeExists(CK_ATTRIBUTE_TYPE type);
    virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type);
    virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute);
    virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type);
    
    virtual bool isValid();
    
    bool hasSlotID(CK_SLOT_ID inSlotID);
    bool removeOnSessionClose(CK_SESSION_HANDLE inHSession);
    bool removeOnAllSessionsClose(CK_SLOT_ID inSlotID);
    bool removeOnTokenLogout(CK_SLOT_ID inSlotID);
    
    virtual bool startTransaction(Access access);  // Stub
    virtual bool commitTransaction();               // Stub
    virtual bool abortTransaction();                // Stub
    
    virtual bool destroyObject();
    void invalidate();

private:
    std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> attributes;
    bool valid;
    Mutex* objectMutex;
    CK_SLOT_ID slotID;
    CK_SESSION_HANDLE hSession;
    bool isPrivate;
    SessionObjectStore* parent;
};

Session绑定

bool SessionObject::removeOnSessionClose(CK_SESSION_HANDLE inHSession)
{
    // 如果Object绑定的Session被关闭
    if (hSession == inHSession) {
        invalidate();
        return true;
    }
    return false;
}

bool SessionObject::removeOnTokenLogout(CK_SLOT_ID inSlotID)
{
    // 如果Object是私有的且属于该Token
    if (isPrivate && slotID == inSlotID) {
        invalidate();
        return true;
    }
    return false;
}

事务机制

ObjectFile支持事务操作:

bool ObjectFile::startTransaction(Access access)
{
    MutexLocker lock(objectMutex);
    
    if (inTransaction) {
        return false;  // 已有事务在进行
    }
    
    // 创建锁文件
    transactionLockFile = new File(lockpath);
    if (!transactionLockFile->createFile()) {
        delete transactionLockFile;
        transactionLockFile = NULL;
        return false;
    }
    
    inTransaction = true;
    return true;
}

bool ObjectFile::commitTransaction()
{
    MutexLocker lock(objectMutex);
    
    if (!inTransaction) {
        return false;
    }
    
    // 写入文件
    store(true);
    
    // 删除锁文件
    transactionLockFile->close();
    delete transactionLockFile;
    transactionLockFile = NULL;
    
    inTransaction = false;
    return true;
}

bool ObjectFile::abortTransaction()
{
    MutexLocker lock(objectMutex);
    
    if (!inTransaction) {
        return false;
    }
    
    // 重新加载文件(丢弃修改)
    refresh();
    
    // 删除锁文件
    transactionLockFile->close();
    delete transactionLockFile;
    transactionLockFile = NULL;
    
    inTransaction = false;
    return true;
}

事务用途

密钥生成时,需要一次性写入所有属性:

OSObject* obj = token->createObject();

obj->startTransaction();

obj->setAttribute(CKA_CLASS, ...);
obj->setAttribute(CKA_KEY_TYPE, ...);
obj->setAttribute(CKA_TOKEN, ...);
obj->setAttribute(CKA_SENSITIVE, ...);
obj->setAttribute(CKA_VALUE, ...);
// ... 更多属性

obj->commitTransaction();  // 一次性写入

一个类比:银行保险箱的存储系统

银行保险箱存储类比:

银行大楼(ObjectStore)
│
├── 保险箱库A(OSToken)
│   ├── 库记录文件(token.object)
│   │   ├── 库名称
│   │   ├── 管理员密码(加密)
│   │   ├── 客户密码(加密)
│   │   └── 库状态
│   │
│   ├── 保险箱001(ObjectFile)
│   │   ├── 保险箱文件(001.object)
│   │   ├── 内容:钥匙属性
│   │   └── 锁文件(事务用)
│   │
│   ├── 保险箱002(ObjectFile)
│   │   └── ...
│   │
│   └── 版本记录(generation)
│       └── 记录最后修改时间
│
├── 保险箱库B(OSToken)
│   └── ...
│
└── 访客临时柜(SessionObjectStore)
    ├── 临时柜1(SessionObject)
    │   ├── 内容:临时钥匙
    │   ├── 绑定访客Session
    │   └── 访客离开后清空
    │
    ├── 临时柜2(SessionObject)
    │   └── ...
    │
    └── 访客离开时清空

本篇小结

今天我们分析了SoftHSM2的Object存储架构。

层次结构

  • ObjectStore:顶层存储管理器
  • OSToken:Token存储(一个目录)
  • ObjectFile/SessionObject:Object存储

两种Object类型

  • Token Object(ObjectFile):持久化,文件存储
  • Session Object(SessionObject):临时,内存存储

文件存储特点

  • 每个Object一个文件
  • UUID命名
  • Generation版本控制
  • 事务支持(锁文件)

Session Object生命周期

  • 绑定到Session
  • Session关闭后自动删除
  • Logout后私有对象删除

下一节,我们将分析Token存储实现——PIN如何存储,Token元数据如何管理。

【下集预告】

Token的元数据存储在哪里?

PIN如何安全存储?

token.object文件包含什么?

DBToken与OSToken有什么区别?

下一节,Token存储实现。