encryption-rust
语言 / Languages: 简体中文 | English
可插拔密码学组件库:在统一契约下提供对称加密、非对称加密、哈希、密钥派生(HKDF / PBKDF2),并包含 AES/Sodium、国密 SM2/SM3/SM4/ZUC 等实现,通过 Cargo 安装。
Locky 就是上面这只小锁,也是本项目的宠物:每个算法各持一把子密钥,每次加密都用新的 IV,钥匙孔从不泄露秘密。宿主项目也可以展示它:pet::SVG 返回 SVG 内容(可内联进 HTML),pet::ASCII 返回终端横幅,pet::NAME 即 Locky。
Rust 移植自 PHP 包 erikwang2013/encryption。
项目宠物:Locky
一只握着金钥匙的小锁。人设不是装饰,是把本库的设计画出来了:
| 形象 | 对应设计 |
|---|---|
| 锁孔(微笑) | Key / IV 纪律:钥匙孔从不透露自己见过什么,密钥与明文从不外泄 |
| 另一只手的金色钥匙 | 主密钥:EncryptionManagerFactory::from_master_key 从它派生各算法子密钥 |
| 一把锁只认自己的钥匙 | 认证加密:GCM / encrypt-then-MAC 解不开就是密钥不对或密文被改,没有第三种解释 |
| 每算法一份子密钥 | 同一主密钥下 AES 与 SM4/ZUC 互不共用密钥材料,避免密钥复用 |
座右铭:钥匙孔从不泄露秘密。
形象以 include_str! 打进库里(src/pet.rs,零运行时开销,不用就不链接),pet::ASCII 可直接打进终端或日志:
use pet;
println!;
// .-------.
// / \
// .-------------.
// | o o |
// | |
// | ,-. |
// | ( ) |
// | '-' |
// | | |
// '-------------'
pet::NAME / pet::TAGLINE / pet::ASCII / pet::SVG 四个常量对外公开,README、CLI banner、下游管理界面共用同一份。
宠物的整合点不止 README:三张设计图右上角各有一枚 Locky 小图标(纯内联 SVG,GitHub 上直接渲染),docs/social-preview.svg / social-preview.png 是宠物主视觉的 GitHub 社交预览图(1280×640,上传到仓库设置即可)。
docs/pet.svg 不能进 Cargo 的 exclude —— include_str! 在编译期读它,排掉就当场编译失败(cargo package 会直接报错,不会静默漏发)。
项目说明
这是什么
encryption-rust 是一个纯 Rust 密码学组件库,为 Rust 应用提供类型安全、可扩展的加解密、哈希与密钥派生能力。它不与任何框架耦合,可在 axum、actix-web 等 Web 框架,或纯库、CLI 项目中独立使用。
解决什么问题
Rust 生态中密码学实现长期分散:AES 用 aes-gcm、流密码用 chacha20poly1305、国密散落在 sm2 / sm3 / sm4 / zuc 等各自独立的 crate,HKDF / PBKDF2 缺少统一接口。本库在单一契约体系下收敛了主流对称 / 非对称 / 哈希 / KDF 算法,使得:
- 业务层只需面对接口,切换算法不改业务代码
- 国密与 AES/Sodium 同等待遇,注册后通过同一 Manager 调用
- 密钥管理规范化:一键从主密钥派生各算法子密钥,避免密钥复用
- 安全默认值:认证加密(GCM / encrypt-then-MAC)、随机 IV、常量时间比较等开箱即用
适用场景
- 字段级加解密(手机号、身份证号等敏感字段入库前加密)
- 多算法并存与平滑迁移(如从 AES-256-CBC 迁移到 AES-256-GCM)
- 需要国密合规的中后台系统(SM2 非对称、SM3 哈希、SM4 对称、ZUC 流密码)
- API 签名与验证(HMAC / SHA-256 / SM3)
- 基于口令或主密钥的子密钥派生(PBKDF2 / HKDF)
项目功能
- 五族能力,一套结构:对称加密 / 非对称加密 / 哈希 / 密钥派生 / 口令派生 —— 每族都是「契约(trait)→ 注册表(Registry)→ 门面(Manager)」的同一形状,学会一族就会全部。
- 十个开箱标识:
aes-256-gcm、sodium-xchacha20、aes-256-cbc-hmac、sm4-cbc、zuc-128、sm2、sha256、sm3、hkdf-sha256、pbkdf2-sha256(与 PHP 版标识一致,迁移零改名成本)。 - 主密钥工厂:
EncryptionManagerFactory::from_master_key—— 一把 32 字节主密钥按用途标签派生各算法独立子密钥,一次装配全部算法,杜绝密钥复用。 - 请求守卫 Guard + 框架适配:原生 Rust 与 axum / actix-web / rocket / poem / salvo / warp / bee-rust / e-cat 共 8 个 opt-in feature;处理器参数直接声明
Guard即可加解密。 - 国密一等公民:SM2 / SM3 / SM4 / ZUC 与 AES / SHA 走同一条注册-门面链路;SM1 / SM7 / SM9 给出明确错误而非静默。
- 安全默认值:随机 IV / nonce、AEAD 或 encrypt-then-MAC、常量时间比对、密钥材质 Drop 清零、Debug 遮蔽、解密失败不区分原因。
- 纯 Rust、零扩展依赖:没有
ext-sodium/ext-gmp之类门槛;ZUC 已经 GB/T 33133.1 标准向量校验。 - 文档即设计图:架构设计 / 功能设计 / 请求生命周期三张 SVG(中英文各一份),加上宠物 Locky。
- 零框架耦合:核心库不依赖任何 Web 框架,框架集成全部可选、默认一个都不拉。
目录
安装
[]
= "1.0"
快速开始
use EncryptionManagerFactory;
// 生产环境请从环境变量 / KMS 读取随机 32 字节主密钥,并妥善备份。
let master_key: = load_master_key;
let manager = from_master_key?;
// 字段级加密:密文是二进制,存 TEXT 列时自行 base64 / hex。
let stored = manager.encrypt?;
let phone = manager.decrypt?;
完整可运行版本见 examples/quickstart.rs:cargo run --example quickstart 会打印宠物 ASCII、加密 → 转文本 → 解密 → 篡改检测的完整往返。
架构概览
图源文件:docs/architecture-design.svg
能力被拆成五组契约,每组一套「契约(trait)→ 注册表(Registry)→ 门面(Manager)」;EncryptionManagerFactory 从一把主密钥派生全部子密钥并装配对称注册表。
| 能力 | 契约(trait) | 注册表 | 门面(默认算法) |
|---|---|---|---|
| 对称加密 | SymmetricCipher |
Registry<Box<dyn SymmetricCipher>> |
EncryptionManager(aes-256-gcm) |
| 非对称加密 | AsymmetricCipher |
Registry<Box<dyn AsymmetricCipher>> |
AsymmetricCryptoManager(sm2) |
| 哈希 | Hasher |
Registry<Box<dyn Hasher>> |
HashingManager(sha256) |
| 密钥派生(IKM) | KeyDerivation |
Registry<Box<dyn KeyDerivation>> |
KeyDerivationManager(hkdf-sha256) |
| 口令派生 | PasswordBasedKdf |
Registry<Box<dyn PasswordBasedKdf>> |
PasswordBasedKdfManager(pbkdf2-sha256) |
- 对称:实例绑定固定密钥;载荷为二进制,适合字段级批量加密。
- 非对称:每次调用传入密钥材料(十六进制,格式由实现约定,如 SM2)。
- 哈希:单向摘要,无密钥;
digest返回字节,digest_hex返回十六进制。 - 密钥派生:HKDF 把高熵材料展开为子密钥;PBKDF2 拉伸人类口令(随机盐 + 高迭代数)。
- 子密钥:
HMAC-SHA256(key = 主密钥, msg = 用途标签);SM4 / ZUC 取前 16 字节。 - 五个契约全部
Send + Sync:实现可跨线程共享,请求守卫得以零密钥拷贝地克隆。
功能设计
图源文件:docs/functional-design.svg
| 能力族 | 门面 | 随 crate 交付的标识 |
|---|---|---|
| 对称加密 | EncryptionManager |
aes-256-gcm / sodium-xchacha20 / aes-256-cbc-hmac / sm4-cbc / zuc-128 |
| 非对称加密 | AsymmetricCryptoManager |
sm2(另有静态门面 Sm2Service) |
| 哈希 | HashingManager |
sha256 / sm3(均 32 字节) |
| 密钥派生(IKM) | KeyDerivationManager |
hkdf-sha256(RFC 5869) |
| 口令派生 | PasswordBasedKdfManager |
pbkdf2-sha256(默认 310,000 次迭代) |
| 国密 | 同上五个门面 | sm2 / sm3 / sm4-cbc / zuc-128;SM1 / SM7 / SM9 → 明确错误 |
设计原则、安全默认值与「新增一个算法」的四步路径见上图右侧三块;每个算法的细节(密钥长度、载荷结构)见下文内置算法与标识。
请求生命周期
图源文件:docs/lifecycle.svg
写入路径(每进程引导一次,随后的 encrypt / decrypt 是每请求热路径):
- 配置:32 字节随机主密钥来自环境变量 / KMS,绝不写进代码;
- 派生:工厂按用途标签为每个算法派生子密钥,密钥永不跨算法复用;
- 注册:实现按自己的
identifier()注册进注册表,未知标识明确报错; - 加密:每次调用生成新的随机 IV / nonce,认证加密或 encrypt-then-MAC 打包为
v1 | IV | Tag/MAC | 密文。
读取路径:
- 落盘:二进制入 BLOB 列,文本存储先转 base64;
- 解析:记录旁的标识决定用哪个实现 —— 切换默认算法后旧数据照常解密;
- 校验:先查前缀与长度,再以常量时间比对 Tag / MAC,任何字节被改都在这里露馅;
- 解密:明文返回调用方;失败时不区分原因、不向客户端泄露任何细节。
密钥轮换、迁移与全部失败变体见图中底部的「维护与失败模式」。
内置算法与标识
对称加密(SymmetricCipher)
| 标识 | 类型 | 密钥长度 | 载荷 |
|---|---|---|---|
aes-256-gcm |
Aes256GcmEncryptor |
32 字节 | v1 | IV(12) | Tag(16) | 密文,AES-GCM 认证加密,推荐默认 |
sodium-xchacha20 |
SodiumXChaCha20Encryptor |
32 字节 | v1 | Nonce(24) | 密文‖Tag(16),XChaCha20-Poly1305,纯 Rust、恒可用 |
aes-256-cbc-hmac |
Aes256CbcEncryptor |
32 字节 | v1 | IV(16) | MAC(32) | 密文,CBC + HMAC,兼容旧环境 |
sm4-cbc |
Sm4CbcEncryptor |
16 字节 | 同 CBC-HMAC 结构,国密 SM4 |
zuc-128 |
Zuc128Encryptor |
16 字节 | 同 CBC-HMAC 结构,ZUC-128 流密码与密钥流异或 |
非对称加密(AsymmetricCipher)
| 标识 | 类型 | 说明 |
|---|---|---|
sm2 |
Sm2AsymmetricCipher / Sm2Service |
国密 SM2;密钥与密文为十六进制,密文布局 C1C3C2 |
哈希(Hasher)
| 标识 | 类型 | 输出长度 |
|---|---|---|
sha256 |
Sha256Hasher |
32 字节 |
sm3 |
Sm3Hasher |
32 字节 |
密钥派生
| 标识 | 类型 | 契约 | 说明 |
|---|---|---|---|
hkdf-sha256 |
HkdfSha256 |
KeyDerivation |
RFC 5869,基于 IKM + salt + info |
pbkdf2-sha256 |
Pbkdf2Sha256 |
PasswordBasedKdf |
口令 + 盐 + 迭代次数(默认 310,000,构造参数可调) |
SM1、SM7、SM9 不提供实现:调用 guomi::unavailable::sm1() 等会得到明确的 UnsupportedNationalAlgorithm 错误,便于业务层统一捕获或接入厂商 SDK / 加密机。
使用说明
1. 对称加密:单算法与注册表
use Aes256GcmEncryptor;
use EncryptionManager;
use Registry;
use SymmetricCipher;
// 单算法
let key = ;
let encryptor = new;
let blob = encryptor.encrypt?;
let plaintext = encryptor.decrypt?;
// 注册表 + 门面
let mut registry = new;
registry.register;
let manager = new?;
let blob = manager.encrypt?;
主密钥工厂:EncryptionManagerFactory::from_master_key(&master_key32, "aes-256-gcm") 一次注册 aes-256-gcm、aes-256-cbc-hmac、sodium-xchacha20、sm4-cbc、zuc-128 五个算法,各用按用途标签派生的独立子密钥。
2. 非对称加密(SM2)
use ;
use AsymmetricCryptoManager;
use Registry;
let pair = generate_key_pair_hex?; // 私钥 64 位 hex,公钥 130 位 hex(04‖X‖Y)
let cipher = new;
let ciphertext_hex = cipher.encrypt?;
let plaintext = cipher.decrypt?;
let mut registry = new;
registry.register;
let manager = new?;
let ciphertext_hex = manager.encrypt?;
公钥接受 130 位(04‖X‖Y)、128 位(X‖Y,自动补 04)与 66 位(压缩点)三种十六进制写法。
3. 哈希
use Sm3Hasher;
use Sha256Hasher;
use HashingManager;
use Registry;
let mut registry = new;
registry.register;
registry.register;
let hashing = new?;
let digest = hashing.digest?; // 32 字节
let hex = hashing.digest_hex_with?; // 十六进制
4. 密钥派生(HKDF / PBKDF2)
use ;
use ;
use Registry;
// 从高熵材料派生子密钥(如信封加密中的子密钥)
let hkdf = new;
let sub_key = hkdf.derive?;
let mut registry = new;
registry.register;
let kdf = new?;
// 从用户口令派生密钥(存储密码哈希请改用 Argon2 等专用 API)
let pbkdf2 = with_default_iterations; // 310,000 次,可 new(n) 调整
let derived = pbkdf2.derive_from_password?;
let mut registry = new;
registry.register;
let pwd = new?;
5. 国密(SM3 / SM4 / ZUC / SM2)
use ;
use ;
let digest = new.digest?;
let sm4 = new;
let blob = sm4.encrypt?;
let zuc = new;
let blob = zuc.encrypt?;
// SM2 见上文非对称示例。
6. 自定义插件
实现对应契约再注册即可,其余逻辑(门面、默认算法切换、错误语义)全部复用:
- 对称:实现
SymmetricCipher,注册到对称注册表; - 非对称:实现
AsymmetricCipher,注册到非对称注册表; - 哈希:实现
Hasher(只需实现digest,digest_hex有默认实现); - KDF:实现
KeyDerivation或PasswordBasedKdf,注册到对应注册表。
use ;
use Result;
manager.registry_mut.register;
7. 错误处理
失败时返回 encryption::error::Error,消息与 PHP 版同指:解密失败一律是「密钥不对或密文被改」,不区分原因。业务层应捕获并记录,不要把错误细节透传给前端。
match manager.decrypt
框架集成(可选 feature)
Guard 是框架无关的请求守卫:内部是一把 Arc<EncryptionManager>,克隆它就是引用计数递增,不含任何密钥拷贝。每个框架一个 opt-in feature,默认构建一个都不拉。
| feature | 集成方式 |
|---|---|
| (原生 Rust) | Guard::from_master_key(&key, "aes-256-gcm"),自行放进任意状态容器 |
axum |
实现 FromRequestParts:处理器参数直接写 Guard |
actix-web |
实现 FromRequest:注册 web::Data::new(guard),处理器参数写 Guard |
rocket |
实现 FromRequest(请求守卫):manage(guard),处理器参数写 Guard |
poem |
实现 FromRequest:.data(guard),处理器参数写 Guard |
salvo |
实现 Extractible:affix_state::inject(guard),处理器参数写 Guard |
warp |
with_guard(guard) 组合子接进过滤器链 |
bee-rust |
bee-rust 的 Router 直接吃 axum handler,复用 axum 适配(模块:integrations::bee_rust) |
ecat |
GuardLayer(tower Layer/Service)把 Guard 注入请求扩展 |
真实应用的状态结构体不会只有一把加密器。让状态实现 Guarded(只需指出守卫在哪个字段),各框架的提取逻辑就都能复用:
use Arc;
use Guard;
use Guarded;
// axum 示例:处理器参数直接声明要一把守卫
async
装配漏了(忘了 manage / app_data / with_state / .data(...))时是明确的 500(服务端装配错误),不是 panic,也不是 4xx。加解密失败由处理器自己决定怎么回应——本库不替应用决定「解密失败该返回 500 还是 422」。
与原版的差异
本库参照 PHP 版 erikwang2013/encryption 的架构与标识设计,但采用 Rust 独立格式:
- 密文与 PHP 版不互通:结构沿用
v1 | IV | Tag/MAC | 密文的外形,但无字节级互解承诺,也未做互解测试。 - 只有修正后的子密钥派生(HMAC 的 key 是主密钥、message 是用途标签);原版历史遗留的 v1 反序方案不复刻。
- 无扩展依赖:Sodium XChaCha20 为纯 Rust 实现,恒可用(原版需
ext-sodium);SM2 无ext-gmp门槛。 - SM2 密文 / 密钥编码以 RustCrypto
sm2实现的标准为准(C1C3C2、C1 非压缩),不对齐原版 pohoc/crypto-sm 的布局。 - 框架适配为 Rust 生态重写:原生 Rust(
Guard请求守卫)+ axum / actix-web / rocket / poem / salvo / warp / bee-rust / e-cat,对应原版的 Laravel / ThinkPHP / Hyperf / webman 接入方式。
项目目录
encryption-rust/
├── src/
│ ├── lib.rs crate 文档与模块导出
│ ├── contract.rs 五种能力契约(trait)与 Identified
│ ├── registry.rs 泛型注册表 Registry<T>
│ ├── manager.rs 五个门面:Encryption / Asymmetric / Hashing / KeyDerivation / PasswordBasedKdf
│ ├── factory.rs 主密钥工厂:子密钥派生与注册表装配
│ ├── guard.rs 请求守卫 Guard(Arc<EncryptionManager>,框架无关)
│ ├── integrations/ 8 个框架适配(各自 feature 门控)
│ │ axum / actix / rocket / poem / salvo / warp / bee_rust / ecat
│ ├── key.rs 定长密钥材质 Key<N>(Drop 清零、遮蔽 Debug)
│ ├── error.rs Error / Result
│ ├── encryptor/ aes256gcm / aes256cbc / sodium_xchacha20
│ ├── hash/ sha256
│ ├── kdf/ hkdf_sha256 / pbkdf2_sha256
│ ├── guomi/ sm2 / sm3 / sm4 / zuc / unavailable(SM1/SM7/SM9 占位)
│ ├── internal/ hex 工具与 etm.rs(CBC/SM4/ZUC 共用的 encrypt-then-MAC)
│ └── pet.rs 项目宠物 Locky(NAME / TAGLINE / ASCII / SVG)
├── tests/
│ ├── crypto_roundtrip.rs 各算法与五个门面的端到端往返
│ ├── invariants.rs 跨算法不互通、篡改必败、IV 新鲜性、插件扩展
│ └── sm2.rs SM2 密钥对、门面与错误路径
├── examples/quickstart.rs 零配置快速开始(加密 → 存 → 读 → 解密 → 篡改检测)
├── docs/
│ ├── pet.svg 项目宠物形象(src/pet.rs 用 include_str! 内联)
│ ├── architecture-design.svg 架构设计(本 README 引用)
│ ├── functional-design.svg 功能设计(本 README 引用)
│ ├── lifecycle.svg 请求生命周期(本 README 引用)
│ ├── social-preview.svg/png GitHub 社交预览(宠物 Locky 主视觉,上传仓库设置用)
│ └── i18n/en/ 英文版 README 与三张英文设计图
├── Cargo.toml
├── SECURITY.md
└── LICENSE
常见问题
需要安装系统扩展吗?
不需要。整套实现都是纯 Rust crate:没有 ext-sodium、ext-gmp 之类的门槛,cargo build 即可。
密文为什么是二进制?
与原版一致:库产出 / 接受字节。要存 JSON / TEXT 列,自行 base64 或 hex 编码(见 examples/quickstart.rs)。
主密钥从哪来? 32 字节随机数,从环境变量 / KMS / 密钥服务读取,不要写进代码、不要进版本库;必须备份——丢了主密钥就等于丢了数据。
为什么解密失败时错误信息不区分原因? 密钥不对、密文被改、算法用错都是同一种失败。区分原因等于给攻击者提供 oracle,也会把内部细节泄露给前端。
SM2 密文的布局是什么?
C1C3C2(现行标准默认布局),C1 为非压缩点 04‖X‖Y。与原版 pohoc/crypto-sm 的十六进制约定不一定逐字节一致(见「与原版的差异」)。
设计图有英文版吗?
有。English README 使用 docs/i18n/en/ 下的三张英文设计图;图片源文件与本文引用的是同一套布局。
安全建议
- 主密钥:32 字节 CSPRNG 随机;存环境变量 / KMS,不进代码库、不进日志;离线备份并限定访问。
- 口令:存储用户密码用 Argon2 / bcrypt 等专用 API;PBKDF2 仅用于「从口令派生加解密密钥」,迭代次数按环境调高。
- 错误:捕获并记录日志,不向前端透传错误细节。
- 算法选择:新系统默认
aes-256-gcm;CBC / SM4-CBC 的加密-后-MAC 由库保证,勿自行拼装。 - 随机性:IV / nonce 每次加密由库生成(系统 CSPRNG),不要复用,也不要自备。
运行测试
覆盖:各算法往返与篡改必败、跨算法不互通、工厂子密钥独立性与校验、注册表 / 门面行为、SM2 密钥路径、宠物常量,以及各框架的守卫提取(含「忘注册 → 500」路径)。
参考原项目
许可证
MIT © 2026 erik — https://erik.xyz