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;
};
职责:
- 管理Token列表:枚举存储目录中的所有Token
- 创建新Token:创建Token目录和初始化文件
- 销毁Token:删除Token目录及其内容
- 线程安全: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;
};
两个实现:
- ObjectFile:文件存储,持久化
- 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;
};
用途:
- 多实例同步:多个SoftHSM2实例共享同一存储
- 缓存失效:检测文件是否被其他实例修改
- 版本跟踪:记录文件的最后修改时间
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存储实现。