当前位置: 首页 > news >正文

Rust使用tokio(二)HTTPS相关

reqwest

在tokio中的HTTP客户端,可以使用reqwest。

reqwest的feature中,有native-tls,可以直接支持证书校验。

在Cargo.toml中加入依赖:

[dependencies]  
tokio = { version = "1.36.0", features = ["full"] }    
reqwest = { version = "0.11", features = ["native-tls"] }

即可以在初始化HTTP Client的时候,加入证书。方法是把证书读入一个Identity中,之后使用Client::Builder的identity函数。

如:

fn client_new(path: &str) -> Client {let p12_data = fs::read(path).expect("Failed to read certificate file");  let identity = Identity::from_pkcs12_der(&p12_data, "")  .expect("Failed to create identity from PKCS12");  let client_builder = Client::builder()  .identity(identity)  client_builder.build().expect("Failed to create HTTP client")
}

另外,Client::Builder还有一个danger_accept_invalid_certs函数,可以控制在服务端证书有问题的情况下,是否接受。

如以下代码,就忽略了服务端证书错误:

let client = client_builder  .identity(identity)  .danger_accept_invalid_certs(true).build().expect("Failed to create HTTP client")

使用Client的get方法:

async fn get_request(client:Client, url: &str) -> Result<Response, Box<dyn std::error::Error + Send + Sync>> {  let response = client.get(url).send().await?;  if !response.status().is_success() {  return Err(format!("HTTP get failed with status: {}", response.status()).into());  }Ok(response)
}```使用Client的post方法:
```rust
async fn post_request(client: &Client,url: &str,  data: serde_json::Value,  
) -> Result<Response, Box<dyn std::error::Error + Send + Sync>> {    let response = client.post(url).json(&data).send().await?;  if !response.status().is_success() {  return Err(format!("HTTP post failed with status: {}", response.status()).into());  }  Ok(response)
}

hyper作为Web服务端

hyper是Rust中开发Web服务的crate。

创建一个SocketAddr,之后使用hyper::Server的bind方法绑定到地址上,之后即可以在之上挂接一个回调函数来处理Web请求。

如:

async fn start_http(port: u16) {let addr = SocketAddr::from(([0, 0, 0, 0], port));  let make_svc = make_service_fn(|conn: &AddrStream| {  let client_addr = conn.remote_addr();  async move { Ok::<_, Infallible>(service_fn(move |req| handle_request(req, client_addr))) }  
});let server = Server::bind(&addr).serve(make_svc);server.await.expect("Failed to start HTTP server");
}

openssl工具

我们可以使用OpenSSL套件中的openssl命令,生成测试证书。

如:

# 创建 CA  
openssl req -x509 -newkey rsa:4096 -keyout ca_key.pem -out ca_cert.pem -days 365 -nodes -subj "/CN=MyCA"  # 创建服务器证书  
openssl req -newkey rsa:4096 -keyout server_key.pem -out server_csr.pem -nodes -subj "/CN=localhost"  
openssl x509 -req -in server_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out server_cert.pem  
-days 365  # 创建客户端证书  
openssl req -newkey rsa:4096 -keyout client_key.pem -out client_csr.pem -nodes -subj "/CN=TestClient"  
openssl x509 -req -in client_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out client_cert.pem  
-days 365

还可以把证书、key以及ca导出到一个p12格式的证书文件中:

# 服务端P12证书
openssl pkcs12 -export -in server_cert.pem -inkey server_key.pem -CAfile ca_cert.pem -out server.p12# 客户端测试证书
openssl pkcs12 -export -in client_cert.pem -inkey client_key.pem -CAfile ca_cert.pem -out client.p12

Rust的openssl::Pkcs12

Rust中也有openssl的crate,可以使用openssl,解析P12格式的文件,取出其中的证书、key以及ca:

let p12_data = std::fs::read("cert.p12").expect("Failed to read P12 certificate");  
let p12 = Pkcs12::from_der(&p12_data).expect("Failed to load P12 certificate");  
let p12_parsed = p12.parse2("").expect("Failed to parse P12 certificate");  let cert_chain = if let Some(cert) = p12_parsed.cert {  vec![Certificate(  cert.to_der().expect("Failed to serialize certificate"),  )]  
} else {  panic!("No certificate found in P12");  
};  let private_key = if let Some(pkey) = p12_parsed.pkey {  PrivateKey(  pkey.private_key_to_der()  .expect("Failed to load private key"),  )  
} else {  panic!("No private key found in P12");  
};  let mut roots = RootCertStore::empty();  
if let Some(ca) = p12_parsed.ca {  for cert in ca {  roots.add(&Certificate(  cert.to_der().expect("Failed to serialize certificate"),  ))  .expect("Failed to add certificate");  }  
}

hyper-rustls

前面提到的hyper,没有支持HTTPS。我们可以使用hyper-rustls,来支持HTTPS的服务。

首先,使用Pcs12导出的证书、key以及ca链,生成ServerConfig:

let client_auth = AllowAnyAuthenticatedClient::new(roots);  let server_config = ServerConfig::builder()  .with_safe_defaults()  .with_client_cert_verifier(Arc::new(client_auth))  .with_single_cert(cert_chain, private_key)  .expect("Failed to set certificate")

使hyper应用Rustls的ServerConfig:

let addr = SocketAddr::from(([0, 0, 0, 0], port));  
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");  
let acceptor = TlsAcceptor::from(Arc::new(server_config));  
let make_svc = make_service_fn(|conn: &TlsStream<TcpStream>| {  let client_addr = conn.get_ref().0.peer_addr().unwrap();  async move { Ok::<_, Infallible>(service_fn(move |req| handle_request(req, client_addr))) }  
});let incoming_tls_stream = futures_util::stream::unfold(listener, move |listener| {  let acceptor = acceptor.clone();  async move {  match listener.accept().await {  Ok((stream, _)) => {  let acceptor = acceptor.clone();  match acceptor.accept(stream).await {  Ok(tls_stream) => Some((Ok(tls_stream), listener)),  Err(e) => Some((  Err(std::io::Error::new(std::io::ErrorKind::Other, e)),  listener,  )),  }  }  Err(e) => Some((Err(e), listener)),  }  }  
});let server = Server::builder(from_stream(incoming_tls_stream)).serve(make_svc);  
info!("HTTPS running on https://{}", addr);  
server.await.expect("Failed to run HTTPS server");

主要的逻辑,就是使用hyper的Server::builder从TLS的Stream,来执行serve函数,挂接make_svc过程。

上面的incoming_tls_stream,在失败的情况下会退出。可以把上面的过程,包在一个循环中,出错就只是打印错误,之后继续等待连接。

如:

let listener = TcpListener::bind(&addr).await.expect("Failed to bind");  
let incoming_tls_stream = futures_util::stream::unfold(listener, move |listener| {  let acceptor = acceptor.clone();  async move {  loop {  match listener.accept().await {  Ok((stream, _)) => match acceptor.accept(stream).await {  Ok(tls_stream) => {  return Some((  Ok::<_, Box<dyn std::error::Error + Send + Sync>>(tls_stream),  listener,  ));  }// TLS层接受连接失败,则报错,之后继续循环 Err(e) => {  warn!("tls accept error: {}", e);  continue;  }  },// TCP层接受连接失败,也报错,之后继续循环Err(e) => {  warn!("tcp accept error: {}", e);  continue;  }  }  }  }
});
http://www.lqws.cn/news/455959.html

相关文章:

  • day03-微服务01
  • 商业秘密被公开后的损失计算:从法律规定到司法实践的深度解析
  • 举办骑行展览的城市需要具备哪些关键条件
  • 【vdbench】vdbench配置文件参数含义
  • 【工具使用-VScode】VScode如何设置空格和tab键显示
  • .Net Framework 4/C# 数据访问技术(ADO.NET)
  • 一些实用的chrome扩展0x02
  • C#上位机实现报警语音播报
  • VS Code快捷键大全
  • Python OpenCV 模板匹配的一些应用场景和方法思考,浅析KAZE特征匹配对比
  • Windows 10 防火墙 0x8007045b 打不开
  • 数据库 ACID 特性之 -- 原子性 Atomicity
  • 软件架构的发展历程——从早期的单体架构到如今的云原生与智能架构
  • shell学习笔记
  • 领码 SPARK 融合平台赋能工程建设行业物资管理革新——数智赋能,重塑中国模式新范式
  • LeetCode 275.H指数 II
  • RPC - 服务注册与发现模块
  • 【C++】哈希表的实现(开放定址法)
  • 电磁场数值仿真技术及天线设计与应用
  • 协程(C++)
  • 设计模式精讲 Day 9:装饰器模式(Decorator Pattern)
  • HTTP与HTTPS深度解析:从明文传输到安全通信的演进之路
  • flask通过表单自动产生get请求的参数、form表单实现POST请求的自动提交
  • 轻量级web开发框架之Flask web开发框架学习:get请求数据的发送
  • HCIP-数据通信基础
  • FFmpeg 超级详细安装与配置教程(Windows 系统)
  • Java八股文——消息队列「场景篇」
  • OSI网络通信模型详解
  • linux操作系统---小白玩转shell脚本
  • Clang Static Analyzer 使用教程:本地 + CMake + GitHub Actions 自动静态分析实战