为客户端实现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"
lazy_static = "1.5.0"
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 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<Client>) {
println!("客户端连入ID{}", client.id);
debug!("客户端连入ID{}", client.id);
}
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<()> {
println!(
debug!(
"客户端发送数据ID{},数据长度:{}",
client.id,
packet.len()

View File

@@ -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<Mutex<dyn OwnedReadHalfAbstraction>>;
pub type ClientWrite = Arc<Mutex<dyn OwnedWriteHalfAbstraction>>;
/// 客户端
pub struct Client {
read: Arc<Mutex<dyn OwnedReadHalfAbstraction>>,
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>,
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<Mutex<dyn OwnedReadHalfAbstraction>>,
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>,
) -> Self {
Client { read, write }
fn init(read: ClientRead, write: ClientWrite, key: String) -> Self {
Client { read, write, key }
}
pub async fn tcp_connect<A: ToSocketAddrs>(addr: A, ca: X509) -> io::Result<Self> {
let stream = TcpStream::connect(addr).await?;
pub async fn tcp_connect(host: &str, port: u16, ca: X509) -> io::Result<Self> {
let stream = TcpStream::connect((host, port)).await?;
let (read, write) = stream.into_split();
let read: Arc<Mutex<dyn OwnedReadHalfAbstraction>> = Arc::new(Mutex::new(read));
let write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>> = 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<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(
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<Mutex<dyn OwnedReadHalfAbstraction>>,
write: Arc<Mutex<dyn OwnedWriteHalfAbstraction>>,
) -> io::Result<Vec<u8>> {
async fn protocol_connection(read: ClientRead, write: ClientWrite) -> io::Result<Vec<u8>> {
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<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::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<Option<Box<dyn OwnedReadHalfAbstraction>>>;
pub type WriteSoc = Mutex<Box<dyn OwnedWriteHalfAbstraction>>;
@@ -29,8 +29,8 @@ pub type WriteSoc = Mutex<Box<dyn OwnedWriteHalfAbstraction>>;
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;
}
}*/
};
//处理客户端请求