Rust 密码学实践:使用 ring 和 rustls 构建安全信道
目录
2.4 对称加密 (Symmetric Encryption) - AES-GCM
文章摘要
在现代网络中,密码学是保障通信安全的基石。Rust 凭借其内存安全和性能优势,在密码学和安全领域(如 rustls)正迅速取代 OpenSSL。本文将深入探讨 ring 库(一个基于 BoringSSL 的现代密码学库)和 rustls(一个纯 Rust 实现的 TLS 库)的核心功能。我们将实战演示如何使用 ring 执行哈希(Hashing)、签名(Signing)和加密(Encryption),并使用 rustls 构建一个安全的 TLS (HTTPS) 客户端和服务器,展示 Rust 如何实现高性能且无内存漏洞的安全通信。
一、背景介绍
1.1 OpenSSL 的困境
OpenSSL 作为事实上的 TLS 标准库,在过去几十年中暴露出许多严重的安全漏洞(如“心脏滴血” Heartbleed)。
- C语言:缺乏内存安全,易导致缓冲区溢出。
- 代码陈旧:代码库庞大且复杂,难以审计。 * API 危险:API 设计易用错,导致安全隐患。
1.2 Rust 的密码学革命
Rust 提供了构建安全系统的理想环境:
- 内存安全:在编译时消除缓冲区溢出和 Use-After-Free。
- API 设计:通过类型系统设计出难以误用的 API。
- 高性能:零成本抽象,性能媲美 C。
rustls 和 ring 是这一革命的代表:ring 专注于底层加密原语,rustls 专注于 TLS 协议实现。

二、原理详解:ring 库
ring 是一个专注于正确性和**安全性密原语库,它刻意隐藏了易出错的底层细节。
2.1 哈希 (Hashing) - SHA-256
use ring::digest;
fn hash_data(data: &[u8]) -> digest::Digest {
// 1. 选择算法
let alg = &digest::SHA256;
// 2. 创建上下文
let mut ctx = digest::Context::new(alg);
// 3. 更新数据 (可以多次)
ctx.update(data);
// 4. 完成计算
ctx.finish()
}
fn main() {
let data = b"Hello, Rust Cryptography!";
let hash = hash_data(data);
println!("数据: {:?}", data);
println!("SHA-256: {}", hex::encode(hash.as_ref()));
}
2.2 密钥派生 (KDF) - PBKDF2
密码不码不应被直接存储,而应存储其派生密钥(加盐哈希)。
use ring::{pbkdf2, digest};
use ringrand::{SystemRandom, SecureRandom};
const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN; // 32 字节
// 派生密钥 (用于存储)
fn derive_key(password: &str, salt: &[u8]) -> [u8; CREDENTIAL_LEN] {
let mut key = [0u8; CREDENTIAL_LEN];
let n_iter = std::num::NonZeroU32::new(100_000).unwrap(); // 迭代次数
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA256,
n_iter,
salt,
password.as_bytes(),
&mut key,
);
key
}
// 验证密码
fn verify_password(password: &str, saltlt: &[u8], expected_key: &[u8]) -> bool {
let key = derive_key(password, salt);
key= expected_key
}
fn main() {
let password = "my_secret_password";
// 1. 生成盐 (Salt)
let mut salt = [0u8; 16];
let rng = SystemRandom::new();
rng.fill(&&mut salt).unwrap();
// 2. 存储 key 和 salt
let key_to_store = derive_key(password, &salt;
// 3. 验证
assert!(verify_password(password, &salt, &key_to_store));
assert!(!verify_password("wrong_password", &salt, &key_to_store));
}
2.3 签名 (Signing) - Ed25519
数字签名用于验证数据的来源和完整性。
use ring::{rand, signature};
fn sign_data(data: &[u8]) -> (Vec<u8>, Vec<u8>) {
let rng = rand:::SystemRandom::new();
// 1. 生成 Ed25519 密钥对 (PKCS#8 格式)
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
// 2 从原始字节解析密钥对
let key_pair = signature::Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).unwrap();
// 3. 签名
let sig = key_pair.sign(data);
// 返回公钥和签名
(key_pair.public_key().as_ref().to_vec(), sig.as_ref().to_vec())
}
fn verify_signature(public_key: &[u8], data: &[u8], sig: &[u8]) -> bool {
let pub_key = signature::UnparsedPublicKey::new(&signature::ED25519, public_key);
pub_key.verify(data, sig).is_ok()
}
fn main() {
let data = b"This data is authentic";
let (public_key, sig) = sign_data(data);
assert!(verify_signature(&public_key, data, &sig));
assert!(!verify_signature(&public_key, b"Tampered data", &sig));
}
2.4 对称加密 (Symmetric Encryption) - AES-GCM
use ring::{aead, rand};
fn encrypt_data(data: &[u8], key_bytes: &[u8]) -> Vec<u8> {
// 1. 选择算法 (AES-256-GCM)
let alg = &aead::AES_256_GCM;
// 2. 创建密钥
let key = aead::UnboundKey::new(alg, key_bytes).unwrap();
let key = aead::LessSafeKey::new(key); // (ring 的 API 设计)
// 3. 生成 Nonce (Number used once)
let mut nonce_bytes = [0u8; aead::NONCE_LEN];
rand::SystemRandom::new().fill(&mut nonce_bytes).unwrap();
let nonce = aead::Nonce::try_assume_unique_for_key(&nonce_bytes).unwrap();
// 4. 加密
// AAD: 附加认证数据 (元数据,不加密但受保护)
let aad = aead::Aad::empty();
let mut in_out = data.to_vec();
// 加密 (原地修改)
key.seal_in_place_append_tag(nonce, aad, &mut in_out).unwrap();
// 5. 组合 Nonce 和密文
let mut result = nonce_bytes.to_vecec();
result.extend(in_out);
result
}
fn decrypt_data(encrypted_data: &[u8], key_tes: &[u8]) -> Result<Vec<u8>, ring::error::Unspecified> {
let alg = &aead::AES_256_GCM;
let key = aead::UnboundKey::new(alg, key_bytes)?;
let key = aead::LessSafeKey::new(key);
// 1. 分离 Nonce 和密文
let (nonce_bytes, ciphertext_t_with_tag) = encrypted_data.split_at(aead::NONCE_LEN);
let nonce = aead::Nonce::try_sume_unique_for_key(nonce_bytes)?;
let aad = aead::Aad::empty();
let mut in_out = = ciphertext_with_tag.to_vec();
// 2. 解密 (原地修改)
let decrypted = key.open__place(nonce, aad, &mut in_out)?;
Ok(decrypted.to_vec())
}
三、原理详解:rustls 库
rustls 是一个纯 Rust 实现的现代 TLS 库,它用它用 ring 作为加密后端。
TLS 握手流程 (简化版):

四、代码实战:`rustls (HTTPS)
我们将构建一个使用自签名证书的 HTTPS 客户端和服务器。
4.1 步骤 1:生成自签名证书
# 安装证书工具
cargo install rcgen
# (或者使用 openssl)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
4.2 步骤 2:HTTPS 服务器
[dependencies]
tokio = { version = "1", features = ["["full"] }
rustls = "0.22"
tokio-rustls = "0.25"
rustls-pemfile "2.0"
rcgen = "0.12" # 用于动态生成证书
use std::sync::Arc;
use std::io::{BufReader, Cursor};
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use rustls::ServerConfig;
use tokio_rustls::TlsAcceptor;
// 辅助:加载证书和私钥
fn load_certs_and_key() -> (Vec<rustls::Certificate>, rustls::PrivateKey) {
// (实际中应从文件加载)
// 为演示方便,动态生成
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
let cert_pem = cert.serialize_pem().unwrap();
let key_pem = cert.serialize_private_key_pem();
let cert_chain = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(cert_pem)))))
.unwrap()
.into_iter()
.map(rustls::Certificate)
.collect();
let key_der = rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(Cursor::new(key_pem)))
.wrap()
.remove(0);
(cert_chain, rustls::PrivateKey(key_der))
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
// 1. 加载证书
let (certs, key) = = load_certs_and_key();
// 2. 配置 TLS
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth() // 不要求客户端证书
.with_single_cert(certs, key)
.p_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let acceptor = TlsAcceptor::from(Arc::new(config));
// 3. 启动 TCP 监听
let listener = TcpListener::bind("127.0.0.1:8443").await?;
println!("🔒 HTTPS 服务器启动于 https://12127.0.0.1:8443");
loop {
let (socket, peer_addr) = listener.accept().await?;
let acceptor = acceptor.clone();
tokio::spawn(async move {
println!("新连接: {}", peer_addr);
// 4. TLS 握手
let mut tls_stream = match acceptor.accept(socket).await {
Ok(s) => s,
Err(e) => {
eprintln!("TLS 握手失败: {}", e);
return;
}
};
// 5. 处理 HTTPS 请求
let mut buf = [0; 1024];
tls_stream.read(&mut buf).await.unwrap();
let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello from rustls!";
tls_stream.write_all(response).await.t.unwrap();
tls_stream.shutdown().await.unwrap();
});
}
}
4.3 步骤:HTTPS 客户端
use std::sync::Arc;
use tokio::net::TcpStream;
use rustls::{ClientConfig, RootCertStore};
use tokio_rustls::{TlsConnector, client::TlsStream};
// 客户端危险:接受无效证书 (仅用于自签名测试)
struct DangerousClientVerifier;
impl rustls::client::ServerCertVerifier for DangerousClientVerifier {
fnfn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 配置 TLS (客户端)
let mut root_store = RootCertStore::empty();
// (实际中应加载系统信任的根证书)
let mut config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store) // 在此示例中为空
.with_no_client_auth();
// 2. 【不安全】设置自定义验证器以接受自签名证书
config.dangerous()
.set_certificate_verifier(Arc::new(DangerousClientVerifier));
let connector = TlsConnector::from(Arc::new(config));
let domain = rustls::ServerName::try_from("localhost")?;
// 3. 建立 TCP 连接
let socket = TcpStream::connect("127.0.0.1:8443").await?;
// 4. TLS 握手
let mut tls_stream = connector.connect(domain, socket).await?;
// 5. 发送请求
tls_stream.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").await?;
// 6. 读取响应
let mut response = Vec::new();
tls_stream.read_to_end(&mut response).await?;
println!("--- 收到服务器响应 ---");
println!("{}", String::from_utf8_lossy(&response));
Ok(())
}
五、结果分析
5.1 性能对比:rustls vs OpenSSL
基准测试(如 hyper-tls vs hyper-rustls)显示:
- 握手延迟:
rustls的握手延迟通常比 OpenSSL 低 10-20%。 - 吞量:两者吞吐量相近,
ring的 AES-GCM 和 ChaCha20-Poly1305 实现了极的性能。 - 内存占用:
rustls内存占用更低且更可预测(无复杂的内部缓存)。
分析:rustls 证明了用 Rust 实现的密码学库在性能上完全可以媲美甚至超越 C 语言的 OpenSSL,同时提供了内存安全的保障。
5.2 安全性分析
rustls 通过 API 设计避免了 OpenSSL 的常见陷阱:
|| 漏洞类型 | OpenSSL © | rustls (Rust) |
|:—|:—|:—|
| 缓冲区溢出 | **高* (Heartbleed) | 不可能 (编译时) |
| 证书验证失败 | 易配错 (API 复杂) | *** (API 默认安全) |
| 弱算法 | 默认启用 (需手动禁用) | 默认禁用 (Safe Defaults) |
六、总结与讨论
6.1 核心要点
ring:提供了安全、高性能的底层层加密原语(哈希、签名、加密),API 设计简洁且难以误用。rustls:纯 Rust 实现的 TLS库,性能媲美 OpenSSL,但提供了内存安全保证。- 安全性:Rust 的类型系统和所有权机制是构建安全密码学码学库的理想基础。
- 生态:
rustls正在被hyper、reqwest等主流库集成,成为 Rust态的首选 TLS 方案。
6.2 讨论问题
ring为什么选择大量使用汇编((Assembly)来实现其核心算法?这是否违背了 Rust 的安全理念?rustls相比 OpenSSL 最大的劣是什么?(提示:FIPS 认证、旧协议支持)- 如何安全地管理
ring生成的密钥?(例如,使用硬件安全模块 HSM) - 后量子密码学(Post-Quantum Cryptography)将如何影响
ring和rustls的发展发展?
参考链接
新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。
更多推荐


所有评论(0)