基于DES和RSA算法自动分配密钥的加密聊天程序

一、实验目的

(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()

五、实验结果
服务端启动:
在这里插入图片描述
客户端私聊:
在这里插入图片描述
客户端群聊:
在这里插入图片描述

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值