EffortlessMetrics/uselesskey

GitHub: EffortlessMetrics/uselesskey

一个用于 Rust 测试的确定性加密固件生成库,通过运行时生成密钥和证书避免将测试用的敏感数据提交到代码仓库。

Stars: 0 | Forks: 0

# uselesskey [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/8a72dd22f5073848.svg)](https://github.com/EffortlessMetrics/uselesskey/actions/workflows/ci.yml) [![Crates.io](https://img.shields.io/crates/v/uselesskey.svg)](https://crates.io/crates/uselesskey) [![docs.rs](https://docs.rs/uselesskey/badge.svg)](https://docs.rs/uselesskey) [![MSRV](https://img.shields.io/badge/MSRV-1.92-blue.svg)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license) *Rust 的确定性加密测试固件(fixture)。* **停止将 PEM/DER/JWK 数据块提交到你的仓库中。** 一个在运行时生成加密密钥材料和 X.509 证书的测试固件工厂。这不是一个加密库。 ## 问题所在 密钥扫描器改变了测试固件的规则: - **GitGuardian** 扫描 PR 中的每一次提交。“添加后删除”仍然会触发事故。 - **GitHub push protection** 会阻止推送,直到从所有提交中移除该密钥。 - 路径忽略虽然存在,但需要持续的维护和文档说明。 即使是看起来像真密钥的假密钥也会引起摩擦。这个 crate 用一个 dev-dependency 替代了“安全策略 + 文档 + 例外”。 ## 为什么不直接…… | 方法 | 缺点 | |----------|----------| | 提交 PEM 文件 | 触发 GitGuardian/GitHub push protection | | 在测试中临时生成密钥 | 无缓存,RSA 密钥生成慢,无确定性 | | 直接使用原始加密 crate | PEM/DER 编码样板代码多,无负面固件 | | 直接使用 `rcgen` | 非测试固件导向;无确定性模式,无负面固件 | ## 你将获得什么 **算法:** - RSA(2048, 3072, 4096 位) - ECDSA(P-256, P-384) - Ed25519 - HMAC(HS256, HS384, HS512) - OpenPGP(RSA 2048/3072, Ed25519) - Token 固件(API key, bearer, OAuth access token/JWT 形状) **输出格式:** - PKCS#8 PEM/DER(私钥) - SPKI PEM/DER(公钥) - OpenPGP ascii armored 和二进制密钥块(需要 `pgp` feature) - JWK/JWKS(需要 `jwk` feature) - 临时文件(适用于需要路径的库) - X.509 自签名证书和证书链(需要 `x509` feature) **负面固件:** - 损坏的 PEM(错误的 base64、错误的头部、被截断) - 被截断的 DER - 不匹配的密钥对(有效的公钥但与私钥不匹配) - X.509:过期的叶子/中间证书、主机名不匹配、未知 CA、已吊销的叶子证书(带 CRL) ## 快速开始 添加到 `Cargo.toml`: ``` [dev-dependencies] uselesskey = "0.2" ``` 生成密钥: ``` use uselesskey::{Factory, Seed, RsaSpec, RsaFactoryExt}; // Random mode (different keys each run) let fx = Factory::random(); // Deterministic mode (stable keys from seed) let seed = Seed::from_env_value("my-test-seed").unwrap(); let fx = Factory::deterministic(seed); // Or fall back to random if env var not set let fx = Factory::deterministic_from_env("USELESSKEY_SEED") .unwrap_or_else(|_| Factory::random()); // Generate RSA keypair let rsa = fx.rsa("issuer", RsaSpec::rs256()); let pkcs8_pem = rsa.private_key_pkcs8_pem(); let spki_der = rsa.public_key_spki_der(); ``` ### JWK / JWKS ``` use uselesskey::{Factory, RsaSpec, RsaFactoryExt}; let fx = Factory::random(); let rsa = fx.rsa("issuer", RsaSpec::rs256()); let jwk = rsa.public_jwk(); let jwks = rsa.public_jwks(); ``` ### 临时文件 ``` use uselesskey::{Factory, RsaSpec, RsaFactoryExt}; let fx = Factory::random(); let rsa = fx.rsa("server", RsaSpec::rs256()); let keyfile = rsa.write_private_key_pkcs8_pem().unwrap(); assert!(keyfile.path().exists()); ``` ### X.509 证书 用于简单 TLS 测试的自签名证书: ``` use uselesskey::{Factory, X509FactoryExt, X509Spec}; let fx = Factory::random(); let cert = fx.x509_self_signed("my-service", X509Spec::self_signed("test.example.com")); let cert_pem = cert.cert_pem(); let key_pem = cert.private_key_pkcs8_pem(); ``` 三级证书链(根 CA → 中间 CA → 叶子证书): ``` use uselesskey::{Factory, X509FactoryExt, ChainSpec}; let fx = Factory::random(); let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com")); // Standard TLS server chain (leaf + intermediate, no root) let chain_pem = chain.chain_pem(); // Individual certs for custom setups let root_pem = chain.root_cert_pem(); let leaf_key = chain.leaf_private_key_pkcs8_pem(); ``` ### X.509 负面固件 生成故意无效的证书以测试错误处理路径: ``` use uselesskey::{Factory, X509FactoryExt, ChainSpec}; let fx = Factory::random(); let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com")); // Expired leaf certificate let expired = chain.expired_leaf(); // Hostname mismatch (SAN doesn't match expected hostname) let wrong_host = chain.hostname_mismatch("wrong.example.com"); // Signed by an unknown CA (not in your trust store) let unknown = chain.unknown_ca(); // Revoked leaf with CRL signed by the intermediate CA let revoked = chain.revoked_leaf(); let crl_pem = revoked.crl_pem().expect("CRL present for revoked variant"); ``` ### 负面固件(密钥) ``` use uselesskey::{Factory, RsaSpec, RsaFactoryExt}; use uselesskey::negative::CorruptPem; let fx = Factory::random(); let rsa = fx.rsa("issuer", RsaSpec::rs256()); let bad_pem = rsa.private_key_pkcs8_pem_corrupt(CorruptPem::BadBase64); let truncated = rsa.private_key_pkcs8_der_truncated(32); let mismatched_pub = rsa.mismatched_public_key_spki_der(); ``` ### Token 固件 生成逼真的 token 形状固件,而无需提交 token 数据块: ``` use uselesskey::{Factory, TokenFactoryExt, TokenSpec}; let fx = Factory::random(); let api_key = fx.token("billing", TokenSpec::api_key()); let bearer = fx.token("gateway", TokenSpec::bearer()); let oauth = fx.token("issuer", TokenSpec::oauth_access_token()); assert!(api_key.value().starts_with("uk_test_")); assert!(bearer.authorization_header().starts_with("Bearer ")); assert_eq!(oauth.value().split('.').count(), 3); ``` ## 适配器示例 适配器 crate 将 uselesskey 固件桥接到第三方库类型。它们是独立的 crate(而非 feature),以避免耦合版本控制。完整列表请参阅 [Workspace Crates](#workspace-crates) 部分。 ### TLS 配置构建器 (uselesskey-rustls) 使用 `tls-config` feature,一行代码构建 rustls 配置: ``` [dev-dependencies] uselesskey-rustls = { version = "0.2", features = ["tls-config", "rustls-ring"] } ``` ``` use uselesskey_core::Factory; use uselesskey_x509::{X509FactoryExt, ChainSpec}; use uselesskey_rustls::{RustlsServerConfigExt, RustlsClientConfigExt}; let fx = Factory::random(); let chain = fx.x509_chain("my-service", ChainSpec::new("test.example.com")); let server_config = chain.server_config_rustls(); // ServerConfig (no client auth) let client_config = chain.client_config_rustls(); // ClientConfig (trusts root CA) ``` ### ring 签名密钥 (uselesskey-ring) ``` [dev-dependencies] uselesskey-ring = { version = "0.2", features = ["all"] } ``` ``` use uselesskey_core::Factory; use uselesskey_rsa::{RsaFactoryExt, RsaSpec}; use uselesskey_ring::RingRsaKeyPairExt; let fx = Factory::random(); let rsa = fx.rsa("signer", RsaSpec::rs256()); let ring_kp = rsa.rsa_key_pair_ring(); // ring::rsa::KeyPair ``` ### RustCrypto 类型 (uselesskey-rustcrypto) ``` [dev-dependencies] uselesskey-rustcrypto = { version = "0.2", features = ["all"] } ``` ``` use uselesskey_core::Factory; use uselesskey_rsa::{RsaFactoryExt, RsaSpec}; use uselesskey_rustcrypto::RustCryptoRsaExt; let fx = Factory::random(); let rsa = fx.rsa("signer", RsaSpec::rs256()); let rsa_pk = rsa.rsa_private_key(); // rsa::RsaPrivateKey ``` ### aws-lc-rs 类型 (uselesskey-aws-lc-rs) ``` [dev-dependencies] uselesskey-aws-lc-rs = { version = "0.2", features = ["native", "all"] } ``` ``` use uselesskey_core::Factory; use uselesskey_rsa::{RsaFactoryExt, RsaSpec}; use uselesskey_aws_lc_rs::AwsLcRsRsaKeyPairExt; let fx = Factory::random(); let rsa = fx.rsa("signer", RsaSpec::rs256()); let lc_kp = rsa.rsa_key_pair_aws_lc_rs(); // aws_lc_rs::rsa::KeyPair ``` ### gRPC TLS (uselesskey-tonic) ``` [dev-dependencies] uselesskey-tonic = "0.2" ``` ``` use uselesskey_core::Factory; use uselesskey_x509::{X509FactoryExt, ChainSpec}; use uselesskey_tonic::{TonicClientTlsExt, TonicServerTlsExt}; let fx = Factory::random(); let chain = fx.x509_chain("grpc", ChainSpec::new("test.example.com")); let server_tls = chain.server_tls_config_tonic(); let client_tls = chain.client_tls_config_tonic("test.example.com"); ``` ## 可运行示例 [`crates/uselesskey/examples/`](crates/uselesskey/examples/) 目录包含独立的程序,你可以使用 `cargo run -p uselesskey --example` 运行它们: | 示例 | 描述 | |---------|-------------| | [`adapter_jsonwebtoken`](crates/uselesskey/examples/adapter_jsonwebtoken.rs) | 使用 `jsonwebtoken` crate 集成签名和验证 JWT | | [`adapter_rustls`](crates/uselesskey/examples/adapter_rustls.rs) | 将 X.509 固件转换为 rustls `ServerConfig` / `ClientConfig` | | [`basic_ecdsa`](crates/uselesskey/examples/basic_ecdsa.rs) | 生成 P-256 和 P-384 的 ECDSA 密钥对(PEM, DER, JWK 格式) | | [`basic_ed25519`](crates/uselesskey/examples/basic_ed25519.rs) | 生成 PEM、DER 和 JWK 格式的 Ed25519 密钥对 | | [`basic_hmac`](crates/uselesskey/examples/basic_hmac.rs) | 生成 HS256、HS384 和 HS512 的 HMAC 密钥 | | [`basic_rsa`](crates/uselesskey/examples/basic_rsa.rs) | 生成 PEM、DER 和 JWK 格式的 RSA 密钥对 | | [`basic_token`](crates/uselesskey/examples/basic_token.rs) | 生成 API key、bearer 和 OAuth access-token 固件 | | [`basic_usage`](crates/uselesskey/examples/basic_usage.rs) | 多合一:RSA、ECDSA 和 Ed25519 固件生成 | | [`deterministic`](crates/uselesskey/examples/deterministic.rs) | 从种子生成可复现固件 —— 相同种子总是产出相同密钥 | | [`deterministic_mode`](crates/uselesskey/examples/deterministic_mode.rs) | 顺序无关的确定性派生保证 | | [`jwk_generation`](crates/uselesskey/examples/jwk_generation.rs) | 使用 `JwksBuilder` 跨密钥类型构建 JWK 和 JWKS | | [`jwk_jwks`](crates/uselesskey/examples/jwk_jwks.rs) | 来自多种密钥类型并带有元数据检查的 JWK 集合 | | [`jwks`](crates/uselesskey/examples/jwks.rs) | 从 RSA 和 ECDSA 公钥构建 JWKS | | [`jwks_server_mock`](crates/uselesskey/examples/jwks_server_mock.rs) | 为模拟 `/.well-known/jwks.json` 端点生成 JWKS 响应体 | | [`jwt_rs256_jwks`](crates/uselesskey/examples/jwt_rs256_jwks.rs) | 用于 JWT 验证流程的带有 JWK/JWKS 提取的 RSA 密钥对 | | [`jwt_signing`](crates/uselesskey/examples/jwt_signing.rs) | 使用确定性 RSA、ECDSA 和 HMAC 密钥进行 JWT 签名 | | [`negative_fixtures`](crates/uselesskey/examples/negative_fixtures.rs) | 用于错误路径测试的故意无效证书和密钥 | | [`tempfile_paths`](crates/uselesskey/examples/tempfile_paths.rs) | 将密钥固件写入临时文件以用于基于路径的 API | | [`tempfiles`](crates/uselesskey/examples/tempfiles.rs) | 将 X.509 证书、密钥和身份 PEM 写入临时文件 | | [`tls_server`](crates/uselesskey/examples/tls_server.rs) | 用于 TLS 服务器测试的证书链生成 | | [`token_generation`](crates/uselesskey/examples/token_generation.rs) | 用于测试的逼真 API key、bearer token 和 OAuth token | | [`x509_certificates`](crates/uselesskey/examples/x509_certificates.rs) | 自签名证书、证书链和负面 X.509 固件 | ## Workspace Crates `uselesskey` 是一个 **外观 crate (facade crate)**,从专注于实现的 crate 重新导出。 为了方便依赖外观 crate,或者为了最小化编译时间而依赖单独的 crate。 ### 实现 Crates | Crate | 描述 | |-------|-------------| | [`uselesskey`](https://crates.io/crates/uselesskey) | 公共外观 —— 在 feature flags 背后重新导出所有密钥类型和 traits | | [`uselesskey-core`](https://crates.io/crates/uselesskey-core) | 工厂、确定性派生、缓存和负面固件辅助工具 | | [`uselesskey-rsa`](https://crates.io/crates/uselesskey-rsa) | RSA 2048/3072/4096 密钥对(PKCS#8, SPKI, PEM, DER) | | [`uselesskey-ecdsa`](https://crates.io/crates/uselesskey-ecdsa) | ECDSA P-256 / P-384 密钥对 | | [`uselesskey-ed25519`](https://crates.io/crates/uselesskey-ed25519) | Ed25519 密钥对 | | [`uselesskey-hmac`](https://crates.io/crates/uselesskey-hmac) | HMAC HS256/HS384/HS512 密钥 | | [`uselesskey-pgp`](https://crates.io/crates/uselesskey-pgp) | OpenPGP 密钥固件(armored + binary keyblocks) | | [`uselesskey-token`](https://crates.io/crates/uselesskey-token) | API key、bearer token 和 OAuth access-token 固件 | | [`uselesskey-jwk`](https://crates.io/crates/uselesskey-jwk) | 类型化 JWK/JWKS 模型和构建器 | | [`uselesskey-x509`](https://crates.io/crates/uselesskey-x509) | X.509 自签名证书和证书链 | ### 适配器 Crates | Crate | 集成对象 | |-------|-----------------| | [`uselesskey-jsonwebtoken`](https://crates.io/crates/uselesskey-jsonwebtoken) | `jsonwebtoken` `EncodingKey` / `DecodingKey` | | [`uselesskey-rustls`](https://crates.io/crates/uselesskey-rustls) | `rustls` `ServerConfig` / `ClientConfig` 构建器 | | [`uselesskey-tonic`](https://crates.io/crates/uselesskey-tonic) | `tonic::transport` TLS 身份 / 配置(用于 gRPC) | | [`uselesskey-ring`](https://crates.io/crates/uselesskey-ring) | `ring` 0.17 原生签名密钥类型 | | [`uselesskey-rustcrypto`](https://crates.io/crates/uselesskey-rustcrypto) | RustCrypto 原生类型(`rsa::RsaPrivateKey` 等) | | [`uselesskey-aws-lc-rs`](https://crates.io/crates/uselesskey-aws-lc-rs) | `aws-lc-rs` 原生类型 | ## Feature Flags | Feature | 描述 | |---------|-------------| | `rsa` | RSA 密钥对(默认) | | `ecdsa` | ECDSA P-256/P-384 密钥对 | | `ed25519` | Ed25519 密钥对 | | `hmac` | HMAC 密钥 | | `pgp` | OpenPGP 密钥对(armored + binary keyblocks) | | `token` | API key、bearer token 和 OAuth access token 固件 | | `x509` | X.509 证书生成(隐含 `rsa`) | | `jwk` | 为启用的密钥类型提供 JWK/JWKS 输出 | | `all-keys` | 所有密钥算法(`rsa` + `ecdsa` + `ed25519` + `hmac` + `pgp`) | | `full` | 所有内容(`all-keys` + `token` + `x509` + `jwk`) | 按 Feature 划分的扩展 trait: - `rsa`: `RsaFactoryExt` - `ecdsa`: `EcdsaFactoryExt` - `ed25519`: `Ed25519FactoryExt` - `hmac`: `HmacFactoryExt` - `pgp`: `PgpFactoryExt` - `token`: `TokenFactoryExt` - `x509`: `X509FactoryExt` ## Feature 矩阵 ### 外观 crate features(`uselesskey` crate) | Feature | 扩展 Trait | 算法 / 输出 | 隐含 | |---------|----------------|---------------------|---------| | `rsa` *(默认)* | `RsaFactoryExt` | RSA 2048/3072/4096 — PKCS#8, SPKI, PEM, DER | — | | `ecdsa` | `EcdsaFactoryExt` | P-256 (ES256), P-384 (ES384) — PKCS#8, SPKI | — | | `ed25519` | `Ed25519FactoryExt` | Ed25519 — PKCS#8, SPKI | — | | `hmac` | `HmacFactoryExt` | HS256, HS384, HS512 | — | | `pgp` | `PgpFactoryExt` | OpenPGP RSA 2048/3072, Ed25519 — armored, binary | — | | `token` | `TokenFactoryExt` | API key, bearer, OAuth access token | — | | `x509` | `X509FactoryExt` | 自签名证书、证书链、负面证书 | `rsa` | | `jwk` | — | 为所有启用的密钥类型提供 JWK/JWKS 输出 | — | | `all-keys | — | *(捆绑包)* | `rsa` `ecdsa` `ed25519` `hmac` `pgp` | | `full` | — | *(所有内容)* | `all-keys` `token` `x509` `jwk` | ### 适配器 crate 密钥类型支持 每个适配器 crate 都有按算法划分的 feature flags(`rsa`, `ecdsa`, `ed25519`, `hmac`)和一个 `all` 便利 flag。 | 适配器 | RSA | ECDSA | Ed25519 | HMAC | X.509 / TLS | 额外 features | |---------|:---:|:-----:|:-------:|:----:|:-----------:|----------------| | `uselesskey-jsonwebtoken` | ✓ | ✓ | ✓ | ✓ | — | — | | `uselesskey-ring` | ✓ | ✓ | ✓ | — | — | — | | `uselesskey-rustcrypto` | ✓ | ✓ | ✓ | ✓ | — | — | | `uselesskey-aws-lc-rs` | ✓ | ✓ | ✓ | — | — | `native` (启用 aws-lc-rs dep) | | `uselesskey-rustls` | ✓ | ✓ | ✓ | — | ✓ | `tls-config`, `rustls-ring`, `rustls-aws-lc-rs` | | `uselesskey-tonic` | — | — | — | — | ✓ | — | ## 为什么选择这个 Crate? ### 顺序无关的确定性 `seed + (domain, label, spec, variant) -> derived seed -> artifact` 添加新固件不会干扰现有的固件。测试顺序无关紧要。 ### 基于身份的缓存 RSA 密钥生成开销大。通过 `(domain, label, spec, variant)` 进行进程内缓存,使得运行时生成足够廉价,可以替代提交的固件。 ### 形状优先的输出 请求 PKCS#8/SPKI/JWK,而不是加密原语。用户不需要知道哪个 crate 负责编码。 ### 负面固件作为一等公民 损坏的 PEM、被截断的 DER、不匹配的密钥、过期的证书、带 CRL 的已吊销叶子证书。这些手动生成很麻烦,这就是为什么团队会提交它们。这个 crate 使它们变得廉价且短暂。 ### 何时不使用此 crate - 生产环境密钥生成或证书管理 - 证书验证逻辑(请使用 `rustls`, `x509-parser`) - 运行时 CA 操作(请直接使用 `rcgen`) ## 生态系统 当你需要 **不会触发密钥扫描器的测试固件** 时,请使用 uselesskey。如果你需要用于生产环境的运行时证书生成(例如内部 CA),请直接使用 [`rcgen`](https://docs.rs/rcgen)。如果你需要证书验证逻辑,请参阅 [`rustls`](https://docs.rs/rustls) 或 [`x509-parser`](https://docs.rs/x509-parser)。 ## 稳定性与版本控制 **派生稳定性:** 使用给定 `(seed, domain, label, spec, variant)` 元组生成的产物在同一 `DerivationVersion` 内是稳定的。我们永远不会更改 `V1` 输出;如果派生逻辑发生变化,将引入新版本(例如 `V2`)。 **MSRV:** 最低支持的 Rust 版本为 **1.92**(edition 2024)。 ## 许可证 根据以下任一许可授权: - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)) - MIT license ([LICENSE-MIT](LICENSE-MIT)) 由你选择。
标签:Crate, DER, ECDSA, Ed25519, GitGuardian, GitHubPushProtection, HMAC, JWK, OpenPGP, PEM, PKCS8, RSA, Rust, SOC Prime, X.509证书, 代码安全, 加密测试夹具, 单元测试, 可视化界面, 密钥生成, 开发工具, 漏洞枚举, 确定性, 网络流量审计, 误报消除, 通知系统, 集成测试, 零依赖