HTTPS
在上一篇文章中,我们简单描述了HTTPS的传输过程:
1.客户端发送TLS版本号,生成的随机数1和自己支持的加密方法(明文)
2.服务器返回确认的加密方法,服务端的证书,生成的随机数2(明文)
3.客户端验证证书有效(浏览器和操作系统内置),然后生成随机数3,并且用有效证书中的公钥加密这个随机数,加密传输给服务器。(非对称加密密文)
4.服务器使用自己的私钥解密这个密文,得到随机数3,此时双方都有了3个随机数(2个明文传输,1个密文传输),双方根据之前确定的加密算法,生成对话密钥(对称密钥)
5.之后的每次数据传输,都使用这个密钥对数据进行加密(对称加密密文)
本篇基于上述模型,在python中实现socket加密通信
socket基本通信
这里,我们再次请出密码学两位传奇人物:ALice和Bob,近似于客户端和服务器
ALice
import socket
alice_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
bob_address = ('127.0.0.1', 5000)
alice_socket.connect(bob_address)
while True:
message = input("请输入给Bob发送的信息:")
message=str(message)
alice_socket.send(message.encode('utf-8'))
print("发送成功,请等待回复......")
received_message = alice_socket.recv(1024)
print(f"收到 Bob 的消息: {received_message.decode('utf-8')}")
Bob
import socket
bob_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
local_address = ('127.0.0.1', 5000)
bob_socket.bind(local_address)
bob_socket.listen(1)
alice_socket, alice_address = bob_socket.accept()
print("消息接收中,请等待......")
while True:
received_message = alice_socket.recv(1024)
print(f"收到 Alice 的消息: {received_message.decode('utf-8')}")
message = input("请输入发送给Alice的消息:")
alice_socket.send(message.encode('utf-8'))
print("发送成功,请等待回复......")
上述代码可以在本地模拟实现ALice和Bob的相互通信
加密算法
生成随机数
模型中,我们需要生成三个随机数,这里设计一个统一的随机数方法,使得生成的随机数符合一个既定的标准
def generate_num():
characters = string.digits
randomNum = ''.join(random.choice(characters) for _ in range(16))
return randomNum
generate_num()每次运行都会返回一个16位随机纯数字的字符串
合成公钥
模型中,当双方都获取到了3个随机数后,会合成一个相同的对称密钥,这里要求根据3个随机数进行相同的数据处理操作,保证双方生成的对称密钥相同
def generate_sykey(s1, s2, s3):
num1 = int(s1, 16)
num2 = int(s2, 16)
num3 = int(s3, 16)
result = num1 + num2 + num3
hex_result = hex(result)[2:]
if len(hex_result) > 16:
hex_result = hex_result[-16:]
return hex_result
generate_sykey(s1, s2, s3),传入三个随机数,生成一个16位,值为16进制的字符串(合成算法可以设计得更加复杂,但是必须保证同样的输入获得一致的输出)
对称加密
在对称加密传输中,我们需要对发送的信息进行签名,然后对源信息和签名组合加密
def encrypt_message(SYkey, message, private):
# 签名
hash_message = rsa.compute_hash(message.encode('utf-8'), "SHA-256")
signer = rsa.sign(hash_message, private, "SHA-256")
b_message = message.encode('utf-8') + ",".encode('utf-8') + signer
# 加密
cipher = AES.new(SYkey.encode(), AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(b_message)
return ciphertext, tag, cipher.nonce
签名
加密时需要传入的参数:SYkey 对称密钥,message源数据,private个人私钥
首先对源数据进行hash,得到数据摘要hash_message
然后对数据摘要进行签名,得到signer签名
随后将源信息和签名组合,这里我们添加一个“,”来方便进行数据分割
加密
生成一个AES加密对象chiper
通过EAX加密模式,得到密文和一些参数
返回值:ciphertext(密文),nonce(随机数),tag(标记)三个值都需要获取并且全部传输给对方
对称解密
解析
数据接收方会得到ciphertext(密文),nonce(随机数),tag(标记)组合在一起的值,我们可以设计一个类来分割存储这个值(根据传输时的结构简单解析)
class Aes_Data():
def __init__(self, ciphers):
self.ciphertext = ciphers[:-32]
self.nonce = ciphers[-32:-16]
self.tag = ciphers[-16:]
这里需要注意,和传输时结构组合顺序一致,上述即ciphertext+nonce+tag顺序
解密
解析完成密文结构后,就可以进行解密了
def decrypt_message(SYkey, public,ciphinfo):
# 解密
cipher = AES.new(SYkey.encode(), AES.MODE_EAX, nonce=ciphinfo.nonce)
b_message = cipher.decrypt_and_verify(ciphinfo.ciphertext, ciphinfo.tag)
b_message_list = b_message.split(b',')
message = b_message_list[0].decode('utf-8')
# 验证签名
signer = b_message_list[1]
temp_hash_message = rsa.compute_hash(message.encode('utf-8'), "SHA-256")
try:
rsa.verify(temp_hash_message, signer, public)
return message
except rsa.VerificationError:
print("签名验证失败")
解密函数传入SYkey 对称密钥,public发送方公钥,ciphinfo解析完成的密文对象(带有ciphertext(密文),nonce(随机数),tag(标记))
首先建立解密AES对象,传入SYkey对称密钥和nonce随机数
然后进行AES解密,得到源信息+“,”(分隔符)+签名的的数据,分割后得到源信息和签名
验签
这里我们先将接受到的消息进行一次hash,得到接受的数据的临时摘要
然后使用rsa.verify()传入(临时摘要,签名,发送方的公钥),进行验签
验签成功,则返回源信息的数据。验签失败,则提示"签名验证失败",不返回数据。
加密通信
模型中,我们可以将通信过程分为两个阶段:加密连接阶段,加密通信阶段
加密连接阶段
本阶段需要达成两个目的:1.获取对方公钥,2.获取本次通信的对称密钥
ALice:
# 生成公钥私钥
(ALice_public, ALice_private) = rsa.newkeys(512)
ALice_public_str = ALice_public.save_pkcs1().decode()
# 1.生成随机数1,Alice的公钥
rand_num01 = Enconding.generate_num()
alice_socket.send((rand_num01 + "," + ALice_public_str).encode())
# 2.获取随机数2,Bob的公钥
received_message = alice_socket.recv(1024).decode()
code02 = received_message.split(",")
rand_num02 = code02[0]
Bob_public_str = code02[1].encode()
Bob_public = rsa.PublicKey.load_pkcs1(Bob_public_str)
# 3.生成随机数3,(公钥加密)
rand_num03 = Enconding.generate_num()
code03 = rsa.encrypt(rand_num03.encode(), Bob_public)
alice_socket.send(code03)
# 获取对称密钥
SYkey = Enconding.generate_sykey(rand_num01, rand_num02, rand_num03)
Bob:
# 生成公钥私钥
(Bob_public, Bob_private) = rsa.newkeys(512)
Bob_public_str=Bob_public.save_pkcs1().decode()
# 1.接收随机数1,和ALice的公钥
received_message = alice_socket.recv(1024).decode()
code01=received_message.split(",")
rand_num01=code01[0]
ALice_public=rsa.PublicKey.load_pkcs1(code01[1].encode())
# 2.生成随机数2,合并公钥传送
rand_num02=Enconding.generate_num()
alice_socket.send((rand_num02+","+Bob_public_str).encode())
# 3.获取随机数3,(私钥解密)
received_message = alice_socket.recv(1024)
rand_num03=rsa.decrypt(received_message,Bob_private).decode()
# 获取对称密钥
SYkey=Enconding.generate_sykey(rand_num01,rand_num02,rand_num03)
经过上述的几次传输,双方都得到了对方的公钥( _public)和相同的对称密钥(SYkey)
加密通信阶段
得到上述的参数后,我们终于可以进行加密通信了:
Alice
while True:
# 发送消息给 Bob
message = input("请输入给Bob发送的信息:")
b_message,tag,nonce=Enconding.encrypt_message(SYkey,message,ALice_private)
alice_socket.sendall(b_message+nonce+tag)
print("发送成功,请等待回复......")
# 接收 Bob 的消息
received_message = alice_socket.recv(1024)
chipinfp=Enconding.Aes_Data(received_message)
message=Enconding.decrypt_message(SYkey,Bob_public,chipinfp)
print(f"收到 Bob 的消息: {message}")
Bob
print("消息接收中,请等待......")
while True:
received_message = alice_socket.recv(1024)
chiperinfo=Enconding.Aes_Data(received_message)
message=Enconding.decrypt_message(SYkey,ALice_public,chiperinfo)
print(f"收到 Alice 的消息: {message}")
# 发送消息给 Alice
message = input("请输入发送给Alice的消息:")
b_message,tag,nonce=Enconding.encrypt_message(SYkey,message,Bob_private)
alice_socket.sendall(b_message+nonce+tag)
print("发送成功,请等待回复......")
消息发送时,先使用加密函数encrypt_message()对消息进行加密和签名,得到b_message+nonce+tag,合并传输给对方
消息接收时,先将接受的数据解析成chiperinfo,传入解密函数decrypt_message()进行解密和验签
验证
未加密通信
测试程序中,不使用加密,Alice向Bob发送“你好”,使用Wireshark抓包可以得到UTF-8编码的明文数据
使用python解码可以解析到源数据
加密通信
使用加密,然后通过wireshark抓包,
我们知道,解密需要(对称密钥,密文,对方公钥)
在加密数据传输中,对称密钥是通过随机数自己生成的,不在网络中流通,所以即使窃听者得到了密文,公钥,也无法解密数据,我们的实现了加密通信!