编程思路:
- 客户端和服务器端建立连接
- 客户端产生非对称密钥,将公钥传送给服务器端
- 服务器端通过公钥将密钥进行加密并传送给客户端
- 客户端接收到密钥并进行解密,双方开始通信
背景资料:
- AES原理(图解密码技术第三章)
- RSA原理(图解密码技术第五章)
- Socket通信原理
代码部分
注释已经非常详细了,这里就不再赘述。
异常类定义
class AuthenticationError(Exception):
def __init__(self, Errorinfo):
super().__init__()
self.errorinfo = Errorinfo
def __str__(self):
return self.errorinfo
服务器端代码
服务器类定义:
import socket
import rsa
import pickle
from cryptography.fernet import Fernet
import hashlib
from errorclass import AuthenticationError
import time
# 使用图灵机器人的自动回复功能
from tlrobot import get_reply
class Server:
# 用来标记同时连接的客户端的数量
number = 0
# 默认的最大等待数量为5
# 默认使用本机的ip地址和8080端口
def __init__(self, backlog=5, addr=('localhost', 8080)):
# 默认使用AF_INET协议族,即ipv4地址和端口号的组合以及tcp协议
self.serverSocket = socket.socket()
# 绑定监听的ip地址和端口号
self.serverSocket.bind(addr)
# 开始等待
self.serverSocket.listen(backlog)
# 该函数需要并行处理
def link_one_client(self):
# 获取客户端对象和客户端地址
clientSocket, addr = self.serverSocket.accept()
# 客户端数量加1
Server.number = Server.number + 1
# 标记当前客户端编号
now_number = Server.number
# 打印
print("和客户端{0}建立连接\n目标主机地址为:{1}".format(now_number, addr))
# 接受客户端传递的公钥
# 这里可以加一个哈希函数检验公钥的正确性!
# 运用pickle进行反序列化
publicKeyPK, pubKeySha256 = pickle.loads(clientSocket.recv(1024))
if hashlib.sha256(publicKeyPK).hexdigest() != pubKeySha256:
raise AuthenticationError("密钥被篡改!")
else:
publicKey = pickle.loads(publicKeyPK)
print("已接受公钥")
# 下面是用公钥加密对称密钥并传递的过程
# 产生用于对称加密的密钥
sym_key = Fernet.generate_key()
# 用pickle进行序列化用来进行网络传输
# 对密钥进行hash保证其准确性
en_sym_key = rsa.encrypt(pickle.dumps(sym_key), publicKey)
en_sym_key_sha256 = hashlib.sha256(en_sym_key).hexdigest()
print("正在加密传送密钥")
clientSocket.send(pickle.dumps((en_sym_key,en_sym_key_sha256)))
# 这里可以添加密钥交换成功的函数
# 初始化加密对象
f = Fernet(sym_key)
# 下面使用对称密钥进行加密对话的过程
while True:
time.sleep(0.3)
# 接收到的加密消息
en_recvData = clientSocket.recv(1024)
recvData = f.decrypt(en_recvData).decode()
print("接受到客户端{0}传来的消息:{1}".format(now_number, recvData))
# 调用图灵机器人
sendData = get_reply(recvData)
# 对消息进行加密
en_sendData = f.encrypt(sendData.encode())
clientSocket.send(en_sendData)
服务器端类实例化:
import threading
from serverclass import Server
print("欢迎使用服务端程序!")
server = Server()
while True:
# 这里使用多线程可以避免服务器阻塞在一个客户端上
t = threading.Thread(target=server.link_one_client)
t.start()
客户端代码
客户端类定义:
import socket
import rsa
import pickle
from cryptography.fernet import Fernet
import hashlib
from errorclass import AuthenticationError
class Client:
def __init__(self):
# 产生非对称密钥
self.asyKey = rsa.newkeys(2048)
# 公钥和私钥
self.publicKey = self.asyKey[0]
self.privateKey = self.asyKey[1]
def link_server(self, addr=('localhost', 8080)):
# 创建socket通信对象
# 默认使用AF_INET协议族,即ipv4地址和端口号的组合以及tcp协议
clientSocket = socket.socket()
# 默认连接服务器地址为本机ip和8080端口
clientSocket.connect(addr)
# 向服务器传递公钥,和该公钥字符串化后的sha256值
print("正在向服务器传送公钥")
sendKey = pickle.dumps(self.publicKey)
sendKeySha256 = hashlib.sha256(sendKey).hexdigest()
clientSocket.send(pickle.dumps((sendKey, sendKeySha256)))
# 接受服务器传递的密钥并进行解密
symKey, symKeySha256 = pickle.loads(clientSocket.recv(1024))
if hashlib.sha256(symKey).hexdigest() != symKeySha256:
raise AuthenticationError("密钥被篡改!")
else:
self.symKey = pickle.loads(rsa.decrypt(symKey, self.privateKey))
print("密钥交换完成")
# 初始化加密对象
f = Fernet(self.symKey)
while True:
sendData = input("输入你要发送的消息:")
en_sendData = f.encrypt(sendData.encode())
clientSocket.send(en_sendData)
en_recvData = clientSocket.recv(1024)
recvData = f.decrypt(en_recvData).decode()
print("接受到服务器传来的消息:{0}".format(recvData))
客户端类实例化:
from clientclass import Client
print("欢迎使用客户端程序!")
client = Client()
client.link_server()
调用图灵机器人的代码
import requests
import json
def get_reply(data):
datas = {
'reqType': 0,
"perception": {
"inputText": {
"text": data
},
"selfInfo": {
"location": {
"city": "武汉",
"province": "湖北"
}
}
},
"userInfo": {
# 这里填上自己的apiKey
"apiKey": "*********************",
# userid可以随便填写
"userId": "443545"
}
}
datas = json.dumps(datas)
url = "http://openapi.tuling123.com/openapi/api/v2"
r = requests.post(url=url, data=datas)
return json.loads(r.text).get("results")[0].get("values").get("text")