简介:针对传统云存储高成本、依赖可信第三方等问题,这份PDF提供了基于区块链技术的数据存储系统完整设计方案。内容涵盖去中心化云存储架构、加密算法与私有关键字搜索机制,并详细阐述了系统匿名性、低成本、去中心化、可用性与安全性等特点,适合区块链、云存储及密码学方向的研究生、工程师和教师作为技术分析与参考文献使用。资源为1个PDF文件,大小约1.11MB,包含论文正式排版内容,并附有中英文摘要、基金项目及引用信息。已有271人学习浏览。通过该文档,读者可系统掌握区块链用于数据存储的核心思路与实现方法,包括数据加密上传、授权搜索、共识机制权衡以及与传统云存储的对比分析,可作为课程设计、毕业设计或科研开题的参考资料。
1. 区块链数据存储系统:可搜索加密与去中心化存储的交接点
传统云存储把数据集中到少数服务商手里,成本高且要先信任对方,数据一上传,可用性和隐私基本都押在服务商的运维水平上。2019年重庆理工大学学报上的这篇设计论文,给出一个更具体的解法:用许可区块链作为底层支撑,数据所有者加密后把密文分片交给联合云,加密关键字标签写进区块链,DHT负责索引定位。数据消费者不必下载整个数据集,而是用陷门在密文标签上做私有搜索,搜索时区块链节点验证的是凭证是否有授权,而不是用户是谁。这套设计把“去中心化”从记账层推进到存储层,对做分布式系统、密码学应用以及想从0搭建一个区块链平台的人来说,都是值得拆解的样本:它明确了链上只放可验证的引用和凭证,数据本体继续留在链下。
2. 许可区块链存储架构拆解:数据所有者、消费者与联合云节点的职责边界
2.1 三个角色与一条核心链路
数据所有者、数据消费者、区块链节点是论文设定的三方。数据所有者负责构造文档集,每个文档挂一组固定关键字,上传前先在客户端加密。加密数据集由云联盟分布式存储,加密关键字标签交给区块链维护。数据消费者不是所有者的同组人,而是订阅者:所有者授权后,消费者向区块链节点证明自己持有合法凭证,然后执行关键字搜索。
这条链路的关键在于授权和搜索是可分离的。凭证证明用零知识方式完成,区块链节点只确认“你有权限”,不关心“你是谁”。这也直接回答了为什么要把存储系统拆成链上和链下两层:链上放授权、标签、审计记录的根,链下放密文分片,这样既保留去中心化的信任模型,又绕开区块链不适合存大文件的天然限制。
2.2 DHT与区块链各存什么
区块链不存储数据本身,只存对数据的引用。DHT(分布式哈希表)则用于协调和维护对等系统中关于密文分片的元数据。论文把这两层分得很清楚,整理如下:
| 存储层级 | 保存内容 | 写入方 | 读取方 |
|---|---|---|---|
| 区块链 | 加密关键字标签、凭证承诺、可检索性证明记录 | 数据所有者、审计节点 | 授权数据消费者、全部节点 |
| DHT | 密文分片索引、访问密钥引用 | 数据所有者 | 授权数据消费者 |
| 联合云节点 | 加密文档分片 | 数据所有者 | 授权数据消费者 |
为什么把引用和数据分开?区块链本身不适合存大文件,块大小和同步成本都不允许;DHT 在 P2P 系统里已经很成熟,负责维护“哪些分片在哪个节点”的元数据。论文里的做法是:区块链存储对数据的引用,比如文件哈希H(F)和一组 EKS 密文,检索到之后消费者再去 DHT 拿实际分片位置。这一层分离直接决定了后续搜索流程:DHT 不参与授权,授权逻辑全部收敛在区块链的访问控制层。若把 DHT 的元数据也上链,每次文件分片迁移都会产生一笔额外的链上写入,在分片频繁复制时会很快放大存储成本。
2.3 可检索性证明的挑战-响应
联合云节点可能节省存储,私藏部分分片不存,所以系统引入可检索性证明。论文里把文件 F 分成 n 个段:F = {F1, F2, ..., Fn},节点 P 需要证明它确实持有这些段。基本方案是挑战-响应:审计方发随机挑战集合 R,P 返回被挑战段和 Merkle 伴随路径,审计方用根摘要验证。下面是这个流程的逻辑模拟。
# por_sim.py —— 可检索性证明的挑战-响应逻辑模拟 import hashlib def setup(file_segments): # Setup(F) -> digest:叶子是文件段,根是摘要 nodes = [hashlib.sha256(seg).digest() for seg in file_segments] while len(nodes) > 1: nodes = [hashlib.sha256(nodes[i] + nodes[i + 1]).digest() for i in range(0, len(nodes), 2)] return nodes[0] def prove(file_segments, challenge_indices): # Prove(R -> F_ri, pi_i):按挑战下标返回数据块和Merkle伴随路径 proof = [] for idx in challenge_indices: block = file_segments[idx] path = build_merkle_path(file_segments, idx) proof.append((block, path)) return proof def verify(digest, challenge_indices, proof): # Verify(digest, R, F_ri, pi_i):用路径和块重建根摘要 if len(challenge_indices) != len(proof): return False for idx, (block, path) in zip(challenge_indices, proof): if not check_path(digest, idx, block, path): return False return True逻辑说明:setup对应论文里的Setup(F) -> digest,把文件分片作为叶子构造成 Merkle 树,根节点就是被公开的摘要。prove对应Prove(R -> F_ri, pi_i),每次收到随机挑战集合,只取被抽中的段和对应伴随路径,不需要返回整个文件。verify对应Verify(digest, R, F_ri, pi_i),逐条重建并比对路径,只要有一条不一致就判定失败。参数含义:file_segments是文件分片列表 F,challenge_indices是随机挑中的段下标 R,digest是 Setup 产出的 Merkle 根。build_merkle_path和check_path在实现时直接调用成熟 Merkle 树库即可,不必自己重写。
论文要求随机预言机模型生成挑战,并定期调度,负责挑战的区块链节点把证明记录到链上,其他节点可以验证。这里的关键是“挑战不可预测”:如果节点预先知道下轮会被查哪几个段,它可以只保存这些段,所以挑战必须由随机源产生。
2.4 这一层最容易踩的坑
第一个坑是混淆可检索性证明与副本证明。POR 证明“文件能取回”,不等于节点保留了多少份副本,也不覆盖“数据没有被删改”的所有场景。第二个坑是挑战频率:论文里用修正间隔调度随机挑战,实际部署时若间隔过短,审计流量会占满内网带宽;若间隔过长,节点恶意删数据后可以靠时间差补回。第三个坑是不要把 Merkle 根提交到链上后就不再管叶子:分片更新时根会变,需要有版本化机制,否则旧摘要和新分片对不上。这几个问题论文没有展开,属于实现层必须补的细节。
3. 私有关键字搜索落地:EKS加密方案与陷门匹配的代码推演
3.1 五步算法与参数含义
私有关键字搜索部分,论文用 EKS 可搜索加密方案,五个算法串起从密钥生成到匹配的全部过程。整理如下:
| 算法 | 输入 | 输出 | 职责 |
|---|---|---|---|
| KeyGen | 安全参数 | 主密钥 k | 生成密码系统密钥 |
| KeyDerive | k, 用户秘密 s | 搜索令牌 ks | 为数据消费者派生私有搜索关键字 |
| Trapdoor | 关键字 w, ks | 陷门 Tw | 生成不可逆向的搜索陷门 |
| Encrypt | 关键字 w, k | 密文 c=(r,h) | 为索引关键字生成可测密文 |
| Test | Tw, c | 0 或 1 | 判断陷门与密文是否对应同一关键字 |
说明一下记号:G1、G2 是素数阶 p 的乘法循环群,GT = G1 × G2;g1、g2、gT 分别是对应生成元。KeyGen 取随机k <- Z_p;KeyDerive 计算ks = g2^(k*s);Trapdoor 输出Tw = e(H(w)^s, ks),这里 H 把字符串映射到 G1;Encrypt 对关键字 W 计算密文;Test 验证等式。H2 是GT × GT -> {0,1}的哈希函数,实际作用是把配对运算结果压缩成固定长度比特串。
3.2 用HMAC模拟EKS的加密与测试
真实实现要引入双线性对运算,开发阶段不好调。常见做法是先用一个对称版本的模拟把流程跑通,验证“加密-陷门-测试”三者关系,之后再换双线性对库。下面代码用 HMAC-SHA256 模拟配对结果,函数接口与论文算法一一对应。
# eks_sim.py —— 模拟EKS五个算法的简化实现 import hmac import hashlib import os def keygen(): # KeyGen: 返回主密钥k return os.urandom(16) def key_derive(k, user_secret): # KeyDerive(k, s): 用主密钥和用户秘密派生搜索令牌ks return hmac.new(k, user_secret.encode(), hashlib.sha256).digest() def encrypt(keyword, k): # Encrypt: 为关键字W生成EKS密文c=(r,h) r = os.urandom(16) h = hmac.new(k, r + keyword.encode(), hashlib.sha256).digest() return (r, h) def trapdoor(keyword, ks): # Trapdoor: 生成陷门Tw,节点无法从Tw反推keyword return hmac.new(ks, keyword.encode(), hashlib.sha256).digest() def test(tw, cipher): # Test: 检查H2(r, tk)是否等于密文中的h r, h = cipher tk = tw return hmac.new(tk, r, hashlib.sha256).digest() == h # 数据所有者:加密索引关键字 master_key = keygen() cipher = encrypt("invoice", master_key) # 数据消费者:从所有者拿到派生令牌后检索 ks = key_derive(master_key, "user-007") tw = trapdoor("invoice", ks) print(test(tw, cipher)) # 输出True,说明匹配成功逻辑说明:keygen()对应 KeyGen,输出 16 字节随机字节串作为主密钥 k;key_derive(k, user_secret)对应 KeyDerive,把所有者主密钥与消费者秘密加盐派生出一个专属于该消费者的搜索令牌ks,这一步是“私有”的来源;encrypt(keyword, k)对应 Encrypt,生成(r, h)元组密文,r 是每次加密都不同的随机数;trapdoor(keyword, ks)对应 Trapdoor,把关键字和 ks 绑成陷门;test(tw, cipher)对应 Test,只比对 H2 结果,不还原关键字。最后一个用例演示完整流程:所有者用master_key加密关键字,授权消费者用派生令牌生成陷门,测试结果为 True。这个模拟省掉了双线性对,参数含义一一对应论文第 2.3 节的五个协议。真正生产代码里用petlib或pyUmbral这类密码学库替换 HMAC 部分,替换时保持函数接口不变即可。
提示:HMAC 版本的 EKS 只能用于流程演示,不能用于生产环境。双线性对上的可搜索加密方案里,Encrypt 和 Test 的随机数、幂运算顺序都由安全证明定义,私自替换成纯哈希会把数学安全性丢掉。
3.3 私有搜索与公钥可搜索搜索的区别
论文标题里“私有”两个字不是修饰。传统 PEKS 公钥可搜索加密允许任何持公钥的人为任意关键字生成陷门;这套系统的 KeyDerive 需要数据所有者把用户秘密 s 映射进搜索令牌ks,没有ks就无法构造合法 Tw,因此授权粒度被收紧到单个消费者。节点能做的只有 Test:把 trapdoor 和所有 EKS 密文逐个比对,如果输出 1 就把对应的H(F)返回给消费者。由于 H 的哈希特性,节点即使看到大量 Tw,也无法恢复关键字。
这里要注意,陷门是确定性生成的,同一个关键字和同一个 ks 会生成相同 Tw,节点可以观察两个不同请求的 Tw 是否相等,并推断它们是否在搜同一个词。论文没有进一步混淆这一步,真实产品里要在消费者端加随机化或代理层做缓存,否则匿名性会在这条侧信道里打折扣。
4. 匿名凭证验证与性能对比:承诺、零知识证明与搜索时间
4.1 匿名凭证的四步算法
访问控制部分由四个算法组成:Setup1(lambda) -> par、GenCred(s, par) -> (c, skc)、ShowCred(par, S, c, skc, Sc) -> pi_S、VerifyCred(par, pi, Sc) -> 1/0。数据所有者为消费者 B 生成假名 S,用数字承诺方案提交承诺C = g^S h^r;承诺打开随机数只有 B 知道,这样 B 才能在未来构造零知识证明,表明自己知道 C 的打开方式和公开值 r。公告板实际就是许可区块链,上面积累一组承诺集合Sc = {C1, C2, ..., Cn}。ShowCred 不直接出示 c,而是生成一个非交互式证明;验证者用累加器检查 c 是否属于 Sc,但不知道具体是哪一个。
累加器的计算也要看得懂:给定承诺集合,Accumulate(par, Sc)输出A = u^(c1*c2*...*cn) mod N,其中 N 是两个大素数乘积,u 是生成元。要为一个凭证 c 生成见证,就用GenWitness(par, c, Sc)对集合中除 c 以外的所有素数求累加,得到w = Accumulate(Sc - {c})。验证时只需检查w^c mod N == A。这种设计的价值在于证明体积不随订阅者数量增长,节点不需要遍历整张用户表。
4.2 三种云存储系统的性能对比
论文将本系统与两种现有方案做了对比:文献[13]是云存储加密数据的隐私保护全文检索系统,文献[14]是混合策略的低成本云存储方案。整理后如下。
| 方案 | 安全性 | 隐私性 | 依赖可信第三方 | 低成本 | 匿名性 |
|---|---|---|---|---|---|
| 文献[13]全文检索隐私保护 | 支持 | 支持 | 依赖 | 不支持 | 不支持 |
| 文献[14]混合策略低成本存储 | 支持 | 支持 | 依赖 | 不支持 | 不支持 |
| 基于区块链的数据存储系统 | 支持 | 支持 | 不依赖 | 支持 | 支持 |
从表里能看出,前两种方案在安全性和隐私性上并不差,差距集中在“依赖可信第三方”这一列。原设计把授权、搜索令牌生成、完整性审计全部搬到链上,联合云节点之间互相牵制,任何单一节点都不能独自通过审计,这就是它把对第三方信任降到最低的原因。低成本在这里指的不是存储单价更便宜,而是省略了数据所有者自己检索整个数据集再过滤的开销,客户端不需要下载完整密文集做匹配。
4.3 搜索时间为什么反而更短
论文在 Linux 平台、i7 处理器、8G 内存的笔记本上测了三种系统在不同数据存储量下的搜索时间,结果都是随数据量上升,但基于区块链的系统增长更平滑。原因在于它把离线存储访问、许可授权和搜索令牌生成做成一条主干:消费者拿到授权后直接对密文标签测陷门,命中的才去 DHT 取分片;全文检索方案需要在客户端本地完成更多过滤逻辑,混合策略存储方案要维护额外的中心调度。需要提醒的是,实验规模有限,不能直接外推到公链或 TB 级文件场景;许可链的吞吐、区块确认延迟仍会限制搜索请求并发,测试数据只说明架构方向有效,不代表任何未发布的性能承诺。
5. 从论文到原型:把DHT、智能合约和EKS串起来的最小实验技巧
5.1 用hash链模拟区块链中的EKS标签
搭建最小原型的常见做法是先不管共识和网络,只验证数据链路。论文第 2.3 节中,存储在区块链里的结构是(H(F), EKS(W1), ..., EKS(Wn)),即文件哈希加一组关键字密文。下面代码把每个块的数据体简化成文件哈希加一个 EKS 密文,用 hash 指针串成链。
# minimal_blockchain.py import hashlib prev_hash = b'0' * 32 for index in range(5): file_hash = hashlib.sha256(f'file-{index}'.encode()).digest() eks_label = hashlib.sha256(f'eks-{index}'.encode()).digest() block_hash = hashlib.sha256(prev_hash + file_hash + eks_label).digest() print(f'block {index}: {block_hash.hex()[:16]}') prev_hash = block_hash逻辑说明:prev_hash存放前一个区块的哈希,循环里把它与当前区块的文件哈希、EKS 标签拼在一起计算新的区块哈希。任何一节的eks_label被改动,后续所有区块哈希都会不匹配,这就是论文中“块通过哈希指针顺序连接”的最小可验证版本。参数说明:file_hash对应H(F),eks_label在这里用占位哈希模拟 EKS 密文;真实系统里把第 3 节encrypt()的返回值序列化后放进来即可。
5.2 两个验证细节
第一个细节:审计时一定要把 Merkle 路径节点也存进 DHT。POR 验证需要叶子块和路径一起提交,只有根摘要而没有路径,verify是无从下手的。第二个细节:EKS 密文入库前要测试一下“错误关键字不匹配”。调用test(trapdoor("invoice", ks), encrypt("report", k)),如果返回 True,说明生成陷门的派生态和加密用的主密钥不在同一条链路上,通常出在key_derive的拼接顺序上。把这两点排掉,再把第 2 节的 POR 模拟和第 3 节的 EKS 模拟组合起来,就能复现一个从上传、审计到检索的完整演示闭环。
本文还有配套的精品资源,点击获取