使用 Rust 实现自动证书签发 —— 基于 lynx-cert 的实战解读
数字证书是 HTTPS 加密通信的基石。Rust 生态在证书自动签发与管理方面发展迅速,为代理、网关等场景提供安全保障。本文基于 lynx-server 项目的 lynx-cert 模块,详解如何用 Rust 自动生成 CA 证书、自签名证书、为指定域名/IP 颁发服务证书,并实现 PEM 文件的读写。
1. 依赖库与准备
lynx-cert 主要用到了以下核心 crates:
- rcgen:证书生成与签名
- rsa:RSA 密钥生成
- tokio-rustls/rustls:TLS 配置与证书加载
- anyhow:统一错误处理
在 Cargo.toml 中添加相关依赖:
[dependencies]
rcgen = "0.11"
rsa = "0.9"
tokio-rustls = "0.25"
anyhow = "1.0"
rand = "0.8"
time = "0.3"
2. 生成自签名证书(Self-signed Certificate)
最基础的证书是自签名证书,常用于本地开发、内网测试等场景。
pub fn get_self_signed_cert(
subject_alt_names: Option<Vec<String>>,
) -> Result<(Certificate, KeyPair)> {
let subject_alt_names = subject_alt_names.unwrap_or_else(|| vec![
"localhost".to_string(),
"127.0.0.1".to_string()
]);
let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names)?;
Ok((cert, key_pair))
}
- 支持自定义或默认 SAN(Subject Alternative Name),如 localhost、127.0.0.1。
- 返回证书对象和密钥对,可直接用于 TLS 启动。
3. 生成 CA 根证书及密钥
要为其他域名签发证书,需先生成 CA 根证书和密钥:
pub fn gen_ca_private_key() -> Result<KeyPair> {
let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits)?;
let private_key_der = private_key.to_pkcs8_der()?;
let private_key = KeyPair::try_from(private_key_der.as_bytes())?;
Ok(private_key)
}
fn gen_ca_cert(key: &KeyPair) -> Result<Certificate> {
let mut params = CertificateParams::default();
let (yesterday, tomorrow) = validity_period(); // 证书有效期
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.distinguished_name.push(DnType::CommonName, "lynxProxy");
params.distinguished_name.push(DnType::OrganizationName, "lynxProxy");
params.not_before = yesterday;
params.not_after = tomorrow;
params.extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth);
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
params.key_usages.push(KeyUsagePurpose::CrlSign);
let ca_cert = params.self_signed(key)?;
Ok(ca_cert)
}
- 先生成 2048 位 RSA 私钥。
- 再生成 CA 证书,指定用途为签名证书(KeyCertSign)、吊销列表(CrlSign),并标记为 CA。
快速生成一对 CA 证书与密钥:
pub fn create_ca_cert_and_key() -> Result<(Certificate, KeyPair)> {
let private_key = gen_ca_private_key()?;
let ca_cert = gen_ca_cert(&private_key)?;
Ok((ca_cert, private_key))
}
4. 使用 CA 为域名/IP 签发服务端证书
核心函数如下:
pub fn gen_cert_by_ca(
ca_cert: &Certificate,
ca_key: &KeyPair,
host: HostType,
) -> Result<Certificate> {
let mut params = CertificateParams::default();
params.serial_number = Some(thread_rng().gen::<u64>().into());
let not_before = OffsetDateTime::now_utc() - Duration::seconds(NOT_BEFORE_OFFSET);
params.not_before = not_before;
params.not_after = not_before + Duration::seconds(TTL_SECS);
params.distinguished_name.push(DnType::CommonName, &host);
let subject_alt_name = match host {
HostType::DnsName(host) => SanType::DnsName(Ia5String::try_from(host)?),
HostType::IpAddress(ip) => SanType::IpAddress(ip),
};
params.subject_alt_names.push(subject_alt_name);
let cert = params.signed_by(ca_key, ca_cert, ca_key)?;
Ok(cert)
}
- 支持 DNS 名称和 IP 地址(通过 HostType 枚举区分)。
- 自动设置有效期、序列号、SAN。
- 用 CA 密钥对该证书进行签名,得到服务端可用的证书。
5. PEM 文件的读写(证书落盘与加载)
证书和密钥通常需落盘保存或加载:
- 写入文件
pub fn write_cert_to_file(ca_cert: Certificate, path: PathBuf) -> Result<()> {
fs::write(path, ca_cert.pem())?;
Ok(())
}
pub fn write_key_to_file(key: KeyPair, path: PathBuf) -> Result<()> {
fs::write(path, key.serialize_pem())?;
Ok(())
}
- 读取文件
pub fn read_cert_key_by_file(path: &PathBuf) -> Result<KeyPair> {
let pem_key = fs::read_to_string(path)?;
let key = KeyPair::from_pem(&pem_key)?;
Ok(key)
}
pub fn read_cert_by_file(key: &KeyPair, path: &PathBuf) -> Result<Certificate> {
let ca_data = fs::read_to_string(path)?;
let ca_params = CertificateParams::from_ca_cert_pem(&ca_data)?;
let ca_cert = ca_params.self_signed(key)?;
Ok(ca_cert)
}
6. 典型使用流程
- 生成 CA 证书和密钥
let (ca_cert, ca_key) = create_ca_cert_and_key()?; - 为指定域名/IP 签发服务证书
let cert = gen_cert_by_ca(&ca_cert, &ca_key, HostType::DnsName("example.com".to_string()))?; - 保存证书和密钥到 PEM 文件
write_cert_to_file(cert, PathBuf::from("server.crt"))?; - 在 TLS 服务端加载证书和密钥
结合 rustls、tokio-rustls 的ServerConfig配置即可。
7. 总结
lynx-cert 模块为 Rust 项目提供了简洁、易扩展的证书签发能力,无论是自签名、CA 还是为多域名/IP 动态签发,都能快速实现。适合代理、HTTPS 中间人、服务网关等安全场景。
推荐项目
本文基于 lynx-server 开源项目。lynx-server 是一个用 Rust 编写的现代化高性能代理服务器,支持多协议代理、智能证书签发与管理。如果你对网络安全、Rust 网络开发感兴趣,欢迎 Star、试用和贡献代码!