- 前言:之前密码学实验课已经用python分别写过AES和RSA,本次内容要求是使用RSA进行密钥分配,使用AES进行加密传输。
具体要求如下:
编写一段程序,实现两个主机之间的密钥分发和加密传输。
要求:
(1)用RSA算法实现两个主机之间的密钥分发,分发的密钥是“12345678”;
(2)用分发的密钥和AES加密算法,实现两个主机之间的加密数据传输,传输的数据是“NPU”;
(3)两个步骤在程序中自动执行完,无手动参与;程序可以在同一台主机上完成,但数据必须经过网络传输(可以本地发送,本地接收);
(4)RSA和AES算法必须是源码编译得到,不能直接用编译过的库文件;RSA和AES算法的源码可以来自于网络或其他任意渠道;
(5)用Python或C/C++语言实现程序,写出实现技术文档。
分析需要新做的内容是第三条,在一个程序中自动执行,且数据需要经过网络传输,初步计划用socket套接字来实现。
1、Socket通信
参考https://blog.csdn.net/resilient/article/details/82796061
创建文件,修改权限
开启Apache
Mac中有自带的Apache服务,不用再安装软件,开启/关闭命令如下
sudo apachectl start // 开启
sudo apachectl stop // 关闭
sudo apachectl restart
浏览器中输入 127.0.0.1 ,如果开启成功,显示结果如下:
按照教程并不能实现,尝试使用python进行实现。
使用无连接的UDP
客户端代码如下
from socket import *
class UdpClient:
serverName = '127.0.0.1' //服务器名
serverPort = 12000 //端口号
socketAddress = (serverName, serverPort)
def __init__(self):
#define the type of socket is IPv4 and Udp
self.clientSocket = socket(AF_INET, SOCK_DGRAM)
while True:
message = input("Input a lowercase sentence\n")
self.clientSocket.sendto(message.encode('utf-8'), self.socketAddress)
returnMessage, serverAddress = self.clientSocket.recvfrom(2048)
if not returnMessage:
break
print("The peersocket is: %s:%s" %(serverAddress[0], serverAddress[1]))
print("The return message is: %s" %returnMessage.decode('utf-8'))
if __name__ == '__main__':
client = UdpClient()
这个程序的功能是把用户输入的内容用utf-8进行编码,传输出去,并且接受服务器端传来的数据,使用的是self.clientSocket.recvfrom()
服务器端如下:
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('',serverPort))
print('Waiting for connection...')
while True:
message, clientAddress = serverSocket.recvfrom(2048)
print('Receive Message: %s' %message.decode('utf-8'))
modifiedMessage = message.decode('utf-8').upper()
serverSocket.sendto(modifiedMessage.encode('utf-8'), clientAddress)
代码流程和刚才差不多,也是设置端口,进行绑定,接受数据,把数据转成大写,发送给客户端
完成!
2、RSA实现密钥分发
在刚才的基础上加入RSA加密传递明文,参考之前的记录。
由于文件较大代码较多,分在不同的文件中再进行调用逻辑会更加清晰一些
存在的一个小问题是:要传递的密钥为12345678,如果当作数字来传输,数值较大,可能会由于它超过了要模的值,在计算的时候会出现错误。
解决方法是:把数字分割为4个一组,并且传递明文的长度一共是多少。在接收端分别解密,并把它拼在一起,作为数字长度。遇到的问题:如何传递函数。现在的数还是str, 在加密时应该先转为int。
一直报错
后来发现是重复用M了。但是修改之后仍然不行,可能是字符串分割转换的时候有问题了。
加上这句 temp_M = int(temp_M,10)
,也不行。
查阅资料发现是因为使用的是python3,/除法要变成 // ,问题解决。
再次出错,把刚才UDP socket的class改成def了,再改回来,就可以传输了。以为可以,但是实际上还是不行。
尝试按照发送的框架,把RSA嵌套在内部 =。=
可以实现,但要注意一些小细节,密钥产生是在服务器端
分组解密收到的字符串应该连接起来
上面这样是不正确的,应该使用向list尾部增加元素的方法,用.append
尝试如下:
M_de.append(decrypedmessage)
res = ''.join(M_de)
要加入一个密钥传输停止的标志
if (decrypedmessage == 'stop'):
break;
传输这边可以了,解码的有些错误。数字和字符串还是有一些不一样的,把原来打算传输的stop换为404,表示传输结束。【有一定的隐患,如果要传输的内容恰好为404,就会认为是停止传输了】
有一句报错:invalid literal for int() with base 10:
改正:```stop_sign = str(404)``
再次改正:接收端不能停止的原因是:我发送的明文是404,解密后变成了其他的。应该在发送之前先加密再发送
C = Encry(int(stop_sign),e,n)
self.clientSocket.sendto(str(C).encode('utf-8'), self.socketAddress)
记住,在加密函数里面要用到的是数字,其他时候是字符串
- 仍然存在的问题:接收端没法停,不知道密钥分发已经结束了
循环条件尝试改为:while (decrypedmessage != str(404))
先放弃了...老师催作业了
07.15继续
-
输出404的格式,对照看怎们能判断出来
输出发现其实是int型。while判断用int,下面.join用str,res = ''.join(str(M_de))
client端经常报错的原因是:如果恰好是4的倍数,循环次数控制不当,总会多一次而无法正确加密。改正方法:for i in range(ceil(lenOfM/4)):
成功
- 删掉加在最后的404。类型是list
方法:先获取长度,
lenofres = len(M_de)
res = ''
for i in range(lenofres - 1):
res = res + str(M_de[i])
3、AES加密要传输的内容
先把原来的代码加进去,慢慢改错。把文件的长读取改为对三个字符的读取,尾部还是需要加00部全的。
修改密钥为刚才传输的,直接在代码中修改了(这是一个缺点,导致修改密钥时还要在AES代码中修改,但是来不及完善了)
修改完成
再修改一点输出格式即可。
客户端代码:
#coding:utf-8
from socket import *
import random
import math
import AES_en
import binascii
class UdpClient:
serverName = '127.0.0.1'
serverPort = 12000
socketAddress = (serverName, serverPort)
def __init__(self):
#define the type of socket is IPv4 and Udp
self.clientSocket = socket(AF_INET, SOCK_DGRAM)
def power(x,k,r):
# print("求幂运算运算",x,k,r)
m=k
t=1
while(m>0):
m=m//2
t=t*2
m=k
y=1
while(t>1):
t=t//2
y=(y*y)%r #在运算的过程中取模 简化运算
if (m>=t):
y=(y*x)%r
m=m-t
return y
#加密
def Encry(M,e,n):
C_encry=power(M,e,n)
return C_encry
#解密
def Decry(C,d,n):
M_decry=power(C,d,n)
return M_decry
e = int(input("输入对方的公钥e = "))
n = int(input("输入对方的公钥n = "))
M_input=input("输入要传递的会话密钥:")
M_input = str(M_input)
lenOfM = len(M_input)
stop_sign = str(404)
for i in range(int(math.ceil(lenOfM/4))):
temp_M = M_input[(i)*4:(i+1)*4]
temp_M = int(temp_M,10)
C=Encry(temp_M,e,n) #加密
message = C
self.clientSocket.sendto(str(message).encode('utf-8'), self.socketAddress)
# returnMessage, serverAddress = self.clientSocket.recvfrom(2048)
# if not returnMessage:
# break
# print("The peersocket is: %s:%s" %(serverAddress[0], serverAddress[1]))
# print("The return message is: %s" %returnMessage.decode('utf-8'))
C = Encry(int(stop_sign),e,n)
self.clientSocket.sendto(str(C).encode('utf-8'), self.socketAddress)
CCC = 'NPU'
sread = CCC
mood = (len(sread))%16
print("MOOD:",mood)
part_M1 = CCC
while(len(part_M1)<16):
part_M1 = part_M1 + str(0)
M_temp1=binascii.b2a_hex(part_M1)
C_temp=AES_en.EncryptionAES(M_temp1)
CCC1 = ''.join(C_temp)
print("M1",CCC1)
print("SSSMMM!:",mood)
self.clientSocket.sendto(str(CCC1).encode('utf-8'), self.socketAddress)
if __name__ == '__main__':
client = UdpClient()
服务器端代码:
# encoding: utf-8
from socket import *
import random
import math
import AES_de
import binascii
#求d最大公因数
def Greatest_common_factor(a,b):
while a%b !=0:
temp=a
a=b
b=(temp%b)
# print('!!!a=',a,'b=',b)
return b;
#检测是否是素数 //这里用的是费马小定理,还可以用拉宾米勒算法
def check_prime(s):
sign=1
for check_times in range(20): #检测20次
b=random.randint(1,10000) #b是随机的正整数
if Greatest_common_factor(b,s) ==1:
if b**(s-1)%s ==1:
pass
#print('AAA')
else:
sign=0
#print('BBB')
break
else:
sign=0
#print('CCC')
break
if sign==1:
print(s,'is prime')
else:
print(s,'is not prime')
return sign
#选择e的值
def select_e(fi):
e=random.randint(1,fi)
while(Greatest_common_factor(e,fi)!=1):
e=random.randint(1,fi)
return e
#生成d的值
def compute_d(e,fi):
for k in range(10000000):
mod_temp=(k*fi+1)%e
if (mod_temp==0):
commen_factor=Greatest_common_factor((k*fi+1)//e,fi)
if commen_factor==1:
d=mod_temp
break
d=(k*fi+1)/e
print("d is:")
print(d)
return d
#求幂运算的优化
def power(x,k,r):
# print("求幂运算运算",x,k,r)
m=k
t=1
while(m>0):
m=m/2
t=t*2
m=k
y=1
while(t>1):
t=t/2
y=(y*y)%r #在运算的过程中取模 简化运算
if (m>=t):
y=(y*x)%r
m=m-t
return y
#加密
def Encry(M,e,n):
C_encry=power(M,e,n)
return C_encry
#解密
def Decry(C,d,n):
M_decry=power(C,d,n)
return M_decry
#下面是RSA的步骤
#生成p
while 1:
p=random.randint(10,10000)
if p%2 ==0:
pass
else:
if check_prime(p)== 1:
break
print("p is",p)
#生成q
while 1:
q=random.randint(10,10000)
if q%2 ==0:
pass
else:
if check_prime(q)== 1:
break
print("q is",q)
print("\n")
n=p*q
fi=(p-1)*(q-1)
e=select_e(fi)
print("e is")
print(e)
print("fi is")
print(fi)
print("n is")
print(n)
d=compute_d(e,fi)
print("密钥分配阶段,等待对方输入...")
#解密
def Decry(C,d,n):
M_decry=power(C,d,n)
return M_decry
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('',serverPort))
print('Waiting for connection...')
M_de = []
decrypedmessage = ''
while (decrypedmessage != int(404)) :
message, clientAddress = serverSocket.recvfrom(2048)
print('Receive: %s' %message.decode('utf-8'))
# modifiedMessage = message.decode('utf-8').upper()
decrypedmessage = Decry(int(message.decode('utf-8')),d,n)
print(decrypedmessage)
print(type(decrypedmessage))
# if (decrypedmessage == str(404)):
# print("AAA")
# break;
# modifiedMessage = message.decode('utf-8')
# serverSocket.sendto(modifiedMessage.encode('utf-8'), clientAddress)
M_de.append(decrypedmessage)
print(M_de)
lenofres = len(M_de)
res = ''
for i in range(lenofres - 1):
res = res + str(M_de[i])
#res = ''.join(str(M_de))
print("解密后的密钥为:")
print(res)
while True:
message, clientAddress = serverSocket.recvfrom(2048)
print('message : %s' %message.decode('utf-8'))
C = message
res = []
for arr in range(0,len(C),32):
part_M1 = C[arr:arr+32]
M_temp1 = AES_de.DecryptionAES(part_M1)
M_temp2 = ''.join(M_temp1)
M_temp3 = binascii.a2b_hex(M_temp2)
res.append(M_temp3)
CCC = ''.join(res)
t=0
for i in range(16):
if(CCC[len(CCC)-16+i] == '0'):
t = i
break
print(CCC[len(CCC)-16:len(CCC)])
resss = CCC[0:len(CCC)-16+t]
print("AAA",resss)