• 2021-01-17 13:45:01

之前讲到RSA可以用来加密和数字签名，这里是RSA用作数字签名。

Python的pycrypto库实现的数字签名有一个限制，必须对哈希(hash)值进行签名，而不能直接对原文进行数字签名。好像大部分实现都有此限制。

from Crypto.PublicKey import RSA

from Crypto.Signature import PKCS1_v1_5

import Crypto.Hash.SHA512

def rsa_sign(plaintext, key, hash_algorithm=Crypto.Hash.SHA512):

"""RSA 数字签名"""

signer = PKCS1_v1_5.new(RSA.importKey(key))

#hash算法必须要pycrypto库里的hash算法，不能直接用系统hashlib库，pycrypto是封装的hashlib

hash_value = hash_algorithm.new(plaintext)

return signer.sign(hash_value)

def rsa_verify(sign, plaintext, key, hash_algorithm=Crypto.Hash.SHA512):

"""校验RSA 数字签名"""

hash_value = hash_algorithm.new(plaintext)

verifier = PKCS1_v1_5.new(RSA.importKey(key))

return verifier.verify(hash_value, sign)

if __name__ == '__main__':

private_key = '''-----BEGIN RSA PRIVATE KEY-----

MIICXQIBAAKBgQD1t3KRf4oS3sH8PbABbXL1KBYCnGq4C/yinpfQ2j2eUmZarHuw

IMT9y5ns1lpZZTktGnypvnQjF8c0Rr/cYU53DJjglAgVEb3el6iU+WZ7nwLub/BN

YS83zpzrhDE3Qy6qTM3evsUsekBR8x6f6Usl7KpEI/0b+EfRSpXDdvU64wIDAQAB

AoGBAJK0odHfPTgBCf8pcaGYkG9xLJsIeutCNOd/GxOWif2yIux2WS8SkasaWd+/

J5iCSD32t4G9dafSNZyvtTPGYUqll4aGXlFqNW8pm16HPQXWrhv1D5LVEEu3zbj+

iNG+gHwB4bISQAOJbnvB6GoFUbDf8VYwkGGlSLGw5D5tulhRAkEA/XBLTfj+5j40

QPfuRIhcBsgxynKJDcmV0sLAIOTBIfSKs5nuYHEVEOcGaxS+nPY3w1ffSUPUdxm0

7L2s+9c0SQJBAPgzLLFvUjM58J/AtklkGyJ3KK5W+jLi/N1PIw7CGYGM2yfFiQLR

ibtJVjTFhLKqDz/BK4lZ9ffU/VNHSApOncsCQQCRBzSgnw9GtGv0jaxUnW+EFgWg

IyDYufW5kOafLCh1BNpmYnztxWhXrsyWdF2Ltr48U8mbxGwN57EIFJar2v+5AkA7

GkSMRAv48tUf1Y4Sz+m+PU3Mph2SPIcmVA/vFb1pIheV0u4bY7Y+iOokStychu52

qhMp8+gkie2BBTpcafgdAkBw8bAzLgmCV8SZEN60x8c2M2Y95CoYOoMLjvQdEfen

IeDmun3DtAPBuStwYNfeQnAHCwvcOJsgDiRLzhys3056

-----END RSA PRIVATE KEY-----'''

public_key = '''-----BEGIN PUBLIC KEY-----

nGq4C/yinpfQ2j2eUmZarHuwIMT9y5ns1lpZZTktGnypvnQjF8c0Rr/cYU53DJjg

lAgVEb3el6iU+WZ7nwLub/BNYS83zpzrhDE3Qy6qTM3evsUsekBR8x6f6Usl7KpE

I/0b+EfRSpXDdvU64wIDAQAB

-----END PUBLIC KEY-----'''

message = 'RSA数字签名演示'

signature = rsa_sign(message.encode(encoding='utf-8'), private_key)

result = rsa_verify(signature, message.encode('utf-8'), public_key)

print(result)

相关文章

更多相关内容
• 利用python实现RSA数字签名（纯算法实现），用到的hash256得到消息摘要，欧几里得，扩展欧几里得算法

# python实现RSA数字签名（纯算法实现）

## 一:什么是数字签名

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

## 二：数字签名的原理

发送方将先生成一对公私钥，将公钥放到网络上，发送方利用私钥将文件或者消息进行签名。接收方得到发送方的文件或者消息、公钥以及生成的签名；首先利用公钥将得到签名生成消息摘要，在对比文件或者消息的消息摘要，如果匹配，则签名认证成功。

## 四：实现数字签名的算法

分为三种：RSA、DSA、ECDSA

## 五：具备前提知识RSA算法

RSA加密过程：

步骤说明描述
1选择一对不相等且足够大的质数p，q
2计算p，q的乘积n=p*q
3计算n的欧拉函数φ(n)=(p-1)*(q-1)
4选一个与φ(n)互质的整数e1<e<φ(n)
5计算出e对于φ(n)的模反元素dde mod φ(n)=1
6公钥KU=(e，n)
7私钥KR=(d，n)

相关概念：

质数：质数又称素数。一个大于1的自然数，除了1和它自身外，不能被其他自然数整除的数叫做质数；否则称为合数（规定1既不是质数也不是合数）。

欧拉函数：在数论，对正整数n，欧拉函数是小于n的正整数中与n互质的数的数目

例如：

• 如果n可以分解成2个质数的整数之积，那么n的欧拉函数等于这两个因子的欧拉函数之积。
• φ(n)= φ(pxq)=(p-1)*(q-1)

互质：公约数只有1的两个整数，叫做互质整数

## 六：消息摘要的前提知识

消息摘要算法的特点：

消息摘要是把任意长度的输入揉和而产生长度固定的伪随机输出的算法。消息摘要的主要特点有：

①无论输入的消息有多长，计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位，用SHA-1算法摘要的消息最终有160比特位的输出，SHA-1的变体可以产生192比特位和256比特位的消息摘要。一般认为，摘要的最终输出越长，该摘要算法就越安全。

②消息摘要看起来是“随机的”。这些比特看上去是胡乱的杂凑在一起的。可以用大量的输入来检验其输出是否相同，一般，不同的输入会有不同的输出，而且输出的摘要消息可以通过随机性检验。但是，一个摘要并不是真正随机的，因为用相同的算法对相同的消息求两次摘要，其结果必然相同；而若是真正随机的，则无论如何都是无法重现的。因此消息摘要是“伪随机的”。

③一般地，只要输入的消息不同，对其进行摘要以后产生的摘要消息也必不相同；但相同的输入必会产生相同的输出。这正是好的消息摘要算法所具有的性质：输入改变了，输出也就改变了；两条相似的消息的摘要确不相近，甚至会大相径庭。

④消息摘要函数是无陷门的单向函数，即只能进行正向的信息摘要，而无法从摘要中恢复出任何的消息，甚至根本就找不到任何与原信息相关的信息。当然，可以采用强力攻击的方法，即尝试每一个可能的信息，计算其摘要，看看是否与已有的摘要相同，如果这样做，最终肯定会恢复出摘要的消息。但实际上，要得到的信息可能是无穷个消息之一，所以这种强力攻击几乎是无效的。

⑤好的摘要算法，没有人能从中找到“碰撞”，虽然“碰撞”是肯定存在的。即对于给定的一个摘要，不可能找到一条信息使其摘要正好是给定的。或者说，无法找到两条消息，使它们的摘要相同。

## 七：python实现消息摘要

该处我采用的是hash256: SHA256算法使用的哈希值长度是256位

python代码：

def Sha256sum(message: bytes) -> bytes:
# 定义常量
# 前8个素数2..19的平方根的小数部分的前32位
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h4 = 0x510e527f
h5 = 0x9b05688c
h6 = 0x1f83d9ab
h7 = 0x5be0cd19

# 定义常数K 64
# 前64个素数2..311的立方根的小数部分的前32位
K = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)

# R为循环右移，
# 右移之后可能会超过32位，所以要和0xffffffff做与运算，确保结果为32位。
R = lambda x, n: ((x >> n) | (x << (32 - n))) & 0xffffffff
# 大端  0x12,0x34,0x56,0x78 -> 0x12345678
W = lambda i1, i2, i3, i4: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4

# 对每一个输入先添加一个'0x80'，即'10000000', 即128
ascii_list = list(map(lambda x: x, message))
msg_length = len(ascii_list) * 8
ascii_list.append(128)

# 补充0
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append(0)

# 最后64为存放消息长度，以大端数存放。
# 例如，消息为'a'，则长度为'0x0000000000000008'
for i in range(8):
ascii_list.append(msg_length >> (8 * (7 - i)) & 0xff)

# print(ascii_list)
# print(len(ascii_list)//64)
for i in range(len(ascii_list) // 64):  # 64*8=512bits
# print(ascii_list[i*64:(i+1)*64])
# 每个512bits的块进行循环
w = []
# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组
for j in range(16):
s = i * 64 + j * 4
w.append(W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3]))
for j in range(16, 64):
s0 = (R(w[j - 15], 7)) ^ (R(w[j - 15], 18)) ^ (w[j - 15] >> 3)
s1 = (R(w[j - 2], 17)) ^ (R(w[j - 2], 19)) ^ (w[j - 2] >> 10)
w.append((w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff)
# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))
# 初始化
a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7
# for j in w:
#    print(hex(j)[2:])
for j in range(64):
s0 = R(a, 2) ^ R(a, 13) ^ R(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
t2 = s0 + maj
s1 = R(e, 6) ^ R(e, 11) ^ R(e, 25)
ch = (e & f) ^ ((~e) & g)
t1 = h + s1 + ch + K[j] + w[j]
h = g & 0xffffffff
g = f & 0xffffffff
f = e & 0xffffffff
e = (d + t1) & 0xffffffff
d = c & 0xffffffff
c = b & 0xffffffff
b = a & 0xffffffff
a = (t1 + t2) & 0xffffffff

h0 = (h0 + a) & 0xffffffff
h1 = (h1 + b) & 0xffffffff
h2 = (h2 + c) & 0xffffffff
h3 = (h3 + d) & 0xffffffff
h4 = (h4 + e) & 0xffffffff
h5 = (h5 + f) & 0xffffffff
h6 = (h6 + g) & 0xffffffff
h7 = (h7 + h) & 0xffffffff

digest = (h0 << 224) | (h1 << 192) | (h2 << 160) | (h3 << 128)
digest |= (h4 << 96) | (h5 << 64) | (h6 << 32) | h7
# print(hex(digest)[2:])  # .rjust(32, '0'))
return hex(digest)[2:]  # .rjust(32, '0')
if __name__=="__main__":
aa='你好，中国'.encode('utf-8')
print(Sha256sum(aa))
print(len(Sha256sum(aa)))


消息摘要运行效果：

## 八：RSA公私钥生成

生成公钥与私钥：此步骤按照第五步

明文 M 加密 Me mod n= C
密文 C 解密 Cd mod n=M

#D、E和N的密钥生成
def generatePublicAndSecretKeys(size = 5):
p, q = randPrime(size), randPrime(size) #生成一对不相等且足够大的质数
N = p * q #计算p、q的乘积
f = (p - 1) * (q - 1) #计算n的欧拉函数
e = randGcd1(f) #选出一个与f互质的整数e
d = liyuan(e, f)#计算出e对于f的模反元素d  de mod f =1
keys = {'d' :  d, 'e' : e, 'n' : N} #得出公钥与私钥
return keys


1、选择一对不相等且足够大的质数 p，q

#确定素数
def isPrime(num):
if (num < 2):
return False
else:
i = 2
flag=True
while i < num:
# 如果num能被i整除，说明num不是质数
if num % i == 0:
# 只要num不是质数，将flag的值修改为 False
flag = False
i += 1
return  flag
#大质数生成
def randPrime(n):
rangeStart = 10 ** (n-1) #10**4
rangeEnd = (10 ** n) - 1  #10**5-1
while True:
num = random.randint(rangeStart, rangeEnd)  #返回rangestart到rangeend任意一个数
if isPrime(num): #判断是否是质数，如果是则生成
return num


2、寻找与f互质的整数e,利用欧几里算法，如果值等于1·，那么这两个数互质

#寻找与f互质整数e
def randGcd1(b):
rangeStart = 2
rangeEnd = b - 1
while True:
num = random.randint(rangeStart, rangeEnd)
if oujilide(num, b) == 1: #利用欧几里算法，如果值等于1，那么这个两个数互质
return num
#欧几里得算法
def oujilide(a,b):
if a > b:
x = a
y = b
else:
x = b
y = a
while True:
if y == 0:
return x
else:
r = x % y
x = y
y = r


3、使用扩展欧几里得算法，计算出e对于φ(n)的模反元素d。

#扩展欧几里得算法，求逆元
def liyuan(a, n):
x1, x2, x3 = 1, 0, n
y1, y2, y3 = 0, 1, a
while y3 != 1 and y3 != 0 and y3 > 0:
Q = math.floor(x3 / y3)
t1, t2, t3 = x1 - Q * y1, x2 - Q * y2, x3 - Q * y3
x1, x2, x3 = y1, y2, y3
y1, y2, y3 = t1, t2, t3
if y3 == 0:
return 0
if y3 == 1:
if y2 >0:
return y2
else:
return n+y2


4、SHA256算法得到消息摘要，并哈希值转为整型
将消息摘要进行数字签名与验证

#SHA256算法得到消息摘要
def hashing(M, size = 5):
aa=zy.Sha256sum(M) #得到哈希值
cc=int(aa, 16) % 10 ** (size * 2 - 2)#将哈希值转化为整型
return cc
#将消息摘要进行签名
def signMessage(M, d, N):
s = power(M, d, N) #使用私钥签名 hashM**d mod N 得到签名内容
return s
#将得到
def verifySign(s, e,n):
w = power(s, e, n)
return w



## 九：实现数字签名的整体实现代码

摘要：zaiyao.py

def Sha256sum(message: bytes) -> bytes:
# 定义常量
# 前8个素数2..19的平方根的小数部分的前32位
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h4 = 0x510e527f
h5 = 0x9b05688c
h6 = 0x1f83d9ab
h7 = 0x5be0cd19

# 定义常数K 64
# 前64个素数2..311的立方根的小数部分的前32位
K = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)

# R为循环右移，
# 右移之后可能会超过32位，所以要和0xffffffff做与运算，确保结果为32位。
R = lambda x, n: ((x >> n) | (x << (32 - n))) & 0xffffffff
# 大端  0x12,0x34,0x56,0x78 -> 0x12345678
W = lambda i1, i2, i3, i4: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4

# 对每一个输入先添加一个'0x80'，即'10000000', 即128
ascii_list = list(map(lambda x: x, message))
msg_length = len(ascii_list) * 8
ascii_list.append(128)

# 补充0
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append(0)

# 最后64为存放消息长度，以大端数存放。
# 例如，消息为'a'，则长度为'0x0000000000000008'
for i in range(8):
ascii_list.append(msg_length >> (8 * (7 - i)) & 0xff)

# print(ascii_list)
# print(len(ascii_list)//64)
for i in range(len(ascii_list) // 64):  # 64*8=512bits
# print(ascii_list[i*64:(i+1)*64])
# 每个512bits的块进行循环
w = []
# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组
for j in range(16):
s = i * 64 + j * 4
w.append(W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3]))
for j in range(16, 64):
s0 = (R(w[j - 15], 7)) ^ (R(w[j - 15], 18)) ^ (w[j - 15] >> 3)
s1 = (R(w[j - 2], 17)) ^ (R(w[j - 2], 19)) ^ (w[j - 2] >> 10)
w.append((w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff)
# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))
# 初始化
a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7
# for j in w:
#    print(hex(j)[2:])
for j in range(64):
s0 = R(a, 2) ^ R(a, 13) ^ R(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
t2 = s0 + maj
s1 = R(e, 6) ^ R(e, 11) ^ R(e, 25)
ch = (e & f) ^ ((~e) & g)
t1 = h + s1 + ch + K[j] + w[j]
h = g & 0xffffffff
g = f & 0xffffffff
f = e & 0xffffffff
e = (d + t1) & 0xffffffff
d = c & 0xffffffff
c = b & 0xffffffff
b = a & 0xffffffff
a = (t1 + t2) & 0xffffffff

h0 = (h0 + a) & 0xffffffff
h1 = (h1 + b) & 0xffffffff
h2 = (h2 + c) & 0xffffffff
h3 = (h3 + d) & 0xffffffff
h4 = (h4 + e) & 0xffffffff
h5 = (h5 + f) & 0xffffffff
h6 = (h6 + g) & 0xffffffff
h7 = (h7 + h) & 0xffffffff

digest = (h0 << 224) | (h1 << 192) | (h2 << 160) | (h3 << 128)
digest |= (h4 << 96) | (h5 << 64) | (h6 << 32) | h7
# print(hex(digest)[2:])  # .rjust(32, '0'))
return hex(digest)[2:]  # .rjust(32, '0')
if __name__=="__main__":
aa='你好，中国'.encode('utf-8')
print(Sha256sum(aa))
print(len(Sha256sum(aa)))


RSA公私钥生成：shuziqianming.py

import random
import math
import zhaiyao as zy
#确定素数
def isPrime(num):
if (num < 2):
return False
else:
i = 2
flag=True
while i < num:
# 如果num能被i整除，说明num不是质数
if num % i == 0:
# 只要num不是质数，将flag的值修改为 False
flag = False
i += 1
return  flag
#大质数生成
def randPrime(n):
rangeStart = 10 ** (n-1) #10**4
rangeEnd = (10 ** n) - 1  #10**5-1
while True:
num = random.randint(rangeStart, rangeEnd)  #返回rangestart到rangeend任意一个数
if isPrime(num): #判断是否是质数，如果是则生成
return num
#扩展欧几里得算法，求逆元
def liyuan(a, n):
x1, x2, x3 = 1, 0, n
y1, y2, y3 = 0, 1, a
while y3 != 1 and y3 != 0 and y3 > 0:
Q = math.floor(x3 / y3)
t1, t2, t3 = x1 - Q * y1, x2 - Q * y2, x3 - Q * y3
x1, x2, x3 = y1, y2, y3
y1, y2, y3 = t1, t2, t3
if y3 == 0:
return 0
if y3 == 1:
if y2 >0:
return y2
else:
return n+y2

#寻找与f互质整数e
def randGcd1(b):
rangeStart = 2
rangeEnd = b - 1
while True:
num = random.randint(rangeStart, rangeEnd)
if oujilide(num, b) == 1: #利用欧几里算法，如果值等于1，那么这个两个数互质
return num
#欧几里得算法
def oujilide(a,b):
if a > b:
x = a
y = b
else:
x = b
y = a
while True:
if y == 0:
return x
else:
r = x % y
x = y
y = r
#从数字幂快速搜索模块
def power(x, n, mod):  #x**n mod mod
if n == 0:
return 1
elif n % 2 == 0:
p = power(x, n / 2, mod)
return (p * p) % mod
else:
return (x * power(x, n - 1, mod)) % mod
#D、E和N的密钥生成
def generatePublicAndSecretKeys(size = 5):
p, q = randPrime(size), randPrime(size) #生成一对不相等且足够大的质数
N = p * q #计算p、q的乘积
f = (p - 1) * (q - 1) #计算n的欧拉函数
e = randGcd1(f) #选出一个与f互质的整数e
d = liyuan(e, f)#计算出e对于f的模反元素d  de mod f =1
keys = {'d' :  d, 'e' : e, 'n' : N} #得出公钥与私钥
return keys
#SHA256算法得到消息摘要
def hashing(M, size = 5):
aa=zy.Sha256sum(M) #得到哈希值
cc=int(aa, 16) % 10 ** (size * 2 - 2)#将哈希值转化为整型
return cc
#将消息摘要进行签名
def signMessage(M, d, N):
s = power(M, d, N) #使用私钥签名 hashM**d mod N 得到签名内容
return s
#将得到
def verifySign(s, e,n):
w = power(s, e, n)
return w



## 十：运行截图

1、对汉字的数字签名运行结果

2、对英文hello world 进行数字签名的运行结果

3、对后缀为.png图片生成数字签名的运行结果

4、对后缀为.doc的文档进行数字签名的运行结果

## 十一：整体代码

此处没有写出主函数，如要参考，请参考github

如要参加完整代码、ppt讲解、文档：请参考(纯算法，没有调用库)python实现RSA数字签名代码、ppt、文档.rar

### 最后：如有任何问题，可以私信博主

看到博主编写文章不容易，不点赞，关注，收藏在走吗？？？

展开全文
• 利用Python实现RSA数字签名的产生和验证过程。 任务1：准备一个私钥文件，一个公钥文件，一个数据文件；   任务2：定义一个函数，能够使用指定的私钥对数据文件进行签 名，并将签名结果输出到文件返回；   任务3...

## 利用Python实现RSA数字签名的产生和验证过程。

任务1：准备一个私钥文件，一个公钥文件，一个数据文件；
任务2：定义一个函数，能够使用指定的私钥对数据文件进行签 名，并将签名结果输出到文件返回；
任务3：定义一个函数，能够使用指定的公钥对任务2中的签名    文件进行验证，返回验证结果；
任务4：利用任务1中的文件对任务2和3中的函数进行测试。

## 标准库Hashlib

实现了SHA1，SHA224以及MD5等多个安全哈希算法，

## pycryptodome的使用

• Python扩展库pycryptodome和cryptography,提供了SHA系列算法和其他哈希算法，以及DES,AES,RSA等多个加密算法和签名算法的实现
• pycryptodome是加密库
• pycryptodome模块不是Python的内置模块，pycryptodome模块实现了各种算法和协议的加密模块的结合，结合了各种加密方式对应的多种加密算法的实现，包括单向加密、对称加密以及公钥加密和随机数操作。
区别：
crypto是GPG加解密库，该库已于2016年停更，且pythongnupg库才是最主流的GPG加解密库。
PyCrypto是Python前主流加解密库，但已停更。
PyCryptodome是当前最主流的Python加解密库，如果遇到代码中import Crypto等字样，就需要安装这个库。
安装pycryptodome: pip install pycryptodome

## Crypto.PubicKey.RSA生成私公密钥对

• 在该模块下的RSA.py源代码中提供了直接生成原始密钥的函数，所以要使用Crypto.Random模块生成
from Crypto.PublicKey import RSA

key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()

print(key)
# print(private_key)
# print(public_key)


输出：

Private RSA key at 0x1A81094DF70


用generate函数生成的密钥是一个私钥对象，通过export_key()函数返回这个密钥：

## 2、RSA.import_key获得公私密钥

• 上文已经可以输出一对公私密钥，还要用import_key函数
• 因为publicKey是一个公钥的实例化对象，即public_key的实例化对象，在使用PKCS1_OAEP实例化加密套件时传入的参数必须是这个对象，查阅PKCS1_OAEP.py的源代码也发现传入的参数key应该是一个对象

## 3、RSA加密

from Crypto.PublicKey import RSA

key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()

# print(key)
# print(private_key)
# print(public_key)

from Crypto.Cipher import PKCS1_OAEP

data = b"123456"

publicKey = RSA.import_key(public_key)
# print(publicKey)
# 加密
cipher = PKCS1_OAEP.new(publicKey)
encrypted_data = cipher.encrypt(data)
print(encrypted_data)



输出

b'"9\x8fW+b\x83\xbf\xc7y\xfc3\xb5R@\xf4\xad}\x9a\x11\x81\xf2\x0f\xf1\xc6_\xf0K\xb7\xf7t\xcb\xaevK\x08\x8cg\xa1\x8ai\x94\xdf8\xc6\\\xb5\xb8\x98\xfd}A\xb3\\\xf7\x91J\x88\x84/\xdc\xd8\xe7<\xe4\xfa[\nX\xf5\xf6K\xd9s\x17\x11F\x80~\xcdt$>$R\x86b\x11*m\xeb\xe0\x87,):&g\x98\'\x90\xbf\x8cK\xef\xbb-\xfc\xecF\x93G!\xf7\xff&\x91\xdc\xdcY\xff\xf76\x91\xcb\n\x1a\xb4\xf3\xcep6\xde5\xf0^\xe1\'\xf7\t\xc5\xdbU6\x83\xc7\xf5\xe2\xeb\xf7\xe3;J\xd1\x9b\xd0\x925!\x98s\x189\xdd\xa8=\xbfw^\xe8R\xdc\xce\xb8\xdb\x96 f\x85\xd7\x10>\xea\x97\x05p\x8c\x00\xd5\xf0\xae\xea\x0c\xf9\x03\xd98N\xbc\xd8RD]\x97Y\x8a/\n\xec\xb5\xe5\xe9\x8ea\x87\xaeE\x9fM\xc7\\\x0c\xefE*}\xea\xcd*\x1f\xacR\xd7X\x85\xea\xbfl\xcc\xa5\xed\x91\x90\x07\x13\xdd\x97\x8c\xfcl\xf2y'



加密完成

## 4、RSA解密

# 解密
privateKey = RSA.import_key(private_key)
cipher = PKCS1_OAEP.new(privateKey)
data = cipher.decrypt(encrypted_data)
print(data)



输出

b'123456'


## 三、数字签名与验签

• 数字签名与验签，简单来说步骤为：A使用自己的私钥对消息进行签名，将签名后的信息和公钥发送给B，B使用公钥验证签名是否属于A。
• 使用Crypto.Publickey,Crypto.Hash,Crypto.Signature三个库，即公钥加密、摘要算法、数字签名三个库
• ## 1、Crypto.Hash获取消息的HASH值

• 这里会用到摘要算法，常用的摘要算法有：MD2,MD4,MD5,SHA-1,SHA-256,RIPEMD128,RIPEMD160等等，用什么摘要算法不重要，只要在验证时有相同的摘要算法即可。这里选择MD5.
• ## 2、Crypho.Signature对HASH值使用私钥进行签名

• 本质上是使用私钥对HASH值进行加密，将公钥私钥以及消息分别放在public_key.pem、private_key.pem、data.txt三个文件中,
• 有两个注意:获得公私钥要用import_key函数；从data.txt中读取信息后要使用相应的编码格式，digest=MD5.new(data.encode(‘utf-8’))
• 在Crypto.Signature中随便选择一个模式，选择pkcs1_15模式对消息进行签名
• ## 3、签名验证

• 签名验证部分需要传入三个信息：消息原文（data）、摘要算法（MD5）、HASH值签名结果（signature）

## 实验总代码

from Crypto.PublicKey import RSA

key = RSA.generate(2048)

# 任务1：准备一个私钥文件，一个公钥文件，一个数据文件
private_key = key.export_key()
public_key = key.publickey().export_key()
data = "I love you"
with open("private_key.pem", "wb") as prifile,\
open("public_key.pem", "wb") as pubfile,\
open("data.txt","a") as datafile:
prifile.write(private_key)
pubfile.write(public_key)
datafile.write(data)

from Crypto.Signature import pkcs1_15
from Crypto.Hash import MD5
from Crypto.PublicKey import RSA

# 签名
with open("data.txt", "r") as datafile:

# print(data)

# 任务2：定义签名函数，能够使用指定的私钥对数据文件进行签名，并将签名结果输出到文件返回
def signaturer(private_key, data):
# 获取消息的HASH值，摘要算法MD5，验证时也必须用MD5
digest = MD5.new(data.encode('utf-8'))
# 使用私钥对HASH值进行签名
signature = pkcs1_15.new(private_key).sign(digest)
# 将签名结果写入文件
sig_results = open("sig_results.txt", "wb")
sig_results.write(signature)
sig_results.close()
return sig_results

# 任务3：定义签名验证函数，能够使用指定的公钥对任务2中的签名文件进行验证，返回验证结果
def verifier(public_key, data, signature):
digest = MD5.new(data.encode('utf-8'))
try:
pkcs1_15.new(public_key).verify(digest, signature)
print("验证成功！！！")
except:
print("签名无效！！！")

# 任务4：利用任务1中的文件对任务2和3中的函数进行测试。
with open('private_key.pem') as prifile, \
open('data.txt') as datafile:

signaturer(private_key, data)

with open('public_key.pem') as pubfile, \
open('data.txt') as datafile, \
open('sig_results.txt', 'rb') as sigfile:

verifier(public_key, data, signature)


展开全文
• 程序要求清单： 基本流程： 运行结果： INPUT: ...

程序要求清单：

基本流程：

运行结果：

INPUT:
34862844108815430278935886114814204661242105806196134451262421197958661737288465541172280522822644267285105893266043422314800759306377373320298160258654603531159702663926160107285223145666239673833817786345065431976764139550904726039902450456522584204556470321705267433321819673919640632299889369457498214445

OUTPUT:
Private Key:
N: 78841181099223968401000784537446044237784489958930626859661546319915734535564286843929089858806160927583636785213641674742891604758519466416270196149968424211401434194951250003636471951939037856583335344796681676680421749817561884185156901077848451414919839607481314547384942033488032689776582103680101651419
d: 62155833398861149551836037970138554652072353958920365936071774016793504678445192437067806518338650323999067951432452330136982318568982506703216642632242364544915699439246876432122640429532494207894118612299932412964430445703068916755189092721834765176438536555924699728021363703536949347875046151882728437253
Public Key:
N: 78841181099223968401000784537446044237784489958930626859661546319915734535564286843929089858806160927583636785213641674742891604758519466416270196149968424211401434194951250003636471951939037856583335344796681676680421749817561884185156901077848451414919839607481314547384942033488032689776582103680101651419
e: 54379617782319392288508796983156014528426545469465358516629409491913015829046366478896491813056080903568607614496062598318891266649056224190078944747724625447985747390726774036280359056832185917603499408809384176157027477704342977680394354765523168680279688950215134901092221515962361649676202653429692278117
Signature:
s: 71919406352359937084182187510227490541914896613466645529207649594247552008972305126510851689232143019784944438466693655526353672464782061473850818475550335761134900001275567160947332462719877194559692283550511188231994020480408965265818017774491786539810263037297828665666952885117080533173354443950829657265
Verify s of m:
valid
m’ (faked): 76185632416095212195849362379326949631255492531288160212489433749029013809159477234031442742457342006136621757534807192466386195243552371639270795778065774775711134972235456069060155133423454262578532881646543833960203758700964200167278452490427449581011414060671053975994766090766673869892463027013529590471
invalid
s’ (faked): 64886342123697490001106226310357396118874720615188625977759508709028733843402866228754809027591115956249353484164041076852718802175168533537718549149953512673140432858680493424329023049213294957469230294496190453936529669882382633436019205635629312788163444233181192368101923610587237545646774852698003010422
invalid

代码实现：

import random

# 求最大公约数
def gcd(a, b):
if a < b:
return gcd(b, a)
elif a % b == 0:
return b
else:
return gcd(b, a % b)

# 快速幂+取模
def power(a, b, c):
ans = 1
while b != 0:
if b & 1:
ans = (ans * a) % c
b >>= 1
a = (a * a) % c
return ans

# 快速幂
def quick_power(a: int, b: int) -> int:
ans = 1
while b != 0:
if b & 1:
ans = ans * a
b >>= 1
a = a * a
return ans

# 大素数检测
def Miller_Rabin(n):
a = random.randint(2, n - 2)  # 随机第选取一个a∈[2,n-2]
# print("随机选取的a=%lld\n"%a)
s = 0  # s为d中的因子2的幂次数。
d = n - 1
while (d & 1) == 0:  # 将d中因子2全部提取出来。
s += 1
d >>= 1

x = power(a, d, n)
for i in range(s):  # 进行s次二次探测
newX = power(x, 2, n)
if newX == 1 and x != 1 and x != n - 1:
return False  # 用二次定理的逆否命题，此时n确定为合数。
x = newX

if x != 1:  # 用费马小定理的逆否命题判断，此时x=a^(n-1) (mod n)，那么n确定为合数。
return False

return True  # 用费马小定理的逆命题判断。能经受住考验至此的数，大概率为素数。

# 卢卡斯-莱墨素性检验
def Lucas_Lehmer(num: int) -> bool:  # 快速检验pow(2,m)-1是不是素数
if num == 2:
return True
if num % 2 == 0:
return False
s = 4
Mersenne = pow(2, num) - 1  # pow(2, num)-1是梅森数
for x in range(1, (num - 2) + 1):  # num-2是循环次数，+1表示右区间开
s = ((s * s) - 2) % Mersenne
if s == 0:
return True
else:
return False

# 扩展的欧几里得算法，ab=1 (mod m), 得到a在模m下的乘法逆元b
def Extended_Eulid(a: int, m: int) -> int:
def extended_eulid(a: int, m: int):
if a == 0:  # 边界条件
return 1, 0, m
else:
x, y, gcd = extended_eulid(m % a, a)  # 递归
x, y = y, (x - (m // a) * y)  # 递推关系，左端为上层
return x, y, gcd  # 返回第一层的计算结果。
# 最终返回的y值即为b在模a下的乘法逆元
# 若y为复数，则y+a为相应的正数逆元

n = extended_eulid(a, m)
if n[1] < 0:
return n[1] + m
else:
return n[1]

# 按照需要的bit来生成大素数
def Generate_prime(key_size: int) -> int:
while True:
num = random.randrange(quick_power(2, key_size - 1), quick_power(2, key_size))
if Miller_Rabin(num):
return num

# 生成公钥和私钥
def KeyGen(p: int, q: int):
n = p * q
e = random.randint(1, (p - 1) * (q - 1))
while gcd(e, (p - 1) * (q - 1)) != 1:
e = random.randint(1, (p - 1) * (q - 1))
d = Extended_Eulid(e, (p - 1) * (q - 1))
return n, e, d

def Sign(x: int, d: int, n: int) -> int:
s = power(x, d, n)
return s

def Verify(s: int, e: int, n: int) -> int:
x_ = power(s, e, n)
return x_

if __name__ == '__main__':
key_size = 512
p = Generate_prime(key_size)
q = Generate_prime(key_size)
n, e, d = KeyGen(p, q)

# 消息
x = int(input("Message: "))
if type(x) != int:
raise ValueError("Must be an integer!")
# 签名
s = Sign(x, d, n)
# 验证
x_ = Verify(s, e, n)
Valid = (x_ == x)

# Attack
s_ = random.randint(1, (p - 1) * (q - 1))
m_ = random.randint(1, (p - 1) * (q - 1))

# Output
print("Private Key: ")
print("N: ", n)
print("d: ", d)
print("Public Key: ")
print("N: ", n)
print("e: ", e)
print("Signature: ")
print("s: ", s)
print("Verify s of m: ")
if Valid:
print("valid")
else:
print("invalid")

print("m' (faked): ", m_)
if Verify(m_, s, n) == x:
print("valid")
else:
print("invalid")
print("s' (faked): ", s_)
if Verify(x_, s_, n) == x:
print("valid")
else:
print("invalid")

展开全文
• (纯算法，没有调用库)python实现RSA数字签名代码、ppt、文档
• RSA是目前最有影响力的公钥加密算法，它能够抵抗到目前为止已知的绝大多数密码攻击，已被ISO推荐为公钥数据加密标准，下面通过本文给大家介绍python实现RSA加密(解密)算法，需要的朋友参考下
• 本篇文章主要说明pythonrsa生成密钥对，数据的加密解密，api接口的签名和验签，如有抄袭，请留言联系我。 先安装 pip install rsa 安装好后，请看代码 注意： api签名时：签名用私钥，验签用公钥 数据加密时：...
• 基于Python实现RSA算法，包括的函数有：判断一个数是否为素数、判断两个数是否互为素数、欧几里得算法求最大公约数、产生公私钥、扩展欧几里得算法求模逆、加密明文、解密密文以及代码测试。
• 在这之前 记得用pip install 安装几个包： Crypto，base64， 反正环境缺什么就加什么...4.生成签名之后，再加到请求参数内 即可访问 import hashlib import Crypto from Crypto.PublicKey import RSA from Crypto
• 依旧在做实验之前，对实验中会应用到的一些知识做一个总结~~~~ ...  首先import rsa检查是否有rsa模块，没有的直接pip install rsa进行安装，1s就安好了。   接下来先熟悉一下rsa模块的用法： ...
• OpenSSL和Python实现RSA Key数字签名和验证，基于非对称算法的RSA Key主要有两个用途，数字签名和验证(私钥签名，公钥验证)，以及非对称加解密(公钥加密，私钥解密)。本文提供一个基于OpenSSL命令行和Python数字...
• 这个实验的坑好多，自己也是搞了半天~~ 实验的知识点铺垫（挖坑）请见：文章目录一、实验目的二、实验内容三、实验环境四、实验原理和步骤五...  借助Python的标准库和扩展库，实现数字签名的产生和验证过程。 二...
• RSA算法是一种非对称加密算法，是现在广泛使用的公钥加密算法，主要应用是加密信息和数字签名。维基百科给出的RSA算法简介如下：假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息。她可以用以下的方式来产生...
• 使用HASH256对私钥签名的信息进行加密和验证

...