RSA_AES

本文最后更新于:19 天前

西北工业大学夏令营考核项目

一、实验要求

( 1) 用 RSA 算法实现两个主机之间的密钥分发, 分发的密钥为 0x 01 23 45 67 或 0x 01 23 45 67 89 AB CD EF;
( 2) 用分发的密钥和 AES 加密算法, 实现两个主机之间的加密数据传输, 测试数据是“NPU-SCS” 和其他自己构造的 7 条消息;
( 3) 以上 2 个步骤在程序中自动执行完, 无手动参与; 程序可以在同一台主机上完成, 但数据必须经过网络传输( 可以本地发送, 本地接收);
( 4) 密码算法必须是源码编译得到, 不能直接用编译过的库文件;RSA 和 AES 算法的源码可以来自于网络或其他任意渠道;
( 5) 以上算法选择国密算法实现更佳;
( 6) 用 Python 或 C/C++语言实现程序, 写出技术开发文档, 录制一段不超过 8 分钟的演示视频。 提交技术开发文档、 演示视频。

二、需求分析

设传输文件的双方分别为Alice和Bob,Alice为传输文件的一方,Bob作为接收文件的一方。

首先确定使用Windows的socket套接字实现网络通信,Bob作为服务器一方,等待Alice的连接,然后Alice向Bob发送数据。

安全需求如下:

  • 使用RSA算法生成Alice和Bob使用的公钥,并计算出私钥后分发,为后续的数字签名(MD5)和对称加密密钥(AES)的加解密做准备。

  • 因为是在公共信道中传输数据,所以可能存在攻击者冒充bob接收文件,所以Alice在与传输文件之前需要对bob的身份进行认证,而且bob防止被欺骗也需要对Alice的身份进行认证,这里可以采用第二类签名算法。

  • 在确认彼此身份后需要将文件通过公共信道传输,但是可能存在数据,所以需要对文件内容加密,考虑到加密速度问题,所以采用对称加密算法(AES)。

  • 采用对称加密算法需要密钥,而这里使用的密钥的为0x12345678。并且这个密钥也需要传给bob用于解密,但是公共信道不安全,所以也需要对该密钥进行加密。考虑到密钥长度不是很大,所以可以采用公钥加密算法,而且公钥加密算法可以不传输此次加密使用的密钥,提高安全性和效率。

  • 当Bob收到对应的密钥并解密密文后,需要向Alice发送一个确认收到的消息,防止截断攻击,确认消息也要使用到第一步使用的签名算法。

三、设计原理

从上面的需求分析可以知道,整个程序需要用的MD5,RSA,AES算法,最后使用socket编程实现通信。

3.1 MD5

​ MD5是hash函数的一种,而 Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是说,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是是将任意长度的输入变换为固定长度的输出的不可逆的单向密码体制。

​ MD5(Message-Digest Algorithm,信息摘要算法),是由美国著名密码学家Rivest设计的一种密码散列函数,可以将长度小于264比特的消息,按512比特的分组单位进行处理,输出一个128比特的消息摘要。

MD5 算法输入不定长度信息,输出固定长度 128-bits 的算法。经过程序流程,生成四个 32位数据,最后联合起来成为一个 128-bits 散列。基本方式有求余、取余、调整长度、与链接变量进行循环运算, 得出结果。基本流程如下图所示:

image-20210714095352078

3.1.1 消息填充

  • 使消息长度模512=448如果消息长度模512恰等于448,增加512个填充比特。即填充的个数为1~512,填充方法:第1比特为1,其余全部为0
  • 将消息长度转换为64比特的数值,如果长度超过64比特所能表示的数据长度,值保留最后64比特添加到填充数据后面,使数据为512比特的整数倍
  • 512比特按32比特分为16组

处理过程如下

image-20210714095700191

3.1.2 初始化链接变量

使用4个32位的寄存器A, B,C, D存放4个固定的32位整型参数,用于第一轮迭代,这里需要注意的是,寄存器的值要转化为小端序。

A=0x1234567
B=0x89ABCDEF
C=0xFEDCBA98
D=0x76543210

3.1.3 分组处理

  • 由4轮组成,521bit的消息分组Mi被均分为16个分组,参与每轮16步函数运算,即每轮包括16个步骤。每步的输入是4个32bit的链接变量和1个32bit的消息子分组,输出为32位值
  • 经过4轮共64步后,得到的4个寄存器值分别与输入链接变量进行模加,即得到此次分组处理的输出链接变量
  • 4轮操作之前,先将前一分组的链接变量(A、B、C、D的值)复制到备用记录单元,以便执行最后的模加操作

处理过程如下图

image-20210714095845995

3.1.4 步函数

MD5每轮包含16步,每轮的步函数相同,即使用同一个非线性函数,而不同轮的步函数使用的非线性函数是不同的,即四轮使用4个不同的非线性函数。设X、Y、Z是3个32比特的输入变量,输出是一个32比特变量,则这4个非线性函数F、G、H和I定义为:

F(X,Y,Z)=(X&Y)|((~X)&Z)
G(X,Y,Z)=(X&Z)|(Y&(~Z))
H(X,Y,Z)=X^Y^Z
I(X,Y,Z)=Y^(X|(~Z))

MD5步函数的执行过程:

image-20210714100312140

伪随机常数 T[ i ],可以消除输入数据的规律性,i为弧度,1 ≤ i ≤ 64,方框代表取整数部分

image-20210714100450860

轮函数先取向量(A、B、C、D)中的后3个作一次非线性函数运算,所得的结果一次加上第1个变量,32bit消息子分组和1个伪随机常数,再将所得的结果循环左移指定位数,并加上(A、B、C、D)的第二个变量,最后把新值赋给向量中的第1个变量。

3.1.5 主循环

​ 算法具体步骤如图

image-20210714100555756

正确性验证

在线网站https://md5jiami.bmcx.com/

处理字符串: I love cumt very much

image-20210714100739874

image-20210714100744577

3.2 RSA

​ 在Diffie和Hellman提出公钥密码体制的设想后,Merkle和Hellman首先共同提出MH背包公钥加密体制,随后Rivest、Shamir、Adleman联合提出RSA公钥加密体制。RSA虽晚于MH背包公钥加密体制,但它是第一个安全、实用的公钥加密算法,已成为国际标准。

RSA的基础是数论的欧拉定理,它的安全性依赖于大整数因子分解的困难性。且因为加解密次序可换,RSA公钥佳美体制既可用于加密,也可用于设计数字签名体制。

RSA算法原理

​ 安全性基于大素数因子分解的困难问题,整体算法流程简单清晰,分为如下三个部分:

(1)密钥生成算法:

​ ① 选取两个保密的大素数𝑝和𝑞,满足𝑝 ≠ 𝑞,计算𝑛 = 𝑝 × 𝑞, 𝜑(𝑛) = (𝑝 − 1)(𝑞 − 1), 𝜑(𝑛) 为𝑛的欧拉函数。

​ ② 随机选取整数𝑒, 满足1 < 𝑒 < 𝜑(𝑛)且gcd(𝑒, 𝜑(𝑛)) = 1,即𝑒与𝜑(𝑛)互素。

​ ③ 计算𝑑, 满足𝑒𝑑 ≡ 1(𝑚𝑜𝑑 (𝑛)),则公钥为(𝑒, 𝑛), 私钥为𝑑。

(2)加密 对明文进行比特串分组,使每个分组十进制小于𝑛 , 然后对每个分组𝑚(0 ≤ 𝑚 < 𝑛),计算 𝑐 = 𝑚𝑒 (𝑚𝑜𝑑 𝑛),则得到密文 c。

(3)解密 对于密文𝑐(0 ≤ c < n), 有𝑚 = 𝑐𝑑 (𝑚𝑜𝑑 𝑛), 即可得到明文m。

3.2.1 大素数生成

要去寻找一个任意的大素数是很困难的事,但是可是先随机生成一个大奇数,再通过多次素性检验检测是否为宿舍,如果多次检测都为素数则可确定为素数.

随机生成大奇数

此算法可以根据需要生成固定位数的数,当生成奇数后,会通过素性检验,如果通过检查,则素数生成成功

# 生成素数 先生成1024位的奇数,再进行素性检验,通过则生成该素数
def genPrime(b=1024):
    while True:                             # 设置死循环直到生成素数才退出
        res = "1"
        for i in range(b-2):
            res += str(random.randint(0,1))
        res += "1"                              # 最后一位为1保证为奇数
        res = int(res,2)
        if miller_rabin(res):
            return res                          # 直到该数通过素数检验才推出循环

Miller-Rabin素性检验

​ 在本实验中,我使用了 Miller-Rabin 素性检验结合随机数的生成来得到所需要的大素数, 它是一个基于概率的算法,是费马小定理的一个改进。 简单来说, 要测试𝑛是否为素数,首先将𝑛 - 1分解为2𝑠𝑑。 在每次测试开始时,先随机选一个介于[1, 𝑛 - 1]的整数𝑎,之后如果对所有的𝑟 ∈ [0, 𝑠 - 1], 若𝑎𝑑 ≠ 1(𝑚𝑜𝑑 𝑛)且𝑎^((2^𝑟)*𝑑) ≠-1(𝑚𝑜𝑑 𝑛),则𝑛是合数。否则, 𝑛有3/4的概率为素数,随着增加测试的次数,是素数的概率会越来越高,当达到某一次数时,为素数的概率可以接近100%。

def miller_rabin(n):
    s = n - 1
    t = 0
    while s % 2 == 0:  # n,s,t之间的关系为 n = 2^s * t
        s = s // 2
        t += 1
    for trials in range(10):   # 可以多增加几轮保证大概率为素数
        a = random.randrange(2, n - 1) # 随机生成a
        v = pow(a, s, n)               # 验证 a^(n-1) mod n
        if v != 1:
            i = 0
            while v != (n - 1):
                if i == t - 1:
                    return False
                else:
                    i = i + 1
                    v = (v ** 2) % n
    return True

3.2.2 生成公钥e

𝑒需要满足1 < 𝑒 < 𝜑(𝑛)且gcd(𝑒, 𝜑(𝑛)) = 1,即𝑒与𝜑(𝑛)互素。

这里为了保证安全性,设置了e的值不能小于2000。

def genE(phi_n):
    while True:
        e = genPrime(b=random.randint(3,13))  #随机生成e
        if e < 2000 :                  # e不能太小
            continue
        if phi_n%e != 0:               # 保证e不能被phi整除
            return e

3.2.3 计算私钥d

模重复平方法

为了提高大数模时的计算速度,这里采用的时模重复平方法。

算法伪代码如图

image-20210714111320464

计算私钥d

在RSA算法中求私钥中的整数d时,需要使得 (e * d ) % m = 1,该方程等价于 e * d = 1 + y * m ,也等价于 e * d - y * m = 1。(都为整数)

因此求解d的过程就是求解该二元一次方程组(e和m已知,求解d),即求e模m的逆元。

使用欧几里得扩展算法求逆元(辗转相除法)

给定模数m,求a的逆相当于求解ax=1(mod m)
这个方程可以转化为ax-my=1
然后套用求二元一次方程的方法,用扩展欧几里得算法求得一组x0,y0和gcd
检查gcd是否为1
gcd不为1则说明逆元不存在
若为1,则调整x0到0~m-1的范围中即可

具体代码如下

def caculateD(a, m):
    u1,u2,u3 = 1,0,a
    v1,v2,v3 = 0,1,m
    while v3!=0:
        q = u3//v3
        v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
    return u1%m

3.2.4加解密过程

有了以上的基础,RSA的加解密就很简单了,直接套用公式即可。

def RSAEncode(m, e, n):               # 加密公式 m^e mod n
    m = int(str2Hex(m), 16)           # 将字符转换为二进制
    c = fast_mod(m, e, n)
    return c

def RSADecode(c, d, n):                 # 加密公式 c^d mod n
    plaintext = fast_mod(c,d,n)
    plaintext = str(long_to_bytes(plaintext).decode()) # 将数字转换为字符
    return plaintext

3.2.5 正确性验证

image-20210714112147375

3.3 AES

AES加密算法同DES相同都是分组加密算法,AES是对128位数据进行加密,密钥也是128位,加密后产生128位的密文,AES的加密轮数为10轮,明文和密钥都被分为4组,每组32bit。

AES为分组密码,每组长度相等,每次加密一组数据,直到加密完整个明文。 在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。 密钥的长度可以使用128位、192位或256位甚至更多,密钥的长度不同,加密轮数也不同,如下表所示:

img

以下以AES-128为例进行阐述,AES的加密公式为C = E(K,P),在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是完全一样的,只有第10次略有不同,即每个明文分组会进行10轮加密。

AES的处理单位是字节,128位的输入明文分组P和输入密钥K都被分成16个字节,分别记为P = P0 P1 … P15 和 K = K0 K1 … K15。明文分组用字节为单位的正方形矩阵描述,称为状态矩阵。在算法的每一轮中,状态矩阵的内容不断发生变化,最后的结果作为密文输出。

该矩阵中字节的排列顺序为从上到下、从左至右依次排列,如下图所示:

img

同样,128位密钥也是用字节为单位的矩阵表示,矩阵的每一列被称为1个32位比特字。通过密钥编排函数该密钥矩阵被扩展成一个44个字组成的序列W[0],W[1], … ,W[43],该序列的前4个元素W[0],W[1],W[2],W[3]是原始密钥,用于加密运算中的初始轮密钥加(下面介绍);后面40个字分为10组,每组4个字(128比特)分别用于10轮加密运算中的轮密钥加,如下图所示:

img

上图中,W[0] = K0 K1 K2 K3 。

AES加密算法主要步骤有:

  • 轮密钥加:AddRoundKey
  • 字节替代:ByteSub
  • 行移位: ShiftRow
  • 列混肴: MixColumns

AES的整体结构如下图所示,其中的W[0,3]是指W[0]、W[1]、W[2]和W[3]串联组成的128位密钥。加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。

img

下面分别介绍AES中一轮的操作阶段,这些操作阶段使输入位得到充分的混淆。

3.3.1 字节代换

​ AES的字节替换,本质上和DES的S盒替换是一样的,都是根据输入字节查表来获得对应的输出字节,不同的是,AES的S盒规格是16*16的字节矩阵。因此,字节替换步骤也是AES算法轮函数的关键,相较于其他运算步骤的线性特征而易于分析,S盒的非线性提供了更好的安全性。
​ 字节替换的规则是,将状态矩阵中的每个字节的高4位作为行值,低4位作为列值,取出S盒中对应的矩阵字节元素作为替换字节。例如状态矩阵中某个输入字节值为0x6b(01101011),那么行值为6(0110),列值为b(1011),查询如表4.1所示S盒,得到的输出字节为0x7f(01111111)

​ 经过该步骤的字节替换之后,输入的4*4字节状态矩阵中的每个字节元素都被新字节替代,得到的新状态矩阵将作为下一步行移位的输入。

3.3.2 行移位

行移位步骤对输入的状态矩阵进行简单的行循环移位操作(不同行的移位数不同,具体移位数由算法输入决定),本文以第几行左移几个字节为例,行移位过程如图5.1所示,移位完成之后得到的新状态矩阵将作为下一步列混合的输入。

image-20210714114322729

3.3.3 列混淆

列混合基于矩阵乘法实现,使得状态矩阵中的每一列的各个元素之间按照预先定义的列混合加密矩阵的权重来产生相互混淆的影响,产生列元素互相之间被混合的新状态矩阵。
例如,根据公式6.1进行列混合加密,第0行第0列元素的输出计算表示为:

image-20210714114422682

该元素将第0列所有元素按预设权重混合起来。

[注意]:该公式中的乘法和加法不是普通数学意义的乘和加,而是有限域GF(2^8)四则运算中的乘和加,加法即是按位异或,乘法较为复杂,此处不做赘述,有兴趣的同学可以进一步了解有限域的相关知识。

其他行列的计算公式

image-20210714114520386

AES轮函数中的所有四个步骤都是可逆变换,那么列混合公式如下

image-20210714114616930

3.3.4 轮密钥加

轮密钥加的过程十分简单,就是将列混合输出的状态矩阵(共128位)和密钥扩展得到的本轮轮密钥(共128位)进行按位异或,得到最终的本轮状态矩阵输出。

3.3.5 密钥扩展

与明文分组矩阵一样,密钥扩展也是以8比特字节作为单位对主密钥进行处理,128位主密钥可以表示为K=[K0,K1,…,K15]的4*4字节矩阵。经过密钥扩展函数,K扩展为44列的轮密钥字矩阵(其中一个字为4字节32位),表示为W=[W0,W1,…W43]。每一轮依次取出前4个字(128位)作为本轮轮密钥,如初始轮轮密钥为W[0,3]=[W0,W1,W2,W3]。密钥扩展的过程如图

3.3.6 正确性验证

image-20210714121451444

3.4 socket通信

Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

该项目中的通信流程:

绘图1

server绑定ip和端口

server = socket.socket() #创建对象server.bind(("localhost",8888))#绑定 ip和端口server.listen()  #监听con,addr = server.accept()

client创建socket连接server

client = socket.socket()client.connect(('127.0.0.1',8888))  # 服务器IP地址和端口

3.5 数字签名

​ 数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。

实现原理如图:

数字签名

四、程序实现

***加密算法的代码放在另外的文件中

有了上面的原理支撑,整个系统就可以完成了,首先看下整个传输系统的工作过程。

程序整体流程如下图:

image-20210714213527284

具体实现,所有文件如下

├── AES.py    
├── alice_Client.py  //数据发送方
├── bob_Server.py    //数据接收方
├── data     // 需要传输的八条数据,这里使用文件存储
│   ├── test0.txt│   
	├── test1.txt│   
	├── test2.txt│   
	├── test3.txt│   
	├── test4.txt│   
	├── test5.txt│   
	├── test6.txt│   
	└── test7.txt
├── file_decode.py   // RSA公私钥生成和文件读取
├── MD5.py			 
└── RSA.py

file_decode.py

该函数主要作用有两个

  • 读取文件内容
  • 生成RSA算法的n,并计算出d(每次传输时,使用的n都不一样,算是一种一次一密机制,可以提高安全性)
import randomimport refrom RSA import *from MD5 import *from AES import *e = 65537def read_file(num):    path = '.\data\\test'+str(num)+'.txt'    try:        f = open(path, 'r', encoding = 'utf-8')        text = f.read()        f.close()        print("读取成功!")        return text    except IOError:        print("读取错误!")def n_and_d():    p = genPrime(128)    q = genPrime(128)    phi = (p-1)*(q-1)    n = p*q    d = caculateD(e,phi)    print("生成的p:",p)    print("生成的q:",q)    return n,d

bob_server.py

# -*- coding = utf - 8 -*-
#@Time : 2021/7/10 23:17
#@Author : sunzy
#@File : bob.py

import socket
import libnum
from RSA import *
from MD5 import *
from AES import *
from file_decode import *

name = "This is bob."  # bob的身份信息真实环境中可以为双方知道的秘密

n,d = n_and_d()  # 获取n,d
e = 65537

server = socket.socket() #创建对象
server.bind(("localhost",8888))#绑定 ip和端口
server.listen()  #监听
#等待连接
print('Waiting connection...')
#接受请求,返回套接字对象和IP+端口号
con,addr = server.accept()
con.send(bytes("Welcome connect!\n开分发RSA公钥n:\n","utf-8"))
con.recv(1024)
# 向alice发送公钥n
con.send(bytes(str(n),"utf-8"))

n_alice = con.recv(1024).decode()
n_alice = int(n_alice)
print("Alice使用的公钥n:", n_alice)


# 验证alice身份

print("验证alice身份...")
# a = input()
alice_name = con.recv(512).decode()  # alice 的明文信息
con.send(b'1')

hash_name = con.recv(512).decode()   # alice 用私钥加密明文的hash值
plain_text = RSADecode(int(hash_name),e,n_alice) # 用alice 的公钥解密出hash值
if plain_text == md5(alice_name):   # 判断上一步的值与md5函数加密是否相同
    print(alice_name)               # 如果二者相同则可以保证对方是alice,这里的安全性是由公钥算法和hash函数保证
    print("验证通过!")               # 因为只有alice有自己的私钥,hash函数的存在防止伪造明文攻击
    con.send(bytes("您通过了验证!","utf-8"))
else:
    exit("这不是alice,验证错误!")
#送出自己的身份信息和签名值
print("等待alice验证自身身份...")

con.send(bytes(name,'utf-8'))    # 向alice发送身份信息的明文
con.recv(1024).decode()

hash_name = md5(name)            # 明文信息的hash值
crpto_name = str(RSAEncode(hash_name, d, n))  # 使用自己的私钥加密上一步的hash值
crpto_name = bytes(crpto_name,'utf-8')        # 发送给alice
con.send(crpto_name)

print(con.recv(1024).decode())
print("-----------开始接收对称加密密钥-------------")
enc_key = con.recv(1024)
enc_key = enc_key.decode()

enc_key = int(enc_key)  # 将密钥转换为int类
dec_key = fast_mod(enc_key,d,n)
print("收到的对称加密密钥:",hex(dec_key))
print("---"*5+"开始接收密文"+"---"*5)

# 一共接收八条消息
for i in range(0,8):
    print("接收第{0}条消息".format(i+1))
    con.send(b'1')
    length = con.recv(1024)
    length = int(length.decode())

    buff = []
    size = 0
    while size < length:  # 开始接受密文
        dat = con.recv(1024)
        size += len(dat.decode())
        buff.append(dat)
    data = b''.join(buff)
    bin_cipher = data.decode()
    enc_plain = int(bin_cipher)
    print("收到的密文:", enc_plain)
    plain_text = AES_dec(enc_plain, dec_key)
    print("收到的消息:", plain_text)

# 以下是发送确认收到消息和消息的数字签名
message = "Bob received the file successfully!"
con.send(bytes(message,'utf-8'))
con.recv(1024).decode()
hash_message = md5(message)
crypt_message = str(RSAEncode(hash_message,d,n))
con.send(bytes(crypt_message,'utf-8'))

print("文件传输结束!")
server.close()

alice_client.py

# -*- coding = utf - 8 -*-
#@Time : 2021/7/10 23:17
#@Author : sunzy
#@File : Alice.py

from RSA import *
from MD5 import *
from file_decode import *
import socket

name = "This is alice."   # alice的身份信息真实环境中可以为双方知道的秘密
# n = 44531776921047477359676235110843825307036514195195627878765712056028758572817 测试使用的n
# d = 34159352569920789505556306994405309761109146525598570740717995833480670158209 
n,d = n_and_d()
e = 65537  # e使用的都是65537对安全性

client = socket.socket()
client.connect(('127.0.0.1',8888))  # 本机地址和端口

data = client.recv(1024)
print(str(data, "utf-8"),end="")  # 连接成功提示消息
client.send(b'1')

n_bob = client.recv(1024).decode()
n_bob = int(n_bob)
print("bob使用的公钥n:",n_bob)
# 向bob发送自己的n
client.send(bytes(str(n),"utf-8"))

print("等待bob验证自身身份...")
# alice送出自己的身份信息和签名值
client.send(bytes(name,'utf-8'))             # alice发送身份明文信息
client.recv(1024).decode()
hash_name = md5(name)
crpto_name = str(RSAEncode(
    hash_name, d, n)) # 发送对hash值加密后的值
crpto_name = bytes(crpto_name,'utf-8')
client.send(crpto_name)

print(client.recv(1024).decode())             # 接收身份认证的结果
#接收bob身份hash值并验证
print("验证bob身份...")

bob_name = client.recv(512).decode()          # bob发送过来的身份明文信息
client.send(b'1')

hash_name = client.recv(512).decode()          # bob使用自己的私钥加密身份明文信息hash值后的值
plain_text = RSADecode(int(hash_name),e,n_bob) # 使用bob的公钥解密上一步的值

if plain_text == md5(bob_name):                # 验证是不是bob
    print(bob_name)
    print("验证通过!")
    client.send(bytes("您通过了验证!", "utf-8"))
else:
    exit("验证错误!")

print("------------开始传输对称加密密钥-----------")
key = 0x12345678
int_key = int(key)
enc_key = RSA(int_key,e,n_bob)
print("RAS加密后的密钥:",enc_key)
print("开始传输......")
client.sendall(bytes(str(enc_key), "utf-8"))

print("---" * 5 + "开始传输密文" + "---" * 5)
# 一共发送八条消息
for i in range(0,8):
    print("发送第{0}条消息".format(i+1))
    plain = read_file(i)  # 读取文件内容
    print("待加密的内容:", plain)
    enc_plain = AES_enc(plain, key)  # 使用AES算法加密内容
    print("加密后的结果:", enc_plain)
    client.recv(512)

    length = str(len(str(enc_plain)))  # 传输发送内容的长度
    length = bytes(length, "utf-8")  # 方便其接收
    client.send(length)
    print("传输密文...")
    client.sendall(bytes(str(enc_plain), "utf-8"))  # 发送密文

message = client.recv(512).decode()
client.send(b'1')
crypt_message = client.recv(512).decode()
hash_message = RSADecode(int(crypt_message),e,n_bob)
if hash_message == md5(message):
    print(message)
print("文件传输结束!")

client.close()

程序运行结果

程序运行截图

image-20210714162621437 image-20210714162636877

服务器端

生成的p: 212578631077022742980302422715125542611
生成的q: 316188159582830019844276947208676920009
Waiting connection...
Alice使用的公钥n: 76623010774774676027114980830533718839055908618566240235027551452927884987983
验证alice身份...
This is alice.
验证通过!
等待alice验证自身身份...
您通过了验证!
-----------开始接收对称加密密钥-------------
收到的对称加密密钥: 0x12345678
---------------开始接收密文---------------
接收第1条消息
收到的密文: 135682548930565198779372680096238675594
收到的消息: NUP-SCS

接收第2条消息
收到的密文: 204524647430676670718786346706390507203
收到的消息: helle,bob!
接收第3条消息
收到的密文: 246320535020402174803721345214000008151
收到的消息: I am alice!
接收第4条消息
收到的密文: 58157974753797302313029320017455836609
收到的消息: I am a cumter!
接收第5条消息
收到的密文: 226398669380783825649368460310305319452
收到的消息: zheshixiexiaox
接收第6条消息
收到的密文: 53302749101918911090246485689074999058
收到的消息: sadsadddddd
接收第7条消息
收到的密文: 176777421636819016900191058879173899767
收到的消息: asdasda
接收第8条消息
收到的密文: 3474752232209638365517789143309475776
收到的消息: welcome to nwpu!
文件传输结束!

Process finished with exit code 0

客户端

生成的p: 248283914849807386247828596061320396999
生成的q: 308610450343211666351389068102678761017
Welcome connect!
开分发RSA公钥n:
bob使用的公钥n: 67214846126881216073162389743685441714708801777758418373886646066828368003499
等待bob验证自身身份...
您通过了验证!
验证bob身份...
This is bob.
验证通过!
------------开始传输对称加密密钥-----------
RAS加密后的密钥: 32731716332593832426148490730299678696611415440651747149365389018478993136648
开始传输......
---------------开始传输密文---------------
发送第1条消息
读取成功!
待加密的内容: NUP-SCS

加密后的结果: 135682548930565198779372680096238675594
传输密文...
发送第2条消息
读取成功!
待加密的内容: helle,bob!
加密后的结果: 204524647430676670718786346706390507203
传输密文...
发送第3条消息
读取成功!
待加密的内容: I am alice!
加密后的结果: 246320535020402174803721345214000008151
传输密文...
发送第4条消息
读取成功!
待加密的内容: I am a cumter!
加密后的结果: 58157974753797302313029320017455836609
传输密文...
发送第5条消息
读取成功!
待加密的内容: zheshixiexiaox
加密后的结果: 226398669380783825649368460310305319452
传输密文...
发送第6条消息
读取成功!
待加密的内容: sadsadddddd
加密后的结果: 53302749101918911090246485689074999058
传输密文...
发送第7条消息
读取成功!
待加密的内容: asdasda
加密后的结果: 176777421636819016900191058879173899767
传输密文...
发送第8条消息
读取成功!
待加密的内容: welcome to nwpu!
加密后的结果: 3474752232209638365517789143309475776
传输密文...
Bob received the file successfully!
文件传输结束!

五、安全分析

  • 本系统最脆弱也是最危险的位置就是RSA密钥分发。在商业使用的密钥分发都存在一个可信的第三方(CA),其作用是将用户的信息和用户使用的公钥用自己的私钥隐藏在证书中。当bob从可信第三方获取到alice的证书时,需要使用可信第三方的私钥(在CA的根证书)解密证书获取证书所有者信息和公钥(alice),alice使用同样的方法验证bob的信息,这样可以确保不会被中间人攻击。

    image-20210715155058235

  • 认证过程的安全性

    认证过程采用的是第二类签名算法,Alice和Bob的私钥只有自己知道,当Alice使用自己的私钥加密信息后,拥有Alice公钥的Bob能够解密其发送的内容,并使用验证公式就可以验证对面是否为Alice。

    该过程可以理解为:

    因为只有A才拥有私钥d ,而且由公钥e 和n在计算上不能求出保密的私钥。因此签名的操作只有A才能进行,任何其他人都不能伪造签名。所以,私钥d就相当于A的印章或指纹,而加密信息SA就是A对M(明文)的签名。因此A不能抵赖,任何其他人不能伪造。

    这个过程具有不可否认认性和伪造攻击

  • 对AES密钥加密使用的是RSA算法,该过程可以使用更大的数字来保证其安全性。比如使用生成1024位的p,q计算出n。由于我们知道公钥算法目前是安全的,所以这里加密后的密钥是安全的,即使被截获,攻击者也很难破解出明文信息。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

 目录

Copyright © 2020 my blog
载入天数... 载入时分秒...