Rust抓包http/https

[复制链接]
发表于 2025-12-29 17:17:27 | 显示全部楼层 |阅读模式
相干库的安装

使用vcpkg安装openssl库
  1. vcpkg install openssl:x64-windows
复制代码
并设置openssl库位置的环境变量
  1. $Env:OPENSSL_DIR="D:/vcpkg/packages/openssl_x64-windows/"
复制代码
安装openssl软件,由于必要使用openssl天生自署名证书
Cargo依赖

  1. [dependencies]
  2. anyhow = "1.0.81"
  3. bytes = "1.6.0"
  4. futures = "0.3.30"
  5. http-body-util = "0.1.1"
  6. hyper = { version = "1.2.0", features = ["full"] }
  7. hyper-util = { version = "0.1.3", features = ["full"] }
  8. openssl = "0.10.64"
  9. tokio = { version = "1.37.0", features = ["full"] }
  10. tokio-openssl = "0.6.4"
  11. futures-util = "0.3.30"
  12. tokio-util = "0.7.10"
复制代码
使用OpenSSL天生自署名证书

  1. 1、生成RSA私钥文件
  2. openssl genrsa -out proxylea_private.key 2048
  3. 2、创建证书签名请求文件
  4. openssl req -new -key proxylea_private.key -out server.csr
  5. 3、使用私钥为证书签名(自签名)
  6. openssl x509 -req -days 3650 -in server.csr -signkey  proxylea_private.key -out proxylea_cert.crt
  7. 4、将proxylea_cert.crt证书导入电脑受信任根证书目录下
复制代码
动态为相干网站颁发证书

  1. ///创建一个证书颁发模块 crt.mod
  2. use std::{fs::File, io::Read};
  3. use openssl::asn1::Asn1Integer;
  4. use openssl::bn::BigNum;
  5. use openssl::rand;
  6. use openssl::pkey::{Private};
  7. use openssl::rsa::Rsa;
  8. use openssl::{
  9.     pkey::PKey, x509::extension::SubjectAlternativeName, x509::X509,
  10. };
  11. // 以下函数用于加载根证书和私钥
  12. fn load_root_cert_and_key(
  13.     root_cert_path: &str,
  14.     root_key_path: &str,
  15. ) -> (X509, PKey<Private>) {
  16.     let ca_file = File::open(root_cert_path).unwrap();
  17.     let bytes = ca_file.bytes().map(|b| b.unwrap()).collect::<Vec<u8>>();
  18.     let ca = X509::from_pem(&bytes).unwrap();
  19.     let pkey_file = File::open(root_key_path).unwrap();
  20.     let bytes = pkey_file.bytes().map(|b| b.unwrap()).collect::<Vec<u8>>();
  21.     let pkey = PKey::private_key_from_pem(&bytes).unwrap();
  22.     // 实现加载根证书和私钥的逻辑
  23.     (ca, pkey)
  24. }
  25. // 客户端动态颁发证书
  26. fn client_cert_signing(
  27.     _root_cert_path: &str,
  28.     _root_key_path: &str,
  29.     host:&str
  30. ) -> Result<(X509, PKey<Private>), openssl::error::Error> {
  31.     // 加载根证书和私钥
  32.     let (root_cert, root_key) = load_root_cert_and_key(root_cert_path, root_key_path);
  33.    
  34.     // 生成密钥对
  35.     let rsa = Rsa::generate(2048).unwrap();
  36.     let private_key = PKey::from_rsa(rsa).unwrap();
  37.     let public_key_pem = private_key.public_key_to_pem().unwrap();
  38.     let public_key = PKey::public_key_from_pem(&public_key_pem).unwrap();
  39.     // 设置证书有效期
  40.     // let not_before = openssl::asn1::Asn1Time::days_from_now(0).unwrap();
  41.     //let not_after = openssl::asn1::Asn1Time::days_from_now(365).unwrap();
  42.     // 颁发证书
  43.     let mut cert = X509::builder().unwrap();
  44.     cert.set_version(2).unwrap();
  45.     let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap();
  46.     x509_name.append_entry_by_text("C", "ZH").unwrap();
  47.     x509_name.append_entry_by_text("ST", "SC").unwrap();
  48.     x509_name.append_entry_by_text("L", "YC").unwrap();
  49.     x509_name.append_entry_by_text("O", "YC").unwrap();
  50.     x509_name.append_entry_by_text("OU", "YC").unwrap();
  51.     x509_name
  52.         .append_entry_by_text("CN", host)
  53.         .unwrap();
  54.     let x509_name = x509_name.build();
  55.     cert.set_subject_name(&x509_name).unwrap();
  56.     cert.set_issuer_name(&root_cert.subject_name()).unwrap();
  57.     let mut serial_number = [0; 16];
  58.     rand::rand_bytes(&mut serial_number).unwrap();
  59.     let serial_number = BigNum::from_slice(&serial_number).unwrap();
  60.     let serial_number = Asn1Integer::from_bn(&serial_number).unwrap();
  61.     cert.set_serial_number(&serial_number).unwrap();
  62.     cert.set_not_before(root_cert.not_before()).unwrap();
  63.     cert.set_not_after(root_cert.not_after()).unwrap();
  64.     cert.set_pubkey(&public_key).unwrap();
  65.     let alternative_name = SubjectAlternativeName::new()
  66.             .dns(host)
  67.             .build(&cert.x509v3_context(Some(&root_cert),None)).unwrap();
  68.         cert.append_extension(alternative_name).unwrap();
  69.     cert.sign(&root_key, openssl::hash::MessageDigest::sha256())
  70.         .unwrap();
  71.    
  72.     Ok((cert.build(), private_key))
  73. }
  74. pub fn get_crt_key(host:&str) ->(X509, PKey<Private>){
  75.         //导入自签名CA证书,并将crt格式的CA证书导入浏览器
  76.         //导入CA证书的私钥
  77.     client_cert_signing("./crt/proxylea_cert.crt",
  78.                         "./crt/proxylea_private.key", host).unwrap()
  79. }
复制代码
效果如下,可以望见www.baidu.com网站的证书由自署名证书颁发。

使用hyper构建HTTP署理服务器

构建本地署理服务器

  1. #[tokio::main]
  2. pub async fn main() -> Result<()> {
  3.     // This address is localhost
  4.     let addr: SocketAddr = "127.0.0.1:7890".parse().unwrap();
  5.     // Bind to the port and listen for incoming TCP connections
  6.     let listener = TcpListener::bind(addr).await?;
  7.     println!("Listening on http://{}", addr);
  8.     loop {
  9.         let (tcp_stream, addr) = listener.accept().await?;
  10.         let msg = format!("{addr} connected");
  11.         dbg!(msg);
  12.         tokio::task::spawn(async move {
  13.             let io = TokioIo::new(tcp_stream);
  14.             let conn = http1::Builder::new()
  15.                 .serve_connection(io, service_fn(server_upgrade));
  16.             // Don't forget to enable upgrades on the connection.
  17.             let mut conn = conn.with_upgrades();
  18.             let conn = Pin::new(&mut conn);
  19.             if let Err(err) = conn.await {
  20.                 println!("Error serving connection: {:?}", err);
  21.             }
  22.         });
  23.     }
  24. }
复制代码
将http协议升级到https

1、欣赏器在举行https握手时,必要先发送一个CONNECT的http哀求给署理服务器,署理服务器返回状态码200的http相应,代表欣赏器与署理服务毗连创建乐成。
2、欣赏器与署理服务器开始TLS握手,署理服务器开始毗连远程网站服务器。
3、二者都毗连乐成时就可以传输数据了。
4、对于http哀求,署理服务器与远程服务器毗连乐成后直接转发哀求和相应。
  1. /// Intercept local requests
  2. fn intercept_request(mut request: Request<Incoming>) -> (Request<HttpBody>, Option<Response<HttpBody>>) {
  3.     dbg!(request.uri().to_string());
  4.     request.headers_mut().remove("Accept-Encoding");
  5.     let req = request.map(|b| b.boxed());
  6.     if let Some(Ok(host)) = req.headers().get(header::HOST).map(|h| h.to_str()) {
  7.         if host.contains("127.0.0.1:7890")
  8.             || host.contains("localhost:7890")
  9.             || host.contains("baidu") {
  10.             let resp = Response::builder()
  11.                 .header(header::CONTENT_TYPE, "text/plain")
  12.                 .body(full("Proxylea Server Power By Rust & Hyper\n"));
  13.             return (req, Some(resp.unwrap()));
  14.         }
  15.     }
  16.     (req, None)
  17. }
  18. /// Intercept remote responses
  19. fn intercept_response(mut response: Response<Incoming>) -> Response<HttpBody> {
  20.     dbg!({ format!("{:?}", response.headers()) });
  21.     response.headers_mut().insert("proxy-server", "Proxylea".parse().unwrap());
  22.     //let (parts,incoming)=resp.into_parts();
  23.     let resp = response.map(|b| b.map_frame(|frame| {
  24.         //You can modify and record the response body
  25.         if let Some(bytes) = frame.data_ref() {
  26.             //todo
  27.         }
  28.         frame
  29.     }).boxed());
  30.     resp
  31. }
复制代码
拦截本地欣赏器哀求和远程服务器相应

1、拦截欣赏器对baidu网站的哀求,并返回一些信息
2、拦截全部http相应,而且打印相应头和添加一些相应头
  1. /// Intercept local requests
  2. fn intercept_request(mut request: Request<Incoming>) -> (Request<HttpBody>, Option<Response<HttpBody>>) {
  3.     dbg!(request.uri().to_string());
  4.     request.headers_mut().remove("Accept-Encoding");
  5.     let req = request.map(|b| b.boxed());
  6.     if let Some(Ok(host)) = req.headers().get(header::HOST).map(|h| h.to_str()) {
  7.         if host.contains("127.0.0.1:7890")
  8.             || host.contains("localhost:7890")
  9.             || host.contains("baidu") {
  10.             let resp = Response::builder()
  11.                 .header(header::CONTENT_TYPE, "text/plain")
  12.                 .body(full("Proxylea Server Power By Rust & Hyper\n"));
  13.             return (req, Some(resp.unwrap()));
  14.         }
  15.     }
  16.     (req, None)
  17. }
  18. /// Intercept remote responses
  19. fn intercept_response(mut response: Response<Incoming>) -> Response<HttpBody> {
  20.     dbg!({ format!("{:?}", response.headers()) });
  21.     response.headers_mut().insert("proxy-server", "Proxylea".parse().unwrap());
  22.     //let (parts,incoming)=resp.into_parts();
  23.     let resp = response.map(|b| b.map_frame(|frame| {
  24.         //You can modify and record the response body
  25.         if let Some(bytes) = frame.data_ref() {
  26.             //todo
  27.         }
  28.         frame
  29.     }).boxed());
  30.     resp
  31. }
复制代码
效果展示




全部代码

团体代码写下来标题不大,重要是前面的openssl库的编译有些标题。Rust的hyper库类似于Java的Netty库,都属于底层库,但是hyper功能远不如Netty,hyper只是一个http相干的底层库。另有就是相干文档与资源着实是TM太少了,只能去看官方的example。从开辟服从上来讲还是Netty更快(不如说是Java开辟服从更快),但是学习hyper库有助于学习Rust的异步、特性、泛型,假如望见hyper库内里的pin_xx、poll_xx、各种特性与泛型非常天然的话,那么离纯熟使用Rust也就不远了。
  1. use std::error::Error;
  2. use std::net::SocketAddr;
  3. use std::pin::Pin;
  4. use std::sync::Mutex;
  5. use anyhow::{anyhow, Result};
  6. use bytes::Bytes;
  7. use futures::FutureExt;
  8. use http_body_util::{BodyExt, Empty, Full};
  9. use http_body_util::combinators::BoxBody;
  10. use hyper::{header, Request, Response, StatusCode};
  11. use hyper::body::{Body, Incoming};
  12. use hyper::client::conn::http1::{Builder, Connection, SendRequest};
  13. use hyper::rt::{Read, Write};
  14. use hyper::server::conn::http1;
  15. use hyper::service::service_fn;
  16. use hyper::upgrade::Upgraded;
  17. use hyper_util::rt::TokioIo;
  18. use openssl::ssl::{Ssl, SslAcceptor, SslConnector, SslMethod, SslVerifyMode};
  19. use tokio::net::{TcpListener, TcpStream};
  20. use tokio_openssl::SslStream;
  21. pub mod crt;
  22. type HttpBody = BoxBody<Bytes, hyper::Error>;
  23. #[tokio::main]
  24. pub async fn main() -> Result<()> {
  25.     // This address is localhost
  26.     let addr: SocketAddr = "127.0.0.1:7890".parse().unwrap();
  27.     // Bind to the port and listen for incoming TCP connections
  28.     let listener = TcpListener::bind(addr).await?;
  29.     println!("Listening on http://{}", addr);
  30.     loop {
  31.         let (tcp_stream, addr) = listener.accept().await?;
  32.         let _msg = format!("{addr} connected");
  33.         //dbg!(msg);
  34.         tokio::task::spawn(async move {
  35.             let io = TokioIo::new(tcp_stream);
  36.             let conn = http1::Builder::new()
  37.                 .serve_connection(io, service_fn(handle));
  38.             // Don't forget to enable upgrades on the connection.
  39.             let mut conn = conn.with_upgrades();
  40.             let conn = Pin::new(&mut conn);
  41.             if let Err(err) = conn.await {
  42.                 println!("Error serving connection: {:?}", err);
  43.             }
  44.         });
  45.     }
  46. }
  47. fn get_host_port(host_name: &str) -> (&str, u16) {
  48.     match host_name.find(":") {
  49.         None => {
  50.             (host_name, 80)
  51.         }
  52.         Some(i) => {
  53.             (&host_name[0..i], *&host_name[i + 1..].parse().unwrap_or(80))
  54.         }
  55.     }
  56. }
  57. fn not_found_host() -> Response<HttpBody> {
  58.     Response::builder().status(404).body(full("not found host")).unwrap()
  59. }
  60. /// Our server HTTP handler to initiate HTTP upgrades.
  61. async fn handle(mut req: Request<Incoming>) -> Result<Response<HttpBody>> {
  62.     if req.method() != hyper::Method::CONNECT {
  63.         let (host, port) = match req.headers().get(header::HOST) {
  64.             None => {
  65.                 return Ok(not_found_host());
  66.             }
  67.             Some(h) => { get_host_port(h.to_str()?) }
  68.         };
  69.         let stream = TcpStream::connect((host, port)).await?;
  70.         let io = TokioIo::new(stream);
  71.         let (mut sender, conn) = Builder::new()
  72.             .preserve_header_case(true)
  73.             .title_case_headers(true)
  74.             .handshake(io)
  75.             .await?;
  76.         tokio::task::spawn(async move {
  77.             if let Err(err) = conn.await {
  78.                 println!("Connection failed: {:?}", err);
  79.             }
  80.         });
  81.         let (req, resp) = intercept_request(req);
  82.         return match resp {
  83.             None => {
  84.                 let resp = sender.send_request(req).await?;
  85.                 Ok(resp.map(|b| b.boxed()))
  86.             }
  87.             Some(resp) => {
  88.                 Ok(resp)
  89.             }
  90.         };
  91.     }
  92.     let res = Response::new(empty());
  93.     // handle https
  94.     tokio::task::spawn(async move {
  95.         match hyper::upgrade::on(&mut req).await {
  96.             Ok(upgraded) => {
  97.                 if let Some(host) = req.uri().host() {
  98.                     if let Err(e) = server_upgraded_https(host, upgraded).await {
  99.                         let error_msg = format!("server io error: {}", e);
  100.                         dbg!(error_msg);
  101.                     };
  102.                 }
  103.             }
  104.             Err(e) => eprintln!("upgrade error: {}", e),
  105.         }
  106.     });
  107.     Ok(res)
  108. }
  109. /// https upgraded
  110. async fn server_upgraded_https(host: &str, upgraded: Upgraded) -> Result<()> {
  111.     let upgraded = TokioIo::new(upgraded);
  112.     // we have an upgraded connection that we can read and
  113.     // write on directly.
  114.     //
  115.     let tls_acceptor = get_tls_acceptor(host);
  116.     let ssl = Ssl::new(tls_acceptor.context())?;
  117.     let mut tls_stream = SslStream::new(ssl, upgraded)?;
  118.     if let Err(err) = SslStream::accept(Pin::new(&mut tls_stream)).await {
  119.         return Err(anyhow!("error during tls handshake connection from : {}", err));
  120.     }
  121.     let stream = TokioIo::new(tls_stream);
  122.     let (sender, conn) = https_remote_connect(host, 443).await?;
  123.     tokio::spawn(async move {
  124.         if let Err(err) = conn.await {
  125.             let err_msg = format!("Connection failed: {:?}", err);
  126.             dbg!(err_msg);
  127.         }
  128.     });
  129.     let wrap_sender = Mutex::new(sender);
  130.     if let Err(err) = http1::Builder::new()
  131.         .serve_connection(stream, service_fn(|req| {
  132.             let (req, resp) = intercept_request(req);
  133.             async {
  134.                 match resp {
  135.                     None => {
  136.                         let remote_resp = wrap_sender.lock().unwrap().send_request(req);
  137.                         match remote_resp.await {
  138.                             Ok(resp) => {
  139.                                 Ok::<_, hyper::Error>(intercept_response(resp))
  140.                             }
  141.                             Err(err) => {
  142.                                 let resp = Response::builder()
  143.                                     .status(StatusCode::INTERNAL_SERVER_ERROR)
  144.                                     .header(header::CONTENT_TYPE, "text/plain")
  145.                                     .body(full(err.to_string())).unwrap();
  146.                                 Ok::<_, hyper::Error>(resp)
  147.                             }
  148.                         }
  149.                     }
  150.                     Some(resp) => {
  151.                         Ok::<_, hyper::Error>(resp)
  152.                     }
  153.                 }
  154.             }
  155.         })).await {
  156.         println!("Error serving connection: {:?}", err);
  157.     }
  158.     Ok(())
  159. }
  160. fn empty() -> HttpBody {
  161.     Empty::<Bytes>::new()
  162.         .map_err(|never| match never {})
  163.         .boxed()
  164. }
  165. fn full<T: Into<Bytes>>(chunk: T) -> HttpBody {
  166.     Full::new(chunk.into())
  167.         .map_err(|never| match never {})
  168.         .boxed()
  169. }
  170. /// Certificate not cached
  171. fn get_tls_acceptor(host: &str) -> SslAcceptor {
  172.     let mut tls_builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).unwrap();
  173.     let (crt, pri_key) = crt::get_crt_key(host);
  174.     tls_builder.set_certificate(&crt).unwrap();
  175.     tls_builder.set_private_key(&pri_key).unwrap();
  176.     tls_builder.check_private_key().unwrap();
  177.     let tls_acceptor = tls_builder.build();
  178.     tls_acceptor
  179. }
  180. async fn https_remote_connect<B>(host: &str, port: u16) -> Result<(SendRequest<B>, Connection<TokioIo<SslStream<TcpStream>>, B>)>
  181.     where
  182.         B: Body + 'static,
  183.         B::Data: Send,
  184.         B::Error: Into<Box<dyn Error + Send + Sync>>, {
  185.     let addr = format!("{}:{}", host, port);
  186.     let tcp_stream = TcpStream::connect(addr).await?;
  187.     let mut builder = SslConnector::builder(SslMethod::tls_client())?;
  188.     builder.set_verify(SslVerifyMode::NONE);
  189.     let connector = builder.build();
  190.     let ssl = Ssl::new(connector.context())?;
  191.     let mut tls_stream = SslStream::new(ssl, tcp_stream)?;
  192.     if let Err(err) = SslStream::connect(Pin::new(&mut tls_stream)).await {
  193.         return Err(anyhow!("error during tls handshake connection from : {}", err));
  194.     }
  195.     let io = TokioIo::new(tls_stream);
  196.     Ok(hyper::client::conn::http1::handshake(io).await?)
  197. }
  198. /// Intercept local requests
  199. fn intercept_request(mut request: Request<Incoming>) -> (Request<HttpBody>, Option<Response<HttpBody>>) {
  200.     dbg!(request.uri().to_string());
  201.     request.headers_mut().remove("Accept-Encoding");
  202.     let req = request.map(|b| b.boxed());
  203.     if let Some(Ok(host)) = req.headers().get(header::HOST).map(|h| h.to_str()) {
  204.         if host.contains("127.0.0.1:7890")
  205.             || host.contains("localhost:7890")
  206.             || host.contains("baidu") {
  207.             let resp = Response::builder()
  208.                 .header(header::CONTENT_TYPE, "text/plain")
  209.                 .body(full("Proxylea Server Power By Rust & Hyper\n"));
  210.             return (req, Some(resp.unwrap()));
  211.         }
  212.     }
  213.     (req, None)
  214. }
  215. /// Intercept remote responses
  216. fn intercept_response(mut response: Response<Incoming>) -> Response<HttpBody> {
  217.     dbg!({ format!("{:?}", response.headers()) });
  218.     response.headers_mut().insert("proxy-server", "Proxylea".parse().unwrap());
  219.     //let (parts,incoming)=resp.into_parts();
  220.     let resp = response.map(|b| b.map_frame(|frame| {
  221.         if let Some(bytes) = frame.data_ref() {
  222.             //
  223.         }
  224.         frame
  225.     }).boxed());
  226.     resp
  227. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表