【密码学】"一"文读懂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(127, 0, 0, 1),
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{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
iv := []byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
block, _ := aes.NewCipher(key)
ctr := cipher.NewCTR(block, iv)
secret := []byte("Hello, I am Alice.")
output := make([]byte, len(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 = [0; 128];
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(127, 0, 0, 1),
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{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
key := []byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
secretInfo := []byte{0x1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
_, 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([]byte, len(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 = [0; 128];
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/
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论