diff --git a/Cargo.toml b/Cargo.toml index 799fe68..d6968b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ colored = "3.0.0" chrono = "0.4.39" lazy_static = "1.5.0" once_cell = "1.20.2" +rand = "0.9.0" diff --git a/assert/ZHSSCA.crt b/assert/ZHSSCA.crt new file mode 100644 index 0000000..9a24fd4 --- /dev/null +++ b/assert/ZHSSCA.crt @@ -0,0 +1,52 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/src/bin/client.rs b/src/bin/client.rs new file mode 100644 index 0000000..13ab3c4 --- /dev/null +++ b/src/bin/client.rs @@ -0,0 +1,15 @@ +// 版权所有 (c) ling 保留所有权利。 +// 除非另行说明,否则仅允许在LingTransmit中使用此文件中的代码。 +// +// 由 ling 创建于 2025/2/23. +#![allow(non_snake_case)] + +use openssl::x509::X509; +use LingTransmit::client::Client; + +#[tokio::main] +async fn main() { + let cert = include_bytes!("../../assert/ZHSSCA.crt"); + let cert = X509::from_pem(cert).unwrap(); + let client = Client::tcp_connect("localhost", 11451, cert).await.unwrap(); +} diff --git a/src/bin/main.rs b/src/bin/main.rs index 74245e3..d18b4d3 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -8,10 +8,8 @@ use async_trait::async_trait; use chrono::Local; use colored::{Color, Colorize}; use fern::Dispatch; -use lazy_static::lazy_static; -use log::{Level, LevelFilter}; -use once_cell::sync::{Lazy, OnceCell}; -use std::sync::{Arc, OnceLock}; +use log::{debug, info, Level, LevelFilter}; +use std::sync::{Arc}; use tokio::task::JoinHandle; use LingTransmit::server::event::ServerEvent; use LingTransmit::server::Client::Client; @@ -97,15 +95,15 @@ struct Event {} #[async_trait] impl ServerEvent for Event { async fn client_linker_listener(&self, client: Arc) { - println!("客户端连入,ID:{}", client.id); + debug!("客户端连入,ID:{}", client.id); } async fn client_close_listener(&self, client: Arc) { - println!("客户端挂断,ID:{}", client.id) + debug!("客户端挂断,ID:{}", client.id) } async fn client_user_data(&self, client: Arc, packet: Vec) -> std::io::Result<()> { - println!( + debug!( "客户端发送数据,ID:{},数据长度:{}", client.id, packet.len() diff --git a/src/client/mod.rs b/src/client/mod.rs index de9d7da..4f28d50 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,76 +6,107 @@ use crate::packet::code::*; use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction}; +use chrono::{DateTime, NaiveDateTime, Utc}; use log::trace; +use openssl::asn1::Asn1Time; +use openssl::pkey::Public; +use openssl::rsa::{Padding, Rsa}; use openssl::x509::{X509NameEntryRef, X509}; +use rand::Rng; use std::io; use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpStream, ToSocketAddrs}; use tokio::sync::Mutex; +pub type ClientRead = Arc>; +pub type ClientWrite = Arc>; + /// 客户端 pub struct Client { - read: Arc>, - write: Arc>, + read: ClientRead, + write: ClientWrite, + key: String, +} + +/// 生成会话密钥 +fn generate_key() -> String { + const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let mut rng = rand::rng(); + let random_string: String = (0..32) + .map(|_| { + let idx = rng.gen_range(0..CHARSET.len()); + CHARSET[idx] as char + }) + .collect(); + random_string } impl Client { - fn init( - read: Arc>, - write: Arc>, - ) -> Self { - Client { read, write } + fn init(read: ClientRead, write: ClientWrite, key: String) -> Self { + Client { read, write, key } } - pub async fn tcp_connect(addr: A, ca: X509) -> io::Result { - let stream = TcpStream::connect(addr).await?; + pub async fn tcp_connect(host: &str, port: u16, ca: X509) -> io::Result { + let stream = TcpStream::connect((host, port)).await?; let (read, write) = stream.into_split(); - let read: Arc> = Arc::new(Mutex::new(read)); - let write: Arc> = Arc::new(Mutex::new(write)); + let read: ClientRead = Arc::new(Mutex::new(read)); + let write: ClientWrite = Arc::new(Mutex::new(write)); let buffer = Self::protocol_connection(read.clone(), write.clone()).await?; let cert = X509::from_pem(&*buffer)?; - //先验证证书签名 - let ca_public = ca - .public_key() - .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "无法提取CA公钥"))?; - if !cert.verify(&ca_public).map_err(|e| { + // 检查服务器证书是否合法 + check_certificate(host.to_string(), cert.clone())?; + + //设定会话密钥 + let key = generate_key(); + + let public = cert.public_key().map_err(|e| { io::Error::new( io::ErrorKind::NotFound, - format!("无法验证服务器证书签名:{}", e.to_string()), + format!("无法提取服务器证书公钥:{:?}", e), ) - })? { + })?; + let rsa = public.rsa()?; + Self::push_aes_key(read.clone(), write.clone(), &key, &rsa).await?; + Ok(Client { read, write, key }) + } + + /// 推送会话密钥 + async fn push_aes_key( + read: ClientRead, + write: ClientWrite, + key: &String, + rsa: &Rsa, + ) -> io::Result<()> { + //使用服务器公钥加密会话密钥,然后发送给服务器 + let mut buffer = vec![0u8; rsa.size() as usize]; + let size = rsa.public_encrypt(key.as_bytes(), &mut buffer, Padding::PKCS1)?; + + let mut read = read.lock().await; + let mut write = write.lock().await; + + write.write_i32_le(LING_START).await?; + write.write_i32_le(size as i32).await?; + write.write_i32_le(API_TYPE_PUSH_AES_KEY).await?; + write.write_all(&buffer).await?; + write.write_i32_le(LING_STOP).await?; + + let result = read.read_i32_le().await?; + if result != SERVER_ACK { return Err(io::Error::new( io::ErrorKind::NotFound, - "服务器证书缺少信任的CA签名", + "建立连接失败:服务器拒绝密钥", )); } - //在此实现中阻止私域证书 - let subject_name = cert.subject_name(); - let cn = match subject_name - .entries_by_nid(openssl::nid::Nid::COMMONNAME) - .next() - { - None => { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "无法获得签发对象信息", - )); - } - Some(cn) => cn, - }; - - todo!() + Ok(()) } /// 执行协议握手, - async fn protocol_connection( - read: Arc>, - write: Arc>, - ) -> io::Result> { + async fn protocol_connection(read: ClientRead, write: ClientWrite) -> io::Result> { let mut write = write.lock().await; let mut read = read.lock().await; //请求执行 Ling Transmit V1.1 握手 @@ -97,3 +128,79 @@ impl Client { Ok(buffer) } } + +fn check_certificate(host: String, cert: X509) -> io::Result<()> { + let ca = X509::from_pem(include_bytes!("../../assert/ZHSSCA.crt"))?; + //先验证证书签名 + let ca_public = ca + .public_key() + .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "无法提取CA公钥"))?; + + if !cert.verify(&ca_public).map_err(|e| { + io::Error::new( + io::ErrorKind::NotFound, + format!("无法验证服务器证书签名:{}", e.to_string()), + ) + })? { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "服务器证书缺少信任的CA签名", + )); + } + + //检查服务器证书是否位于有效期内 + let not_before = asn1_time_to_unix(cert.not_before())? + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + let not_after = asn1_time_to_unix(cert.not_after())? + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + if now < not_before || now > not_after { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "服务器证书不在有效期内", + )); + } + + let subject_name = cert.subject_name(); + let cn = match subject_name + .entries_by_nid(openssl::nid::Nid::COMMONNAME) + .next() + { + None => { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "无法获得证书签发对象信息", + )); + } + Some(cn) => cn, + }; + let cn = cn.data().as_utf8()?.to_string(); + + // 仅在调试构建时允许私域证书 + #[cfg(debug_assertions)] + if cn.eq("private") { + if host.eq("localhost") || host.eq("127.0.0.1") || host.starts_with("192.169") { + return Ok(()); + } + } + if !cn.eq(&host) { + return Err(io::Error::new(io::ErrorKind::NotFound, "服务器证书无效")); + }; + + Ok(()) +} + +fn asn1_time_to_unix(asn1_time: &openssl::asn1::Asn1TimeRef) -> io::Result { + let unix_time = Asn1Time::from_unix(0)?.diff(asn1_time)?; + let system = SystemTime::UNIX_EPOCH + + Duration::from_secs(unix_time.days as u64 * 86400 + unix_time.secs as u64); + Ok(system) +} diff --git a/src/server/Client.rs b/src/server/Client.rs index cbe099d..5aeec03 100644 --- a/src/server/Client.rs +++ b/src/server/Client.rs @@ -7,9 +7,11 @@ use crate::close_sender::CloseSender; use crate::packet::code::{SERVER_ACK, SERVER_ERROR}; use crate::packet::{read_packet, NetworkPackets}; +use crate::server::accept::SocketAddr; use crate::server::event::ServerEvent; use crate::server::ClientID; use crate::ssl::ServerCert; +use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction}; use log::{error, info}; use openssl::rsa::Padding; use std::io; @@ -19,8 +21,6 @@ use std::sync::{Arc, OnceLock}; use std::time::Duration; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::Mutex; -use crate::server::accept::SocketAddr; -use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction}; pub type ReadSoc = Mutex>>; pub type WriteSoc = Mutex>; @@ -29,8 +29,8 @@ pub type WriteSoc = Mutex>; pub struct Client { /// 服务器关闭 server_close: CloseSender, - /// 连接关闭 - client_close: CloseSender, + // 连接关闭 + //client_close: CloseSender, read_soc: ReadSoc, write_soc: WriteSoc, pub id: ClientID, @@ -59,7 +59,7 @@ impl Client { ) -> Self { Client { server_close, - client_close, + //client_close, read_soc: Mutex::new(Some(read_soc)), write_soc: Mutex::new(write_soc), id, @@ -114,10 +114,10 @@ impl Client { info!("{} 号连接被挂断",self.id); return; } - _ = self.client_close.wait_close() => { + /*_ = self.client_close.wait_close() => { info!("{} 号连接被挂断",self.id); return; - } + }*/ }; //处理客户端请求