码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证


    合集 - wmproxy(16)
    1.用Rust手把手编写一个Proxy(代理), 动工09-192.用Rust手把手编写一个Proxy(代理), UDP绑定篇09-213.5. 用Rust手把手编写一个Proxy(代理), 通讯协议建立, 为内网穿透做准备09-284.6. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 通讯协议源码解读篇09-305.7. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP及TCP内网穿透原理及运行篇10-046.8. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP改造篇之HPACK原理10-087. 用Rust手把手编写一个Proxy(代理), 准备篇, 动手造轮子09-168.9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理10-099.10. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP内网穿透支持修改头信息10-1010.11. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 实现健康检查10-12
    11.12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证10-14
    12.13. 从零开始编写一个类nginx工具, HTTP中的压缩gzip,deflate,brotli算法10-1713.14. 从零开始编写一个类nginx工具, HTTP文件服务器的实现过程及参数10-1914.15. 从零开始编写一个类nginx工具, 如果将nginx.conf转成yaml,toml,json会怎么样10-2015.16. 从零开始编写一个类nginx工具, 反向代理upstream源码实现10-2316.17. 从零开始编写一个类nginx工具, Rust中一些功能的实现10-24
    收起

    12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证

    项目 ++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单向验证

    单向通讯的示意图如下

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

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

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

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

    代码实现

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

    获取TlsAcceptor的认证

    /// 获取服务端https的证书信息
    pub async fn get_tls_accept(&mut self) -> ProxyResult {
    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> {
    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(_header: ProtFrameHeader, mut buf: T) -> ProxyResult {
    let username = read_short_string(&mut buf)?;
    let password = read_short_string(&mut buf)?;
    Ok(Self { username, password })
    }
    pub fn encode(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)
    }

    服务端处理

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

    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;
    }

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

  • 相关阅读:
    如何修复wmvcore.dll缺失问题,wmvcore.dll下载修复方法分享
    buuctf刷题9 (反序列化逃逸&shtml-SSI远程命令执行&idna与utf-8编码漏洞)
    GCC: Graph Contrastive Coding for Graph Neural NetworkPre-Training
    基础会计学名词解释
    win10卸载oracle11g
    Spring Boot 配置文件这样加密,才足够安全!
    vue3 快速入门系列 —— 组件通信
    ESP8266-Arduino编程实例-CCS811数字气体传感器驱动
    辅助驾驶功能开发-功能规范篇(16)-2-领航辅助系统NAP-功能ODD定义
    深入了解Java8新特性-日期时间API之ZonedDateTime类
  • 原文地址:https://www.cnblogs.com/luojiawaf/p/17763661.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号