一、实验目的
(1) 理解DES和RSA算法的基本工作原理
(2) 掌握基于DES和RSA算法的网络加密通信系统设计方法与实现技术。
(3) 掌握在Linux或windows平台上实现DES和RSA算法的编程方法。
二、实验要求
(1) 完成基于DES和RSA算法的自动分配密钥加密聊天程序。
(2) 实现密钥自动生成,并基于RSA算法进行密钥共享。
(3) 要求程序能够实现基于DES加密的全双工通信,并且加密过程对用户是透明的。
三、实验思路
客户端通信需要先注册账号后才能进行通信。
服务端在收到客户端注册信息时给该客户分配一对公私钥,并且保存客户账号相应的公钥。
私聊:发送端使用对方的公钥加密会话密钥,用会话密钥加密通信消息;接收端使用私钥解密得到会话密钥,在使用会话密钥对消息解密。
群聊:发送端使用服务器公钥对会话密钥加密,用会话密钥加密消息;服务器使用私钥解密,得到会话密钥,使用接收端的公钥加密会话密钥;接收端处理和私聊一致。
多线程操作
四、实验源码:
server.py
# -*- coding: utf-8 -*-
"""
基于DES和RSA算法自动分配密钥的加密聊天程序
server.py
@author WQ
@time 2021/5/6
"""
from socket import *
from Crypto.PublicKey import RSA
from Crypto import Random
from Crypto.Cipher import PKCS1_v1_5
import threading
import base64
def get_rsa_keys(bits=1024):
"""
生成公钥和私钥
args{
bits:字节长度, 最小为1024,且必需是1024的倍数(2048,...)
return 公钥和私钥
}
"""
random_genenator=Random.new().read #生成伪随机数,生成不同密钥对
private = RSA.generate(bits,random_genenator)
public = private.publickey()
private_key_new = private.exportKey()
public_key_new = public.exportKey()
return private_key_new,public_key_new
HOST="127.0.0.1"
PORT=9999
public_keys={}
server_private,server_public=get_rsa_keys()
server_RSA=[server_private,server_public]
def product_client(clientSocket,addr,client_dics):
#clientSocket,addr=serverSocket.accept()
requestuser=clientSocket.recv(1024).decode('utf-8')
if requestuser[0]=='+':
try:
#print(requestuser[1:],list(client_dics.keys()))
if requestuser[1:] not in list(client_dics.keys()):
client_dics[requestuser[1:]]={clientSocket:addr}
private,public=get_rsa_keys() #为用户生成RSA密钥
public_keys[requestuser[1:]]=public #服务器保存公钥
allkeys=private.decode('utf-8')+'~'+public.decode('utf-8')+'~'+server_public.decode('utf-8')
clientSocket.send(allkeys.encode('utf-8'))
#分配公钥,保存公钥
except:
print(requestuser,"登录失败!")
while True:
recvdata=clientSocket.recv(1024).decode('utf-8')
if recvdata[:4]=='exit':
print("{} 请求退出!".format(requestuser[4:]))
break
elif recvdata[:6]=='*group':
print("{} 请求群聊!".format(recvdata[6:]))
for key in public_keys:
public_key=key+'&'+public_keys[key].decode('utf-8')##发送所有用户公钥
print('群公钥: ',public_key)
clientSocket.send(public_key.encode('utf-8'))
while True:
data=clientSocket.recv(2048).decode('utf-8')
if data[:4]=='exit':
print("{} 退出群聊!".format(recvdata[6:]))
break
#收到的消息
msg=data[:data.find('@')]#含名字
des_session=data[data.find('@')+1:]#会话密钥
private=server_RSA[0]
private_key = RSA.importKey(private)
cipher = PKCS1_v1_5.new(private_key)
#会话密钥
retval = cipher.decrypt(base64.b64decode(des_session.encode()), 'ERROR').decode('utf-8')
print('群聊消息:',data)
#fromuser=data[data.find('[')+1:data.find(']')]
#data='['+recvdata[6:]+']'+data+'@'+public_keys[recvdata[6:]].decode('utf-8')#
fromuser='['+recvdata[6:]+']'
for user in client_dics:
public=public_keys[user]
key=RSA.importKey(public)
cripher=PKCS1_v1_5.new(key)
session=base64.b64encode(cripher.encrypt(retval.encode())).decode()
data=fromuser+msg+'@'+session
#print('发送消息:',data,type(retval))
for socket in client_dics[user].keys():
socket.send(data.encode('utf-8'))
elif recvdata[:6]=='*query':
print("{} 请求查询!".format(recvdata[6:]))
data=''
if not len(client_dics):
data="无人在线"
clientSocket.send(data.encode('utf-8'))
for user in client_dics.keys():
data =data +'*****'+user+" 在线\n"
clientSocket.send(data.encode('utf-8'))
elif recvdata[:8]=='*private':
print("{} 请求私聊!".format(recvdata[8:]))
username=clientSocket.recv(1024).decode('utf-8')
for key in public_keys:
public_key=key+'&'+public_keys[key].decode('utf-8')##发送所有用户公钥
print('私公钥: ',public_key)
clientSocket.send(public_key.encode('utf-8'))
#print('send')
if username in list(client_dics.keys()):
while True:
data=clientSocket.recv(2048).decode('utf-8')
print('私聊消息: ',data)
#print(data)
if data[:4] =='exit':
print("{} 退出私聊!".format(recvdata[8:]))
break
#fromuser=data[data.find('[')+1:data.find(']')]
#data='['+recvdata[8:]+']'+data+'@'+public_keys[recvdata[8:]].decode('utf-8')
data='['+recvdata[8:]+']'+data
#print(data,type(data))
for socket in client_dics[username].keys():
socket.send(data.encode('utf-8'))
else:
clientSocket.send("对方不在线!".encode('utf-8'))
print("{} 断开连接".format(addr))
client_dics.pop(requestuser[1:])
public_keys.pop(requestuser[1:])
clientSocket.close()
def server():
serverSocket=socket(AF_INET,SOCK_STREAM)
serverSocket.bind((HOST,PORT))
serverSocket.listen(10)
print("开始监听....")
client_dics={}#存储用户公钥
while True:
if len(client_dics)==0:
print("目前没有客户端请求服务....")
else:
print("*****客户端在线列表*******")
for user in client_dics:
for key in client_dics[user].keys():
print("username: {} IP: {} PORT: {}".format(user,client_dics[user][key][0],client_dics[user][key][1]))
print("*************************")
clientSocket,addr=serverSocket.accept()
print("connect from: ",addr)
t=threading.Thread(target=product_client,args=(clientSocket,addr,client_dics))
t.start()
serverSocket.close()
if __name__=='__main__':
server()
client.py
# -*- coding: utf-8 -*-
"""
基于DES和RSA算法自动分配密钥的加密聊天程序
@author WQ
@time 2021/5/6
"""
from socket import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
from pyDes import des, ECB, PAD_PKCS5
import threading
import time
server="127.0.0.1"
port=9999
buffsize=2048
RSA_keys=[]
session_key='1234A#CD'
public_keys={}
def display_menu():
# 1,2,3三个操作均在输入数字后显示结果,并再次出现这个菜单
# 4,5两个操作在输入某一字符才退出,不然就一直和别人聊天。。
print('''
----菜单
--------1. 登录
--------2. 查询在线用户
--------3. 群聊
--------4. 私聊
--------5. 查看用户密钥
--------6. 退出
''')
def login(clientSocket,username):
username = input("请输入用户名: ").strip()
username='+'+username
confirm=clientSocket.send(username.encode('utf-8'))
#confirm=clientSocket.recv(buffsize).decode('utf-8')
if confirm:
print("{} 登录成功!".format(username[1:]))
flag=True
else:
print("登录失败请重试!")
flag=False
return username[1:],flag
def query(clientSocket,username):
query='*query'+username
confirm=clientSocket.send(query.encode('utf-8'))
if confirm:
print("查询成功!")
def encrypt_DES(s,key):
#secret_key = '1234A#CD'
secret_key=key
iv = secret_key
k = des(secret_key, ECB, iv, pad=None, padmode=PAD_PKCS5)
en = k.encrypt(s.encode('utf-8'), padmode=PAD_PKCS5)
return str(base64.b64encode(en), 'utf-8')
def descrypt_DES(s,key):
#secret_key = '1234A#CD'
secret_key=key
iv = secret_key
k = des(secret_key, ECB, iv, pad=None, padmode=PAD_PKCS5)
de = k.decrypt(base64.b64decode(s.encode('utf-8')), padmode=PAD_PKCS5)
return de.decode('utf-8')
def crypt_msg(chatName):
data=input('>>').strip()
if data=='exit' or (not data):
return data
if chatName=='group':
key=RSA.importKey(RSA_keys[2])
cripher=PKCS1_v1_5.new(key)
#session=cripher.encrypt(session_key.encode('utf-8'))
#session=base64.b64encode(cripher.encrypt(session_key.encode())).decode()
#print(public_keys[chatName],type(public_keys[chatName]))
else:
public=public_keys[chatName].encode('utf-8')
key=RSA.importKey(public)
cripher=PKCS1_v1_5.new(key)
#session=cripher.encrypt(session_key.encode('utf-8'))
session=base64.b64encode(cripher.encrypt(session_key.encode())).decode()
#DES对data加密
#print(type(session))
data=encrypt_DES(data,session_key)
data=data+'@'+session
return data
def decrypt_msg(msg,sessionkey):
"""校验RSA加密 使用私钥进行解密"""
#导入公钥,返回一个RSA秘钥对象
private=RSA_keys[0].encode('utf-8')
private_key = RSA.importKey(private)
# 创建用于执行PKCS#1 v1.5加密或解密的密码, publicKey: RSA秘钥对象,rand_func=None: 随机字节函数
# 当 rand_func为固定字节时,需要将PKCS1_v1_5.py 文件 87行的 self._randfunc(1) 改 self._randfunc
cipher = PKCS1_v1_5.new(private_key)
# 对需要加密的消息进行PKCS#1 v1.5加密,再使用Base64对类似字节的对象进行解码。
#获取会话密钥
retval = cipher.decrypt(base64.b64decode(sessionkey.encode()), 'ERROR').decode('utf-8')
#print('会话密钥:',retval,type(retval))
data=descrypt_DES(msg,retval)
#print('解密后:',type(data),data)
return data
def chat_with_all(clientSocket,username):
group='*group'+username
confirm=clientSocket.send(group.encode('utf-8'))
if confirm:
print("群聊频道创建成功!")
all='group'
while True:
#print("receving for message.....")
#data=input(">>").strip()
data=crypt_msg(all)
if not data:
print("消息为空! 请输入消息!")
continue
if data=='exit':
clientSocket.send(data.encode('utf-8'))
print("退出聊天!")
#break
return
try:
#data='['+username+']'+data #加密后
clientSocket.send(data.encode('utf-8'))#加密
print("已发送!")
except:
print("发送失败!")
continue
def chat_with_one(clientSocket,username):
private='*private'+username
clientSocket.send(private.encode('utf-8'))
chatName=input('请输入对方姓名:').strip()
confirm=clientSocket.send(chatName.encode('utf-8'))
if confirm:
print("私聊频道创建成功!")
while True:
data=crypt_msg(chatName)
if not data:
print("消息为空! 请输入消息!")
continue
if data=='exit':
clientSocket.send(data.encode('utf-8'))
print("退出聊天!")
#break
return
try:
#data='['+username+']'+data #加密后
clientSocket.send(data.encode('utf-8'))#加密
print("已发送!")
except:
print("发送失败!")
continue
return
def back(clientSocket,username):
data='exit'+username
clientSocket.send(data.encode('utf-8'))
def user_operate(clientSocket):
logined=False
username=''
display_menu()
while True:
time.sleep(1)
try:
choice = int(input("输入选项: "))
except:
print("输入无效!")
continue
if choice==1:
username,logined=login(clientSocket,username)
elif choice==2 and logined:
query(clientSocket,username)
elif choice==3 and logined:
chat_with_all(clientSocket,username)
elif choice==4 and logined:
chat_with_one(clientSocket,username)
elif choice==5:
print('用户的私钥:{}\n 用户的公钥:{}\n 服务器的公钥:{}'.format(RSA_keys[0],RSA_keys[1],RSA_keys[2]))
print("*********所有用户的公钥*******")
for i in public_keys:
print('用户名:{} 公钥:{}'.format(i,public_keys[i]))
print('*****************************')
elif choice==6:
back(clientSocket,username)
break
else:
print("未登录,请先登录!")
return False
def read(clientSocket):
while True:
data=clientSocket.recv(buffsize).decode('utf-8')#解密
if not (len(data)> 0): #判断是否收到数据
break
if data[:5]=='-----': #接收用户密钥对和服务端公钥
keys=data.split('~')
#print(keys)
for key in keys:
RSA_keys.append(key)
continue
if data.find('&')>0:#接受用户公钥
public_keys[data[:data.find('&')]]=data[data.find('&')+1:]
#print(public_keys)
continue
if data.find('@')>0: #收到数据准备解密
#print(data)
msg=data[data.find(']')+1:data.find('@')]#纯数据
des_session=data[data.find('@')+1:]#会话密钥
msg=decrypt_msg(msg,des_session) #解密
#print('消息:',type(msg))
data=data[:data.find(']')+1]+msg
print('\n')
print(data)
print("已收到!")
return False
def run():
clientSocket=socket(AF_INET,SOCK_STREAM)
clientSocket.connect((server,port))
operate=threading.Thread(target=user_operate,args=(clientSocket,))
readdata=threading.Thread(target=read,args=(clientSocket,))
operate.start()
readdata.start()
operate.join()
readdata.join()
clientSocket.close()
if __name__=='__main__':
run()
五、实验结果
服务端启动:
客户端私聊:
客户端群聊: