python实现RSA数字签名(纯算法实现)

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

最后:如有任何问题,可以私信博主

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

请添加图片描述

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:鲸 设计师:meimeiellie 返回首页
评论 2

打赏作者

h1dm

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值