【密码学】"一"文读懂TLS(二)

admin 2022年7月2日01:35:20CTF专场【密码学】"一"文读懂TLS(二)已关闭评论7 views6088字阅读20分17秒阅读模式

【密码学】"一"文读懂TLS(二)

图片

「本文所涉及到的所有代码,均未经过严格的测试,仅供学习使用,请勿直接用于生产环境」

书接上回,我们讲到了Alice和Bob在网络中通信的过程中会被抓包工具抓到原始的报文,这对于Alice和Bob来说,可不是一件好事情,因此呢,他们就在思考,我们怎么能够做到加密通信,于是乎,他们想到了对称加密的方案。

对称加密方案

图片

这里Alice和Bob采用约定好的密钥进行通信,还是根据上一篇文章写的代码来改一改,这里采用aes-ctr模式,至于为什么我选择ctr模式,因为这个模式的明文长度和密文长度是一致的,不会发生消息长度的改变,具体代码如下:

Alice发送加密的UDP数据给Bob

package main

import (
  "crypto/aes"
  "crypto/cipher"
  "fmt"
  "net"
)

func main() {
  c, err := net.DialUDP("udp"nil, &net.UDPAddr{
    IP:   net.IPv4(127001),
    Port: 8080,
  })
  if err != nil {
    fmt.Printf("dial dail,err:%v\n", err)
    return
  }
  defer func(c *net.UDPConn) {
    err := c.Close()
    if err != nil {

    }
  }(c)

  key := []byte{1111111111111111}
  iv := []byte{2222222222222222}

  block, _ := aes.NewCipher(key)

  ctr := cipher.NewCTR(block, iv)

  secret := []byte("Hello, I am Alice.")
  output := make([]bytelen(secret))

  ctr.XORKeyStream(output, secret)

  _, err = c.Write(output)
  if err != nil {
    fmt.Printf("send data failed, err:%v\n", err)
    return
  }

  var buf [1024]byte
  n, addr, err := c.ReadFromUDP(buf[:])
  if err != nil {
    fmt.Printf("recv data failed, err:%v\n", err)
    return
  }
  secret = buf[:n]
  ctr.XORKeyStream(output, secret)

  fmt.Printf("size: %v, src: %v, data: %v\n", n, addr, string(output[:n]))
}

Bob接收到Alice发送的消息并回复Alice

use std::iter::repeat;
use std::net::UdpSocket;
use std::str;
use crypto::aes::{self, KeySize};

fn main() {
    let listener = UdpSocket::bind("0.0.0.0:8080").expect("bind address failed");

    let mut buf = [0128];

    let key: Vec<u8> = repeat(0x1u8).take(16).collect();
    let iv: Vec<u8> = repeat(0x2u8).take(16).collect();

    loop {
        let (size, src) = listener.recv_from(&mut buf).expect("recv data failed");
        let secret = &buf[..size];
        let mut cipher = aes::ctr(KeySize::KeySize128, &key, &iv);

        let mut output: Vec<u8> = repeat(0u8).take(secret.len()).collect();
        cipher.process(secret, &mut output[..]);

        println!(
            "size: {}, src: {}, data: {}",
            size,
            src,
            str::from_utf8(&output).expect("convert byte failed")
        );

        let secret = "Hello, I am Bob";
        let mut output: Vec<u8> = repeat(0u8).take(secret.len()).collect();
        cipher.process(secret.as_bytes(), &mut output[..]);

        listener
            .send_to(&output, src)
            .expect("send data failed");
    }
}

好了,同样的,我们再次使用tcpdump工具来进行抓包,这样实际上我们就看不到明文信息了。

图片

这样,似乎看起来是实现了Alice和Bob进行加密通信的需求,这个方案难道就解决了?

上述方案存在的问题

如果读者看过完整的TLS协议,这个问题如果可以被这么轻松的解决,那也不会出现贼长的那篇TLS1.3的RFC了,事情当然是没有那么简单的。

我们来回顾一下上面的做法是不是存在什么问题,细心的读者可以发现,我们在这里是固定了Key的,那么如果我们不固定Key,那么Alice和Bob的通信应该如何处理呢?

首先Alice发送密钥信息给Bob,然后Alice利用刚才发送的密钥信息进行加密发送消息给Bob,修改之后的代码如下:

Alice发送消息给Bob

package main

import (
  "bytes"
  "crypto/aes"
  "crypto/cipher"
  "fmt"
  "net"
)

func main() {
  c, err := net.DialUDP("udp"nil, &net.UDPAddr{
    IP:   net.IPv4(127001),
    Port: 8080,
  })
  if err != nil {
    fmt.Printf("dial dail,err:%v\n", err)
    return
  }
  defer func(c *net.UDPConn) {
    err := c.Close()
    if err != nil {

    }
  }(c)

  iv := []byte{1111111111111111}
  key := []byte{2222222222222222}

  secretInfo := []byte{0x122222222222222221111111111111111}

  _, err = c.Write(secretInfo)
  if err != nil {
    fmt.Printf("send data failed, err:%v\n", err)
    return
  }

  block, _ := aes.NewCipher(key)

  ctr := cipher.NewCTR(block, iv)

  secret := []byte("Hello, I am Alice.")
  output := make([]bytelen(secret))

  ctr.XORKeyStream(output, secret)

  buffer := bytes.Buffer{}

  buffer.Write([]byte{0x2})
  buffer.Write(output)

  _, err = c.Write(buffer.Bytes())
  if err != nil {
    fmt.Printf("send data failed, err:%v\n", err)
    return
  }

  var buf [1024]byte
  n, addr, err := c.ReadFromUDP(buf[:])
  if err != nil {
    fmt.Printf("recv data failed, err:%v\n", err)
    return
  }
  secret = buf[:n]
  ctr.XORKeyStream(output, secret)

  fmt.Printf("size: %v, src: %v, data: %v\n", n, addr, string(output[:n]))
}

Bob回复消息给Alice

use std::iter::repeat;
use std::net::UdpSocket;
use std::str;
use crypto::aes::{self, KeySize};

fn main() {
    let listener = UdpSocket::bind("0.0.0.0:8080").expect("bind address failed");

    let mut buf = [0128];

    let mut key: Vec<u8> = repeat(0x1u8).take(16).collect();
    let mut iv: Vec<u8> = repeat(0x2u8).take(16).collect();

    loop {
        let (size, src) = listener.recv_from(&mut buf).expect("recv data failed");
        // 简单的自定义个协议,具体报文解析内容如下
        // 第一个字节表示发送的类型
        //  - 0x01: 表示发送的是 密钥 | IV
        //  - 0x02: 表示使用上一次 密钥 | IV 加密的内容

        let body = &buf[..size];
        let msg_type = body[0];

        match msg_type {
            0x1 => {
                key = Vec::from(&body[1..17]);
                iv = Vec::from(&body[17..33]);
            }
            0x2 => {
                let secret = &body[1..size];
                let mut cipher = aes::ctr(KeySize::KeySize128, &key, &iv);
                let mut output: Vec<u8> = repeat(0u8).take(secret.len()).collect();
                cipher.process(secret, &mut output[..]);
                println!(
                    "size: {}, src: {}, data: {}",
                    size,
                    src,
                    str::from_utf8(&output).expect("convert byte failed")
                );

                let secret = "Hello, I am Bob";
                let mut output: Vec<u8> = repeat(0u8).take(secret.len()).collect();
                cipher.process(secret.as_bytes(), &mut output[..]);

                listener
                    .send_to(&output, src)
                    .expect("send data failed");
            }
            _ => {}
        }

    }
}

这里简单自定义了一个Alice和Bob通信的协议,具体协议的内容看代码的注释吧,这个比较简单,所以这里就不再重复赘述了。

好了,我们接着利用tcpdump工具进行抓包

图片

这里虽然是采用了动态的密钥,但是Alice和Bob通信的密钥也是能被抓取到明文的(「这里有个前提, 对于加密算法都是公开的, 密码的安全性仅取决于密钥」),可能有读者会想了,那么我对密钥进行加密呢,那么加密密钥的密钥的安全性又如何保证呢,这看起来是个圈哈,那么Alice和Bob如何安全的交换密钥呢?欲知后事如何,且听下回分解。

参考资料

  • 图解密码技术 第三版(结城浩)
  • https://blog.helong.info/blog/2015/09/06/tls-protocol-analysis-and-crypto-protocol-design/
  • https://pkg.go.dev/crypto
  • https://docs.rs/rust-crypto/latest/crypto/

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月2日01:35:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  【密码学】"一"文读懂TLS(二) http://cn-sec.com/archives/1150830.html