12. 从零用Rust编写正反向代理, TLS的双向认证信息及token验证

2023-12-17 00:04

本文主要是介绍12. 从零用Rust编写正反向代理, TLS的双向认证信息及token验证,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

wmproxy

wmproxy是由Rust编写,已实现http/https代理,socks5代理, 反向代理,静态文件服务器,内网穿透,配置热更新等, 后续将实现websocket代理等,同时会将实现过程分享出来, 感兴趣的可以一起造个轮子法

项目 ++wmproxy++

gite: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

什么是TLS双向认证

TLS双向认证是指客户端和服务器端都需要验证对方的身份,也称mTLS

在建立Https连接的过程中,握手的流程比单向认证多了几步。

  • 单向认证的过程,客户端从服务器端下载服务器端公钥证书进行验证,然后建立安全通信通道。
  • 双向通信流程,客户端除了需要从服务器端下载服务器的公钥证书进行验证外,还需要把客户端的公钥证书上传到服务器端给服务器端进行验证,等双方都认证通过了,才开始建立安全通信通道进行数据传输。

TLS是安全套接层(SSL)的继任者,叫传输层安全(transport layer security)。说直白点,就是在明文的上层和TCP层之间加上一层加密,这样就保证上层信息传输的安全,然后解密完后又以原样的数据回传给应用层,做到与应用层无关,所以http加个s就成了https,ws加个s就成了wss,ftp加个s就成了ftps,都是从普通tcp传输转换成tls传输实现安全加密,应用相当广泛。

单向与双向的差别

SSL单向验证

单向通讯的示意图如下

Client Server Client Hello 包含SSL/TLS版本,对称加密算法列表,随机数A Server Hello,服务端先进行选择 双方都支持的SSL/TLS协议版本,对称加密算法 公钥证书,服务端生成的随机数B Change Cipher Spec,收到这消息后开始密文传输 验证证书,是否过期,是否被吊销,是否可信,域名是否一致 Change Cipher Spec 应用数据(客户端加密) 应用数据(服务端加密) Client Server

双向通讯的示意图如下,差别

Client Server Client Hello Server Hello 额外要求客户端提供客户端证书 验证证书 客户端证书 客户端证书验证信息(CertificateVerify message) 验证客户端证书是否有效 验证客户端证书验证消息的签名是否有效 握手结束 握手结束 Client Server

备注:客户端将之前所有收到的和发送的消息组合起来,并用hash算法得到一个hash值,然后用客户端密钥库的私钥对这个hash进行签名,这个签名就是CertificateVerify message;

代码实现

将原来的rustls中的TlsAcceptor和TlsConnector进行相应的改造,变成可支持双向认证的加密结构。

获取TlsAcceptor的认证

/// 获取服务端https的证书信息
pub async fn get_tls_accept(&mut self) -> ProxyResult<TlsAcceptor> {if !self.tc {return Err(ProxyError::ProtNoSupport);}let certs = Self::load_certs(&self.cert)?;let key = Self::load_keys(&self.key)?;let config = rustls::ServerConfig::builder().with_safe_defaults();// 开始双向认证,需要客户端提供证书信息let config = if self.two_way_tls {let mut client_auth_roots = rustls::RootCertStore::empty();for root in &certs {client_auth_roots.add(&root).unwrap();}let client_auth = rustls::server::AllowAnyAuthenticatedClient::new(client_auth_roots);config.with_client_cert_verifier(client_auth.boxed()).with_single_cert(certs, key).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?} else {config.with_no_client_auth().with_single_cert(certs, key).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?};let acceptor = TlsAcceptor::from(Arc::new(config));Ok(acceptor)
}

获取TlsAcceptor的认证

/// 获取客户端https的Config配置
pub async fn get_tls_request(&mut self) -> ProxyResult<Arc<rustls::ClientConfig>> {if !self.ts {return Err(ProxyError::ProtNoSupport);}let certs = Self::load_certs(&self.cert)?;let mut root_cert_store = rustls::RootCertStore::empty();// 信任通用的签名商root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(ta.subject,ta.spki,ta.name_constraints,)}),);for cert in &certs {let _ = root_cert_store.add(cert);}let config = rustls::ClientConfig::builder().with_safe_defaults().with_root_certificates(root_cert_store);if self.two_way_tls {let key = Self::load_keys(&self.key)?;Ok(Arc::new(config.with_client_auth_cert(certs, key).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err),)?))} else {Ok(Arc::new(config.with_no_client_auth()))}
}

这里默认信任的通用的CA签发证书平台,像系统证书,浏览器信任的证书,只有第一步把基础的被信任才有资格做签发证书平台。

至此双向TLS的能力已经达成,感谢前人的经典代码才能如此轻松。

token验证

首先先定义协议的Token结构,只有sock_map为0接收此消息

/// 进行身份的认证
#[derive(Debug)]
pub struct ProtToken {username: String,password: String,
}

下面是编码解码,密码要求不超过255个字符,即长度为1字节编码

pub fn parse<T: Buf>(_header: ProtFrameHeader, mut buf: T) -> ProxyResult<ProtToken> {let username = read_short_string(&mut buf)?;let password = read_short_string(&mut buf)?;Ok(Self { username, password })
}pub fn encode<B: Buf + BufMut>(self, buf: &mut B) -> ProxyResult<usize> {let mut head = ProtFrameHeader::new(ProtKind::Token, ProtFlag::zero(), 0);head.length = self.username.as_bytes().len() as u32 + 1 + self.password.as_bytes().len() as u32 + 1;let mut size = 0;size += head.encode(buf)?;size += write_short_string(buf, &self.username)?;size += write_short_string(buf, &self.password)?;Ok(size)
}
服务端处理

如果服务端启动的时候配置了usernamepassword则表示他需要密码验证,

let mut verify_succ = option.username.is_none() && option.password.is_none();

如果verify_succ不为true,那么我们接下来的第一条消息必须为ProtToken,否则客户端不合法,关闭
收到该消息则进行验证

match &p {ProtFrame::Token(p) => {if !verify_succ&& p.is_check_succ(&option.username, &option.password){verify_succ = true;continue;}}_ => {}
}
if !verify_succ {ProtFrame::new_close_reason(0, "not verify so close".to_string()).encode(&mut write_buf)?;is_ready_shutdown = true;break;
}

认证通过后消息处理和之前的一样,验证流程完成

这篇关于12. 从零用Rust编写正反向代理, TLS的双向认证信息及token验证的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/502391

相关文章

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)

《使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)》PPT是一种高效的信息展示工具,广泛应用于教育、商务和设计等多个领域,PPT文档中常常包含丰富的图片内容,这些图片不仅提升了... 目录一、引言二、环境与工具三、python 提取PPT背景图片3.1 提取幻灯片背景图片3.2 提取

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

SpringSecurity6.0 如何通过JWTtoken进行认证授权

《SpringSecurity6.0如何通过JWTtoken进行认证授权》:本文主要介绍SpringSecurity6.0通过JWTtoken进行认证授权的过程,本文给大家介绍的非常详细,感兴趣... 目录项目依赖认证UserDetailService生成JWT token权限控制小结之前写过一个文章,从S

JSON Web Token在登陆中的使用过程

《JSONWebToken在登陆中的使用过程》:本文主要介绍JSONWebToken在登陆中的使用过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录JWT 介绍微服务架构中的 JWT 使用结合微服务网关的 JWT 验证1. 用户登录,生成 JWT2. 自定义过滤

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

springboot security使用jwt认证方式

《springbootsecurity使用jwt认证方式》:本文主要介绍springbootsecurity使用jwt认证方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录前言代码示例依赖定义mapper定义用户信息的实体beansecurity相关的类提供登录接口测试提供一