为客户端实现TLS握手

This commit is contained in:
2025-02-23 19:23:31 +08:00
parent 5c140d011f
commit 12fd63604a
6 changed files with 226 additions and 53 deletions

View File

@@ -18,3 +18,4 @@ colored = "3.0.0"
chrono = "0.4.39" chrono = "0.4.39"
lazy_static = "1.5.0" lazy_static = "1.5.0"
once_cell = "1.20.2" once_cell = "1.20.2"
rand = "0.9.0"

52
assert/ZHSSCA.crt Normal file
View File

@@ -0,0 +1,52 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

15
src/bin/client.rs Normal file
View File

@@ -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();
}

View File

@@ -8,10 +8,8 @@ use async_trait::async_trait;
use chrono::Local; use chrono::Local;
use colored::{Color, Colorize}; use colored::{Color, Colorize};
use fern::Dispatch; use fern::Dispatch;
use lazy_static::lazy_static; use log::{debug, info, Level, LevelFilter};
use log::{Level, LevelFilter}; use std::sync::{Arc};
use once_cell::sync::{Lazy, OnceCell};
use std::sync::{Arc, OnceLock};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use LingTransmit::server::event::ServerEvent; use LingTransmit::server::event::ServerEvent;
use LingTransmit::server::Client::Client; use LingTransmit::server::Client::Client;
@@ -97,15 +95,15 @@ struct Event {}
#[async_trait] #[async_trait]
impl ServerEvent for Event { impl ServerEvent for Event {
async fn client_linker_listener(&self, client: Arc<Client>) { async fn client_linker_listener(&self, client: Arc<Client>) {
println!("客户端连入ID{}", client.id); debug!("客户端连入ID{}", client.id);
} }
async fn client_close_listener(&self, client: Arc<Client>) { async fn client_close_listener(&self, client: Arc<Client>) {
println!("客户端挂断ID{}", client.id) debug!("客户端挂断ID{}", client.id)
} }
async fn client_user_data(&self, client: Arc<Client>, packet: Vec<u8>) -> std::io::Result<()> { async fn client_user_data(&self, client: Arc<Client>, packet: Vec<u8>) -> std::io::Result<()> {
println!( debug!(
"客户端发送数据ID{},数据长度:{}", "客户端发送数据ID{},数据长度:{}",
client.id, client.id,
packet.len() packet.len()

View File

@@ -6,76 +6,107 @@
use crate::packet::code::*; use crate::packet::code::*;
use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction}; use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction};
use chrono::{DateTime, NaiveDateTime, Utc};
use log::trace; use log::trace;
use openssl::asn1::Asn1Time;
use openssl::pkey::Public;
use openssl::rsa::{Padding, Rsa};
use openssl::x509::{X509NameEntryRef, X509}; use openssl::x509::{X509NameEntryRef, X509};
use rand::Rng;
use std::io; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, ToSocketAddrs}; use tokio::net::{TcpStream, ToSocketAddrs};
use tokio::sync::Mutex; use tokio::sync::Mutex;
pub type ClientRead = Arc<Mutex<dyn OwnedReadHalfAbstraction>>;
pub type ClientWrite = Arc<Mutex<dyn OwnedWriteHalfAbstraction>>;
/// 客户端 /// 客户端
pub struct Client { pub struct Client {
read: Arc<Mutex<dyn OwnedReadHalfAbstraction>>, read: ClientRead,
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>, 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 { impl Client {
fn init( fn init(read: ClientRead, write: ClientWrite, key: String) -> Self {
read: Arc<Mutex<dyn OwnedReadHalfAbstraction>>, Client { read, write, key }
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>,
) -> Self {
Client { read, write }
} }
pub async fn tcp_connect<A: ToSocketAddrs>(addr: A, ca: X509) -> io::Result<Self> { pub async fn tcp_connect(host: &str, port: u16, ca: X509) -> io::Result<Self> {
let stream = TcpStream::connect(addr).await?; let stream = TcpStream::connect((host, port)).await?;
let (read, write) = stream.into_split(); let (read, write) = stream.into_split();
let read: Arc<Mutex<dyn OwnedReadHalfAbstraction>> = Arc::new(Mutex::new(read)); let read: ClientRead = Arc::new(Mutex::new(read));
let write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>> = Arc::new(Mutex::new(write)); let write: ClientWrite = Arc::new(Mutex::new(write));
let buffer = Self::protocol_connection(read.clone(), write.clone()).await?; let buffer = Self::protocol_connection(read.clone(), write.clone()).await?;
let cert = X509::from_pem(&*buffer)?; 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::Error::new(
io::ErrorKind::NotFound, 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<Public>,
) -> 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( return Err(io::Error::new(
io::ErrorKind::NotFound, io::ErrorKind::NotFound,
"服务器证书缺少信任的CA签名", "建立连接失败:服务器拒绝密钥",
)); ));
} }
//在此实现中阻止私域证书 Ok(())
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!()
} }
/// 执行协议握手, /// 执行协议握手,
async fn protocol_connection( async fn protocol_connection(read: ClientRead, write: ClientWrite) -> io::Result<Vec<u8>> {
read: Arc<Mutex<dyn OwnedReadHalfAbstraction>>,
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>,
) -> io::Result<Vec<u8>> {
let mut write = write.lock().await; let mut write = write.lock().await;
let mut read = read.lock().await; let mut read = read.lock().await;
//请求执行 Ling Transmit V1.1 握手 //请求执行 Ling Transmit V1.1 握手
@@ -97,3 +128,79 @@ impl Client {
Ok(buffer) 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<SystemTime> {
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)
}

View File

@@ -7,9 +7,11 @@
use crate::close_sender::CloseSender; use crate::close_sender::CloseSender;
use crate::packet::code::{SERVER_ACK, SERVER_ERROR}; use crate::packet::code::{SERVER_ACK, SERVER_ERROR};
use crate::packet::{read_packet, NetworkPackets}; use crate::packet::{read_packet, NetworkPackets};
use crate::server::accept::SocketAddr;
use crate::server::event::ServerEvent; use crate::server::event::ServerEvent;
use crate::server::ClientID; use crate::server::ClientID;
use crate::ssl::ServerCert; use crate::ssl::ServerCert;
use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction};
use log::{error, info}; use log::{error, info};
use openssl::rsa::Padding; use openssl::rsa::Padding;
use std::io; use std::io;
@@ -19,8 +21,6 @@ use std::sync::{Arc, OnceLock};
use std::time::Duration; use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::server::accept::SocketAddr;
use crate::stream::{OwnedReadHalfAbstraction, OwnedWriteHalfAbstraction};
pub type ReadSoc = Mutex<Option<Box<dyn OwnedReadHalfAbstraction>>>; pub type ReadSoc = Mutex<Option<Box<dyn OwnedReadHalfAbstraction>>>;
pub type WriteSoc = Mutex<Box<dyn OwnedWriteHalfAbstraction>>; pub type WriteSoc = Mutex<Box<dyn OwnedWriteHalfAbstraction>>;
@@ -29,8 +29,8 @@ pub type WriteSoc = Mutex<Box<dyn OwnedWriteHalfAbstraction>>;
pub struct Client { pub struct Client {
/// 服务器关闭 /// 服务器关闭
server_close: CloseSender, server_close: CloseSender,
/// 连接关闭 // 连接关闭
client_close: CloseSender, //client_close: CloseSender,
read_soc: ReadSoc, read_soc: ReadSoc,
write_soc: WriteSoc, write_soc: WriteSoc,
pub id: ClientID, pub id: ClientID,
@@ -59,7 +59,7 @@ impl Client {
) -> Self { ) -> Self {
Client { Client {
server_close, server_close,
client_close, //client_close,
read_soc: Mutex::new(Some(read_soc)), read_soc: Mutex::new(Some(read_soc)),
write_soc: Mutex::new(write_soc), write_soc: Mutex::new(write_soc),
id, id,
@@ -114,10 +114,10 @@ impl Client {
info!("{} 号连接被挂断",self.id); info!("{} 号连接被挂断",self.id);
return; return;
} }
_ = self.client_close.wait_close() => { /*_ = self.client_close.wait_close() => {
info!("{} 号连接被挂断",self.id); info!("{} 号连接被挂断",self.id);
return; return;
} }*/
}; };
//处理客户端请求 //处理客户端请求