当前位置: 首页 > 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;  }  }  }  }
});

相关文章:

  • 深度学习笔记27-LSTM实现糖尿病探索与预测(Pytorch)
  • 【大数据】java API 进行集群间distCP 报错unresolvedAddressException
  • AWS EC2使用SSM会话管理器连接
  • HarmonyOS 6 + 盘古大模型5.5
  • day30 导包
  • GDI绘制
  • Unity3d中使用Mirror进行自定义消息通信
  • 水晶杂谈3:生物群系大家族,噪声函数塑地形
  • 基于k2-icefall实践Matcha-TTS中文模型训练
  • 解决Docker网络与虚拟机桥接冲突的实践指南
  • VC++ 服务守护qt用户级UI进程
  • QEMU学习之路(10)— RISCV64 virt 使用Ubuntu启动
  • c++set和pair的使用
  • 小白的进阶之路系列之十六----人工智能从初步到精通pytorch综合运用的讲解第九部分
  • docker mysql启动后时间慢8小时问题
  • 24. 开发者常用工具:抓包,弱网模拟,元素检查
  • Tkinter快速入门指南
  • DataWhale-零基础络网爬虫技术(二er数据的解析与提取)
  • 粗浅理解:为什么左旋右旋的组合反而收旋转矩阵影响
  • ajax中get和post的区别
  • 用dw做静态网站的步骤/电子商务seo
  • 永久免费自助建站源代码/北京seo案例
  • 移动电商网站开发/快速提高排名
  • 网站对固定ip转向怎么做/搜狗站长
  • 书画艺术网站建设概况/线上推广哪个平台最好
  • 王爷好大受不了txt下载/站内seo的技巧