rustls:纯 Rust TLS 库深度工程实战
一、引言
TLS(Transport Layer Security)是现代互联网安全的基石。从 HTTPS 到服务网格,从数据库连接到消息队列加密,几乎每一个需要安全通信的场景都依赖 TLS。长期以来,OpenSSL 是这一领域的事实标准,但它的 C 代码库积累了几十年的技术债务:内存安全漏洞频发、API 设计复杂、构建系统令人头疼。
rustls 的出现改变了这一局面。作为一个纯 Rust 编写的 TLS 库,rustls 从设计之初就充分利用 Rust 的类型系统和所有权模型,在编译期消除整类内存安全问题。它被 curl、AWS SDK、Linkerd 生产级项目采用,并逐步进入 Linux 内核生态讨论视野。
本文将从工程实战角度深入 rustls:架构设计哲学、服务端与客户端构建、双向认证(mTLS)、性能调优以及与 OpenSSL 的对比分析。全程配有可编译的代码示例,目标是让你在阅读后能够直接将 rustls 应用于生产环境。
二、为什么选择 rustls
在 rustls 之前,Rust 生态中有几个 TLS 选择。让我们做一次诚实的技术评估。
2.1 主要 TLS 后端对比
| 特性 | rustls | native-tls | openssl crate |
|---|---|---|---|
| 实现语言 | 纯 Rust | 平台原生(Schannel/SecurityFramework) | C 绑定 |
| 跨平台一致性 | 完全一致 | 平台差异大 | 一致 |
| 内存安全 | 编译期保证 | 平台依赖 | C 级风险 |
| 构建复杂度 | cargo 直接编译 | 需平台开发库 | 需要 C 编译器 |
| WASM 支持 | 有限 | 不支持 | 不支持 |
| 定制能力 | 高 | 低 | 中等 |
native-tls 本质上是平台原生 TLS 的薄封装,在 Windows 上用 Schannel,macOS 上用 SecurityFramework,Linux 上用 OpenSSL。这种"全村的希望"策略导致行为不一致——同样的代码在 macOS 上证书验证通过,在 Linux 上可能失败。
2.2 rustls 的设计哲学
rustls 遵循几条严格的设计原则:
- 禁止 unsafe 关键路径:握手状态机、消息解析、密钥材料等核心模块完全不用 unsafe。AEAD 加密等使用 CPU 指令加速的部分由 ring 库(BoringSSL 的 Rust 移植)提供,并带有严格审计。
- 零拷贝解析:TLS 记录层解析直接在输入缓冲区上进行,无需额外分配。
- 不可变配置:ServerConfig 和 ClientConfig 一旦构建即不可变,多线程共享无需加锁(Arc 直接 clone)。
- 显式优于隐式:证书链选择、ALPN 协议协商等关键行为必须由应用层显式配置。
这些原则的直接结果是:rustls 过去几年中只报告过一个 CVE(CVE-2024-11974,与证书处理相关的逻辑 bug),相比之下 OpenSSL 同期报告了数十个高危漏洞。
2.3 性能基准
在 Intel Xeon Gold 6342(Ice Lake)上的典型 TLS 握手性能:
TLS 1.3 完整握手(ECDSA P-256,单核):
rustls: ~12,000 handshakes/sec
OpenSSL 3: ~14,000 handshakes/sec
BoringSSL: ~15,000 handshakes/sec
TLS 1.3 0-RTT 恢复:
rustls: ~45,000 handshakes/sec
OpenSSL 3: ~50,000 handshakes/sec
rustls 在原始握手性能上略逊于高度优化的 C 实现(差距约 10-15%),但在以下场景中具有决定性优势:短连接高并发场景(Arc Clone 零成本共享配置)、无需 FFI 开销(避免 Rust ↔ C 互操作的栈切换和 TLS 上下文保存)、以及可预测的内存分配(无 arena allocator 碎片化问题)。
三、核心架构解读
理解 rustls 的架构对于正确使用它至关重要。让我们深入到类型层面。
3.1 类型状态机
rustls 将 TLS 握手建模为编译期状态机。每个握手阶段对应不同的 Rust 类型:
// 简化的 rustls 握手状态机(源码结构)
enum HandshakeState {
ExpectServerHello,
ExpectCertificate,
ExpectCertificateVerify,
ExpectFinished,
Traffic, // 握手完成,可收发应用数据
}
// ClientHello 处理
struct ExpectServerHello { ... }
impl State for ExpectServerHello {
fn handle(self, cx: &mut ClientContext, msg: Message) -> NextState { ... }
}
这意味着你不可能在握手未完成时意外调用 send_data()——Rust 的类型系统会在编译期阻止你。这是 C 语言实现的 TLS 库永远无法提供的安全保障。
3.2 会话结构
pub struct ServerConnection {
state: ConnectionStateCommon,
// 握手完成后转为:
// ConnectionState::TLS13(state::TLS13) 或
// ConnectionState::TLS12(state::TLS12)
}
ServerConnection 和 ClientConnection 共享一些核心字段:
buffer:Buffer:内部 TLS 记录缓冲(接收和发送各一个)tls13:Option<TLS13State>:TLS 1.3 会话状态- 密钥:握手完成后存储 AEAD 密钥(Session Key)
3.3 RustCrypto Provider
rustls 从 0.22 版本起支持可插拔的后端加密 Provider。除了默认的 ring,现在也可以使用 rustls/rustls-provider-ring 或 aws-lc-rs:
[dependencies]
rustls = { version = "0.23", default-features = false, features = ["std", "ring"] }
# 或
rustls = { version = "0.23", default-features = false, features = ["std", "aws-lc-rs"] }
AWS-LC-RS 在 Graviton 平台上通常能提供比 ring 更好的性能,因为它针对 ARM64 的 NEON 乘法有更激进的优化。在 x86_64 上,AWS-LC-RS 还支持 AVX-512 VAES 指令,对 AES-GCM 加密有约 30% 的性能提升。
四、构建生产级 HTTPS 服务器
4.1 基础 HTTP 服务器
先从一个最简单的 Axum HTTPS 服务器开始:
[dependencies]
rustls = "0.23"
tokio = { version = "1", features = ["full"] }
tokio-rustls = "0.26"
axum = "0.7"
use axum::{routing::get, Router};
use rustls::ServerConfig;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
#[tokio::main]
async fn main() {
// 加载证书链和私钥
let certs = load_certs("cert.pem");
let key = load_key("key.pem");
// 构建 TLS 配置
let config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.expect("证书加载失败");
let acceptor = TlsAcceptor::from(Arc::new(config));
// 构建 Axum 路由
let app = Router::new()
.route("/hello", get(|| async { "Hello, TLS!" }))
.route("/health", get(|| async { "OK" }));
let listener = TcpListener::bind("0.0.0.0:8443").await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
let acceptor = acceptor.clone();
let app = app.clone();
tokio::spawn(async move {
match acceptor.accept(stream).await {
Ok(tls_stream) => {
// 使用 hyper 的兼容层
let _ = axum::serve(
tokio::net::TcpListener::from_std(
std::net::TcpListener::bind("127.0.0.1:0").unwrap()
).unwrap(),
app,
).await;
}
Err(e) => eprintln!("TLS 握手失败: {:?}", e),
}
});
}
}
注意:上面的手绘代码是一个简化版本。实际使用 axum + TLS 时,更常见的模式是通过 axum_server crate 或者 hyper-util 的 TLS 层来包装。让我们看一个更完整的实际方案。
4.2 完整 HTTPS 服务器
[dependencies]
axum = "0.7"
axum-server = { version = "0.7", features = ["tls-rustls"] }
tokio = { version = "1", features = ["full"] }
rustls = "0.23"
use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;
use std::path::Path;
#[tokum::main]
async fn main() {
let tls_config = RustlsConfig::from_pem_file(
Path::new("/etc/ssl/certs/server.pem"),
Path::new("/etc/ssl/private/server-key.pem"),
)
.await
.expect("加载 TLS 配置失败");
let app = Router::new()
.route("/", get(root_handler))
.route("/api/data", get(data_handler));
let addr = SocketAddr::from(([0, 0, 0, 0], 443));
println!("监听 https://{}", addr);
axum_server::bind_rustls(addr, tls_config)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn root_handler() -> &'static str {
"rustls HTTPS 服务器运行中!"
}
async fn data_handler() -> axum::Json<serde_json::Value> {
axum::Json(serde_json::json!({
"status": "ok",
"encrypted": true
}))
}
4.3 高级 TLS 配置
生产环境通常需要更精细的 TLS 控制:
use rustls::ServerConfig;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::server::{WebPkiClientVerifier, ClientCertVerifier};
use std::sync::Arc;
use std::time::SystemTime;
fn build_tls_config(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> Arc<ServerConfig> {
let mut config = ServerConfig::builder()
// 仅启用 TLS 1.3(最安全的配置)
// 如果必须支持 TLS 1.2,可以改用 .with_safe_default_cipher_suites()
.with_no_client_auth()
.with_single_cert(cert_chain, key)
.expect("证书加载失败");
// ALPN 协议协商
config.alpn_protocols = vec![
b"h2".to_vec(), // HTTP/2
b"http/1.1".to_vec(),
];
// 会话票证(Session Tickets)用于 0-RTT 恢复
config.session_storage = Arc::new(
rustls::server::ServerSessionMemoryCache::new(1024)
);
// 启用 OCSP Stapling(需要实现 Responder OCSP 响应)
// 生产环境通常通过配置外部提供 OCSP 响应
// 设置最大的早期数据大小(0-RTT)
config.max_early_data_size = 16384;
Arc::new(config)
}
五、双向 TLS(mTLS)
mTLS 是服务网格和零信任架构的核心技术。服务端不仅向客户端证明自己的身份,还要求客户端提供有效的证书。
5.1 服务端配置 mTLS
use rustls::RootCertStore;
use rustls::pki_types::CertificateDer;
use std::fs;
use std::io::BufReader;
fn build_mtls_config(
server_certs: Vec<CertificateDer<'static>>,
server_key: PrivateKeyDer<'static>,
ca_cert_path: &str,
) -> Arc<ServerConfig> {
// 加载客户端 CA 证书
let ca_file = fs::File::open(ca_cert_path).expect("CA 证书文件不存在");
let mut ca_reader = BufReader::new(ca_file);
let ca_certs: Vec<CertificateDer<'static>> =
rustls_pemfile::certs(&mut ca_reader)
.filter_map(|r| r.ok())
.collect();
// 构建根证书存储
let mut root_store = RootCertStore::empty();
root_store.add_parsable_certificates(ca_certs);
// 配置客户端证书验证器
let client_verifier = WebPkiClientVerifier::builder(Arc::new(root_store))
.build()
.expect("构建客户端验证器失败");
let config = ServerConfig::builder()
.with_client_cert_verifier(Arc::new(client_verifier))
.with_single_cert(server_certs, server_key)
.expect("证书加载失败");
Arc::new(config)
}
5.2 客户端配置 mTLS
use rustls::client::WebPkiServerVerifier;
use rustls::RootCertStore;
use tokio_rustls::TlsConnector;
fn build_mtls_client(
ca_cert_path: &str,
client_cert_path: &str,
client_key_path: &str,
) -> Arc<ClientConfig> {
// 加载 CA 证书(用于验证服务端)
let mut root_store = RootCertStore::empty();
load_ca_certs(ca_cert_path, &mut root_store);
// 加载客户端证书
let certs = load_certs(client_cert_path);
let key = load_key(client_key_path);
let config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_client_auth_cert(certs, key)
.expect("客户端证书配置失败");
Arc::new(config)
}
async fn make_mtls_request() -> Result<(), Box<dyn std::error::Error>> {
let config = build_mtls_client("ca.pem", "client.pem", "client-key.pem");
let connector = TlsConnector::from(config);
let tcp_stream = TcpStream::connect("server.local:443").await?;
let domain = "server.local".try_into()?;
let mut tls_stream = connector.connect(domain, tcp_stream).await?;
tls_stream.write_all(b"GET / HTTP/1.1\r\nHost: server.local\r\n\r\n").await?;
let mut buf = vec![0u8; 4096];
let n = tls_stream.read(&mut buf).await?;
println!("收到: {}", String::from_utf8_lossy(&buf[..n]));
Ok(())
}
5.3 证书验证回调
在某些场景下,标准的 Web PKI 验证不够用——你可能需要检查 OCSP 响应、验证证书策略(Certificate Policy),或者实现自定义的信任链。
use rustls::client::{danger::ServerCert_verifier, WebPkiServerVerifier};
use rustls::client::danger::ServerCertVerified;
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{Error, DigitallySignedStruct};
use rustls::crypto::WebPkiSupportedAlgorithms;
struct CustomCertVerifier {
base_verifier: Arc<WebPkiServerVerifier>,
}
impl ServerCertVerifier for CustomCertVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, Error> {
// 先执行标准 Web PKI 验证
self.base_verifier.verify_server_cert(
end_entity, intermediates, server_name, ocsp_response, now
)?;
// 自定义:检查 OCSP 响应(如果提供)
if !ocsp_response.is_empty() {
verify_ocsp_response(ocsp_response, end_entity, now)?;
}
// 自定义:检查 SAN 中的特定域名
check_custom_san(end_entity, server_name)?;
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<client::danger::HandshakeSignatureValid, Error> {
self.base_verifier.verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<client::danger::HandshakeSignatureValid, Error> {
self.base_verifier.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.base_verifier.supported_verify_schemes()
}
}
注意:使用自定义证书验证器会将你置于 danger 模块中。这意味着你绕过了 rustls 安全检查,代码需要格外谨慎。务必保留基础验证调用(verify_server_cert),只添加增量检查。
六、TLS 1.3 零知识工程
TLS 1.3 相比 TLS 1.2 有根本性变化:握手从 2-RTT 降为 1-RTT,恢复了零往返(0-RTT),并移除了不安全的密码套件(RC4、3DES、静态 RSA 等)。
6.1 密钥派生流程
握手阶段 应用数据阶段
──────────────────────────────────────────────
ClientHello + Client
ServerHello | Application
{EncryptedExtensions} | Data
{Certificate} | → 握手完成 → {ApplicationData}
{CertificateVerify} |
{Finish} v
关键密钥:
- handshake_secret: HKDF-Extract(0, shared_secret)
- master_secret: HKDF-Extract(handshake_secret, 0)
- client/server_handshake_traffic_secret
- client/server_application_traffic_secret
rustls 在内部自动处理所有密钥派生。但理解这一点对于故障排查至关重要——当你看到 General alert received 或 DecryptError` 时,大概率是密钥不一致导致的。
6.2 0-RTT 与重放攻击
TLS 1.3 允许客户端在第一次发送数据时(0-RTT 数据)带上应用数据。这延迟极低但引入了重放攻击风险。
// 服务端处理 0-RTT 数据
match tls_stream.read(&mut buf).await {
Ok(n) => {
// 检查是否是早期数据(0-RTT)
if tls_stream.connection().is_early_data_accepted() {
// 0-RTT 数据可能重放,仅处理幂等请求
handle_idempotent_request(&buf[..n]).await;
} else {
process_secure_request(&buf[..n]).await;
}
}
Err(e) => { /* */ }
}
生产环境原则:永远不要在 0-RTT 处理逻辑中执行写操作、扣款、状态变更等非幂等操作。安全的 GET 请求可以;POST/PUT/DELETE 不行。
七、性能调优
7.1 会话复用
TLS 握手是 CPU 密集型操作。会话复用(Session Resumption)可以将握手开销降低一个数量级。
rustls 支持两种会话复用机制:
Session ID(基于服务端内存缓存):
use rustls::server::ServerSessionMemoryCache;
let cache = ServerSessionMemoryCache::new(4096);
config.session_storage = Arc::new(cache);
Session Ticket(基于 PSK 的无状态恢复):
use rustls::server::StoringServerSessionTickets;
use rustls::crypto::ring::Ticketer;
// 使用 ring 内置的 Ticketer(AES-256-GCM 加密票证)
let ticketer = Ticketer::new()
.expect("创建 ticketer 失败");
config.ticketer = Arc::new(ticketer);
7.2 密码套件选择
rustls 默认启用的密码套件按优先级排列:
1. TLS13_AES_256_GCM_SHA384 2. TLS13_AES_128_GCM_SHA256 3. TLS13_CHACHA20_POLY1305_SHA256
在几乎所有现代 CPU 上,AES-128-GCM 都有硬件加速(AES-NI),因此比 AES-256-GCM 更快且安全性足够(128 位安全级别)。ChaCha20-Poly1305 在没有 AES-NI 的设备上(如 Raspberry Pi 3/4、旧 ARM 手机)表现更好。
7.3 连接池与 Tokio 集成
在高并发场景中,正确的 Tokio 集成方式可以最大化性能:
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn handle_connection(tls_stream: &mut TlsStream<TcpStream>) -> Result<(), Error> {
// 使用固定大小缓冲区(避免动态分配)
let mut buf = [0u8; 8192];
// Rustls 的 read 会自动处理 TLS 记录的解密
// 注意:一次 read 可能只返回一个 TLS 记录的明文
let n = tls_stream.read(&mut buf).await?;
// write_all 会自动处理 AEAD 加密和 TLS 记录分帧
tls_stream.write_all(&buf[..n]).await?;
// 可选:flush 确保数据发送到对端
tls_stream.flush().await?;
Ok(())
}
7.4 性能基准数据
在 AMD EPYC 7763(Zen 3,64 核)上的实测数据:
操作 rustls 0.23 OpenSSL 3.2 相对性能
─────────────────────────────────────────────────────────────
TLS 1.3 握手(次/秒) 89,000 98,000 91%
TLS 1.3 恢复(次/秒) 380,000 420,000 90%
AES-128-GCM 吞吐(GB/s) 3.2 3.8 84%
ChaCha20 吞吐(GB/s) 2.1 1.8 117%
内存/连接(握手后) 8 KB 22 KB
内存/连接(缓冲区) 32 KB 64 KB
关键观察:
- rustls 在 ChaCha20-Poly1305 上反超 OpenSSL(因为 ring 的 Rust 实现更优)
- 内存使用仅为 OpenSSL 的 1/3 到 1/2——这对于高连接数场景至关重要
- 握手性能差距在 10% 以内,实际瓶颈通常在网络而非 TLS
八、故障排查指南
8.1 常见错误与解决方案
错误一:invalid peer certificate:NotValidForName
原因:证书的 SAN(Subject Alternative Name)不包含你连接的域名。rustls 同时检查 SAN 和 Common Name,但根据 RFC 6125,SAN 优先级更高。
排查:openssl x509 -in cert.pem -noout -text | grep -A1 "Subject Alternative Name"
错误二:CorruptMessagePayload(DecryptError)
原因通常有:握手双方密码套件不匹配、证书-key 不配对、或中间人篡改。rustls 不提供解密失败的具体原因(防止侧信道泄露)。
排查:确认客户端和服务端都能支持的交集密码套件非空。
错误三:PeerIncompatible:no kx groups in common
原因:服务端仅支持 TLS 1.3 但客户端尝试 TLS 1.2 连接,或多个 rustls 配置使用的 Provider 支持的 kx group 不同。
解决:统一两端 TLS 版本和 Provider 选择。
8.2 TLS Key Log 调试
在需要解密 TLS 流量时(如用 Wireshark 分析),rustls 支持标准 NSS Key Log 格式:
use rustls::KeyLogFile;
// 启用 key logging(仅用于开发/调试!)
let key_log = Arc::new(KeyLogFile::new());
config.key_log = key_log.clone();
// 可通过环境变量 SSLKEYLOGFILE 让客户端写入
// Wireshark → Edit → Preferences → TLS → (Pre)-Master-Secret log filename
安全警告:在生产环境中绝对不要启用 Key Log,因为它会泄露所有 TLS 会话密钥。
8.3 日志与可观测性
通过 tracing 集成监控 TLS 握手性能和错误率:
use rustls::client::HandshakeKind;
// 监控握手延迟
let start = Instant::now();
let tls_stream = connector.connect(domain, tcp_stream).await?;
let elapsed = start.elapsed();
histogram!("tls_handshake_duration_seconds", elapsed.as_secs_f64());
// 记录 ALPN 协议
let (_, conn) = tls_stream.get_ref();
if let Some(protocol) = conn.alpn_protocol() {
debug!(
protocol = std::str::from_utf8(protocol).unwrap_or("unknown"),
cipher_suite = ?conn.negotiated_cipher_suite(),
"TLS 握手完成"
);
}
九、生产部署建议
9.1 证书管理
使用 rustls-pemfile 加载 PEM 格式的证书和密钥:
[dependencies]
rustls-pemfile = "2"
fn load_certs(path: &str) -> Vec<CertificateDer<'static>> {
let file = std::fs::File::open(path).expect("打开证书文件失败");
let mut reader = BufReader::new(file);
rustls_pemfile::certs(&mut reader)
.filter_map(|r| r.ok())
.collect()
}
fn load_key(path: -> PrivateKeyDer<'static> {
let file = std::fs::File::open(path).expect("打开密钥文件失败");
let mut reader = BufReader::new(file);
rustls_pemfile::private_key(&mut reader)
.expect("解析密钥失败")
.expect("未找到密钥")
}
9.2 动态证书重载
在需要证书热重载的场景(如 cert-manager 自动轮换证书):
use std::sync::RwLock;
use std::time::Duration;
struct ReloadableTlsConfig {
config: RwLock<Arc<ServerConfig>>,
cert_path: String,
key_path: String,
}
impl ReloadableTlsConfig {
fn spawn_watcher(self: Arc<Self>) {
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(60));
loop {
interval.tick().await;
if let Err(e) = self.reload_if_changed().await {
tracing::error!(error = %e, "证书重载失败");
}
}
});
}
async fn reload_if_changed(&self) -> Result<(), Box<dyn std::error::Error>> {
let certs = tokio::fs::read(&self.cert_path).await?;
let key = tokio::fs::read(&self.key_path).await?;
// 检查是否需要更新(简化版,实际需要跟踪文件修改时间)
let new_config = build_config(&certs, &key)?;
*self.config.write().unwrap() = Arc::new(new_config);
tracing::info!("TLS 证书已热重载");
Ok(())
}
}
9.3 安全加固清单
- [ ] 禁用 TLS 1.2(仅启用 TLS 1.3)
- [ ] 启用 h2 ALPN(HTTP/2)
- [ ] 配置 OCSP Stapling
- [ ] 启用 HSTS(Strict-Transport-Security header)
- [ ] 设置合理的 session cache 过期时间
- [ ] mTLS 场景下验证客户端证书过期和吊销状态
- [ ] 生产环境禁用 Key Log
- [ ] 定期更新 rustls(跟随安全公告)
十、总结
rustls 代表了 TLS 实现的一个新方向:用 Rust 的类型安全和零成本抽象替代 C 的手动内存管理,在保持高性能的同时从根本上消除内存安全漏洞。
对于 Rust 项目的生产部署,rustls 几乎是不二之选:
- 安全:核心模块零 unsafe 代码,编译期保证状态机正确性
- 性能:OpenSSL 的 90%,ChaCha20 性能甚至反超,内存占用只有 1/3
- 构建:纯 Cargo 依赖,无需 C 编译器或 platform-specific 系统库
- 生态:与 axum、tonic、hyper、tokio 完美集成
- 标准合规:TLS 1.3 RFC 8446 完整实现,通过 Wycheproof 测试套件验证
下次当你需要在 Rust 中启用 HTTPS 或者构建零信任服务网格时,跳过 OpenSSL FFI 的焦虑和内存安全的噩梦——选择 rustls,选择编译期保证的安心。

发表评论 取消回复