-
2021-12-28 20:05:57
利用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
1、公钥加密
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[\n`X\xf5\xf6K\xd9`s\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: data = datafile.read() # 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: private_key = RSA.import_key(prifile.read()) data = datafile.read() signaturer(private_key, data) with open('public_key.pem') as pubfile, \ open('data.txt') as datafile, \ open('sig_results.txt', 'rb') as sigfile: public_key = RSA.import_key(pubfile.read()) data = datafile.read() signature = sigfile.read() verifier(public_key, data, signature)
更多相关内容 -
编程实现RSA数字签名
2022-06-14 21:54:47一、实验目的:理解RSA数字签名,并运用编程实现RSA数字签名。二、实验过程:1.学习RSA算法及RSA数字签名算法流程。2. RSA数字签名原理:当发送方想要给接收方发送数据,并想进行数字签名的时候,发送方只需要利用...一、实验目的:
理解RSA数字签名,并运用编程实现RSA数字签名。
二、实验过程:
1.学习RSA算法及RSA数字签名算法流程。
2. RSA数字签名原理:当发送方想要给接收方发送数据,并想进行数字签名的时候,发送方只需要利用自己的私钥,对数据进行数字签名算法,就可以得到一个新的签名数据,这时发送方需要把自己原来的数据,以及新得到的签名数据都发送给接收方,接收方接受到签名数据之后,用发送方的公钥对签名数据进行验证算法,看得出来的数据与发送方发送过来的数据是不是完全一样的即可。
三、实验代码及结果:
(1)实验代码:
import random
import math
#快速幂取模
def power(a, b, n): #计算a**b mod n
if b == 0:
return 1 #如果b值为0则返回1
elif b % 2 == 0: # 如果二进制b最后一位为0
p = power(a, b / 2, n) #递归实现
return (p * p) % n #取模运算
else:
return (a * power(a, b - 1, n)) % n #返回结果
# 欧几里得算法求最大公约数
def gcd(a, b):
if a < b:
return gcd(b, a) #如果a小于b,交换两个数
elif a % b == 0:
return b #如果a整除于b则返回b
else:
return gcd(b, a % b) #递归实现欧几里得算法
#确定是否是素数
def isPrime(num):
if (num < 2):
return False #如果num的值小于2返回false
else:
i = 2
flag=True
while i < num: # 如果num能被i整除,说明num不是质数
if num % i == 0:
flag = False # 只要num不是质数,将flag的值修改为 False
i += 1
return flag #最后返回flag的值
#生成大素数函数
def randPrime(n):
Start = 10 ** (n-1) #n的值为5,计算开始值10**4
End = (10 ** n) - 1 #计算结束10**5-1
while True:
num = random.randint(Start, End) #返回从start到end之间任意一个数表示大素数
if isPrime(num): #判断是否是质数,如果是则生成
return num #返回大素数的值num
# 扩展的欧几里得算法,即ab=1 (mod n), 得到a在模n下的乘法逆元b
def Extended_Eulid(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
# 生成公钥和私钥
def KeyGen(p, q): #分别计算n,e,d的值
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, d, n):
s = power(x, d, n)
return s
#利用快速幂取模判断是否有效签名
def Verify(s, e, n):
x_ = power(s, e, n)
return x_
#主函数
if __name__ == '__main__':
key_size = 5
p = randPrime(key_size) #p与q分别为随机生成的大素数
q = randPrime(key_size)
n, e, d = KeyGen(p, q) #用p与q生成公钥和私钥
# 输入消息
x = int(input("请输入加密信息(必须为整数): "))
# 计算签名
s = Sign(x, d, n)
# 验证签名
x_ = Verify(s, e, n)
Valid = (x_ == x)
# 伪造数据攻击
s_ = random.randint(1, (p - 1) * (q - 1))
m_ = random.randint(1, (p - 1) * (q - 1))
# 输出
print("私钥: ")
print("N: ", n)
print("d: ", d)
print("公钥: ")
print("N: ", n)
print("e: ", e)
print("签名: ")
print("s: ", s)
print("验证m的签名: ")
if Valid:
print("签名有效")
else:
print("签名无效")
print("m'(伪): ", m_)
if Verify(m_, s, n) == x:
print("签名有效")
else:
print("签名无效")
print("s' (伪): ", s_)
if Verify(x_, s_, n) == x:
print("签名有效")
else:
print("签名无效")
import random import math #快速幂取模 def power(a, b, n): #计算a**b mod n if b == 0: return 1 #如果b值为0则返回1 elif b % 2 == 0: # 如果二进制b最后一位为0 p = power(a, b / 2, n) #递归实现 return (p * p) % n #取模运算 else: return (a * power(a, b - 1, n)) % n #返回结果 # 欧几里得算法求最大公约数 def gcd(a, b): if a < b: return gcd(b, a) #如果a小于b,交换两个数 elif a % b == 0: return b #如果a整除于b则返回b else: return gcd(b, a % b) #递归实现欧几里得算法 #确定是否是素数 def isPrime(num): if (num < 2): return False #如果num的值小于2返回false else: i = 2 flag=True while i < num: # 如果num能被i整除,说明num不是质数 if num % i == 0: flag = False # 只要num不是质数,将flag的值修改为 False i += 1 return flag #最后返回flag的值 #生成大素数函数 def randPrime(n): Start = 10 ** (n-1) #n的值为5,计算开始值10**4 End = (10 ** n) - 1 #计算结束10**5-1 while True: num = random.randint(Start, End) #返回从start到end之间任意一个数表示大素数 if isPrime(num): #判断是否是质数,如果是则生成 return num #返回大素数的值num # 扩展的欧几里得算法,即ab=1 (mod n), 得到a在模n下的乘法逆元b def Extended_Eulid(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 # 生成公钥和私钥 def KeyGen(p, q): #分别计算n,e,d的值 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, d, n): s = power(x, d, n) return s #利用快速幂取模判断是否有效签名 def Verify(s, e, n): x_ = power(s, e, n) return x_ #主函数 if __name__ == '__main__': key_size = 5 p = randPrime(key_size) #p与q分别为随机生成的大素数 q = randPrime(key_size) n, e, d = KeyGen(p, q) #用p与q生成公钥和私钥 # 输入消息 x = int(input("请输入加密信息(必须为整数): ")) # 计算签名 s = Sign(x, d, n) # 验证签名 x_ = Verify(s, e, n) Valid = (x_ == x) # 伪造数据攻击 s_ = random.randint(1, (p - 1) * (q - 1)) m_ = random.randint(1, (p - 1) * (q - 1)) # 输出 print("私钥: ") print("N: ", n) print("d: ", d) print("公钥: ") print("N: ", n) print("e: ", e) print("签名: ") print("s: ", s) print("验证m的签名: ") if Valid: print("签名有效") else: print("签名无效") print("m'(伪): ", m_) if Verify(m_, s, n) == x: print("签名有效") else: print("签名无效") print("s' (伪): ", s_) if Verify(x_, s_, n) == x: print("签名有效") else: print("签名无效")
(2)实验结果:
-
rsa数字签名
2013-06-03 15:35:17一个基于RSA的数字签名程序源代码,简单实现了数字签名功能 -
数字签名算法,c++实现,RSA的算法
2010-12-23 10:10:56包涵三个RSA算法,c++是实现,数字签名的合集,三个独自的程序,可以独自编译运行,VC6.0下编译 -
RSA数字签名源程序
2012-08-01 14:13:25RSA数字签名源程序,可直接使用,调试方法请看readme文件。适合课程实验所用。 -
mbedtls | 09 - 数字签名算法的配置与使用(RSA数字签名算法、ECDSA数字签名算法)
2020-10-03 19:40:45mbedtls系列文章 mbedtls | 01 - 移植mbedtls库到STM32的两种方法 mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用 mbedtls | 03 - 单向散列算法...mbedtls | 06 - 非对称加密算法的配置与使用(RSA算法) mbembedtls系列文章
- mbedtls | 01 - 移植mbedtls库到STM32的两种方法
- mbedtls | 02 - 伪随机数生成器(ctr_drbg)的配置与使用
- mbedtls | 03 - 单向散列算法的配置与使用(MD5、SHA1、SHA256、SHA512)
- mbedtls | 04 - 对称加密算法的配置与使用(AES算法)
- mbedtls | 05 - 消息认证码的配置与使用(HMAC算法、GCM算法)
- mbedtls | 06 - 非对称加密算法的配置与使用(RSA算法)
- mbedtls | 07 - DH秘钥协商算法的配置与使用
- mbedtls | 08 - ECDH秘钥协商算法的配置与使用
Demo工程源码
本工程基于STM32L41RCT6开发板,包含了本系列文章中所编写的所有Demo,持续更新……
文章目录
一、数字签名算法
1. 什么是数字签名
数字签名类似于盖章和签字,数字签名可以检查消息是否被篡改、并验证消息的可靠性,因为私钥只有签名者持有,所以还可以防止否认。
2. 数字签名算法
① RSA数字签名
RSA数字签名算法基于RSA非对称加密算法,在用于数字签名时公钥和私钥的用法刚好相反:
- 发送方使用私钥对消息执行加密操作 = 对消息进行签名;
- 接收方使用公钥对签名进行解密 = 对签名进行认证;
RSA签名算法还需要包括填充方法,有两种:PKCS1-V1.5和PSS(使用随机数填充,推荐使用)。
② DSA算法
③ ECDSA算法
在相同的安全等级下,ECDSA算法具有秘钥短、执行效率高的特点,更适合物联网应用。
二、RSA数字签名功能模块的配置与使用
1. 配置宏
使用RSA功能需要提前开启伪随机数生成器(依赖AES算法、SHA256算法、MD通用接口),其中伪随机数生成器的硬件适配移植实现已经讲述,请参考对应的第二篇博客,不再赘述。
综合上述,本实验中需要开启的宏如下表:
宏定义 功能 MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES 不使用默认熵源(如果已有、要屏蔽该宏) MBEDTLS_NO_PLATFORM_ENTROPY 不使用系统内置熵源 MBEDTLS_AES_C 使用AES算法 MBEDTLS_ENTROPY_C 使能熵源模块 MBEDTLS_CTR_DRBG_C 使能随机数模块 MBEDTLS_SHA256_C 使能SHA256算法 MBEDTLS_MD_C 开启MD通用接口 MBEDTLS_AES_ROM_TABLES 使能预定义S盒(节约内存空间) MBEDTLS_BIGNUM_C 开启大数运算 MBEDTLS_GENPRIME 开启生成素数 MBEDTLS_OID_C 开启OID数据结构模块 MBEDTLS_RSA_C 开启RSA算法 MBEDTLS_PKCS1_V21 开启PKCS#1 v2.1填充方案(OAEP) 新建配置文件
mbedtls_config_rsa_sign.h
,编写以下内容:/** * @brief Minimal configuration for RSA Sign Function * @author mculover666 * @date 2020/10/03 */ #ifndef _MBEDTLS_CONFIG_RSA_SIGN_H_ #define _MBEDTLS_CONFIG_RSA_SIGN_H_ /* System support */ #define MBEDTLS_HAVE_ASM //#define MBEDTLS_HAVE_TIME /* mbed feature support */ #define MBEDTLS_ENTROPY_HARDWARE_ALT //#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES #define MBEDTLS_NO_PLATFORM_ENTROPY /* mbed modules */ #define MBEDTLS_AES_C #define MBEDTLS_AES_ROM_TABLES #define MBEDTLS_CTR_DRBG_C #define MBEDTLS_ENTROPY_C #define MBEDTLS_SHA256_C #define MBEDTLS_MD_C #define MBEDTLS_BIGNUM_C #define MBEDTLS_GENPRIME #define MBEDTLS_OID_C #define MBEDTLS_RSA_C #define MBEDTLS_PKCS1_V21 #include "mbedtls/check_config.h" #endif /* _MBEDTLS_CONFIG_RSA_SIGN_H_ */
在MDK中设置使用该配置文件:
2. RSA数字签名功能模块API说明
使用该功能模块需要包含头文件:
#include "mbedtls/rsa.h"
① RSA签名接口
/** * \brief This function performs a private RSA operation to sign * a message digest using PKCS#1. * * It is the generic wrapper for performing a PKCS#1 * signature using the \p mode from the context. * * \note The \p sig buffer must be as large as the size * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used. * * \note For PKCS#1 v2.1 encoding, see comments on * mbedtls_rsa_rsassa_pss_sign() for details on * \p md_alg and \p hash_id. * * \deprecated It is deprecated and discouraged to call this function * in #MBEDTLS_RSA_PUBLIC mode. Future versions of the library * are likely to remove the \p mode argument and have it * implicitly set to #MBEDTLS_RSA_PRIVATE. * * \note Alternative implementations of RSA need not support * mode being set to #MBEDTLS_RSA_PUBLIC and might instead * return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED. * * \param ctx The initialized RSA context to use. * \param f_rng The RNG function to use. If the padding mode is PKCS#1 v2.1, * this must be provided. If the padding mode is PKCS#1 v1.5 and * \p mode is #MBEDTLS_RSA_PRIVATE, it is used for blinding * and should be provided; see mbedtls_rsa_private() for more * more. It is ignored otherwise. * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL * if \p f_rng is \c NULL or doesn't need a context argument. * \param mode The mode of operation. This must be either * #MBEDTLS_RSA_PRIVATE or #MBEDTLS_RSA_PUBLIC (deprecated). * \param md_alg The message-digest algorithm used to hash the original data. * Use #MBEDTLS_MD_NONE for signing raw data. * \param hashlen The length of the message digest. * Ths is only used if \p md_alg is #MBEDTLS_MD_NONE. * \param hash The buffer holding the message digest or raw data. * If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable * buffer of length \p hashlen Bytes. If \p md_alg is not * #MBEDTLS_MD_NONE, it must be a readable buffer of length * the size of the hash corresponding to \p md_alg. * \param sig The buffer to hold the signature. This must be a writable * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes * for an 2048-bit RSA modulus. A buffer length of * #MBEDTLS_MPI_MAX_SIZE is always safe. * * \return \c 0 if the signing operation was successful. * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. */ int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int mode, mbedtls_md_type_t md_alg, unsigned int hashlen, const unsigned char *hash, unsigned char *sig );
② RSA验证签名接口
/** * \brief This function performs a public RSA operation and checks * the message digest. * * This is the generic wrapper for performing a PKCS#1 * verification using the mode from the context. * * \note For PKCS#1 v2.1 encoding, see comments on * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and * \p hash_id. * * \deprecated It is deprecated and discouraged to call this function * in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library * are likely to remove the \p mode argument and have it * set to #MBEDTLS_RSA_PUBLIC. * * \note Alternative implementations of RSA need not support * mode being set to #MBEDTLS_RSA_PRIVATE and might instead * return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED. * * \param ctx The initialized RSA public key context to use. * \param f_rng The RNG function to use. If \p mode is #MBEDTLS_RSA_PRIVATE, * this is used for blinding and should be provided; see * mbedtls_rsa_private() for more. Otherwise, it is ignored. * \param p_rng The RNG context to be passed to \p f_rng. This may be * \c NULL if \p f_rng is \c NULL or doesn't need a context. * \param mode The mode of operation. This must be either * #MBEDTLS_RSA_PUBLIC or #MBEDTLS_RSA_PRIVATE (deprecated). * \param md_alg The message-digest algorithm used to hash the original data. * Use #MBEDTLS_MD_NONE for signing raw data. * \param hashlen The length of the message digest. * This is only used if \p md_alg is #MBEDTLS_MD_NONE. * \param hash The buffer holding the message digest or raw data. * If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable * buffer of length \p hashlen Bytes. If \p md_alg is not * #MBEDTLS_MD_NONE, it must be a readable buffer of length * the size of the hash corresponding to \p md_alg. * \param sig The buffer holding the signature. This must be a readable * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes * for an 2048-bit RSA modulus. * * \return \c 0 if the verify operation was successful. * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. */ int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int mode, mbedtls_md_type_t md_alg, unsigned int hashlen, const unsigned char *hash, const unsigned char *sig );
3. 编写测试函数
新建文件
mbedtls_rsa_sign_test.c
,编写以下测试内容:/** * @brief RSA Sign Function demo * @author mculover666 * @date 2020/10/03 */ #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif #if defined(MBEDTLS_RSA_C) #include <stdio.h> #include "string.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/rsa.h" static char buf[516]; static void dump_rsa_key(mbedtls_rsa_context *ctx) { size_t olen; printf("\n +++++++++++++++++ rsa keypair +++++++++++++++++\n\n"); mbedtls_mpi_write_string(&ctx->N , 16, buf, sizeof(buf), &olen); printf("N: %s\n", buf); mbedtls_mpi_write_string(&ctx->E , 16, buf, sizeof(buf), &olen); printf("E: %s\n", buf); mbedtls_mpi_write_string(&ctx->D , 16, buf, sizeof(buf), &olen); printf("D: %s\n", buf); mbedtls_mpi_write_string(&ctx->P , 16, buf, sizeof(buf), &olen); printf("P: %s\n", buf); mbedtls_mpi_write_string(&ctx->Q , 16, buf, sizeof(buf), &olen); printf("Q: %s\n", buf); mbedtls_mpi_write_string(&ctx->DP, 16, buf, sizeof(buf), &olen); printf("DP: %s\n", buf); mbedtls_mpi_write_string(&ctx->DQ, 16, buf, sizeof(buf), &olen); printf("DQ: %s\n", buf); mbedtls_mpi_write_string(&ctx->QP, 16, buf, sizeof(buf), &olen); printf("QP: %s\n", buf); printf("\n +++++++++++++++++ rsa keypair +++++++++++++++++\n\n"); } static void dump_buf(uint8_t *buf, uint32_t len) { int i; for (i = 0; i < len; i++) { printf("%s%02X%s", i % 16 == 0 ? "\r\n\t" : " ", buf[i], i == len - 1 ? "\r\n" : ""); } } static uint8_t output_buf[2048/8]; int mbedtls_rsa_sign_test(void) { int ret; const char* msg = "HelloWorld"; const char *pers = "rsa_sign_test"; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_rsa_context ctx; /* 1. init structure */ mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); /* 2. update seed with we own interface ported */ printf( "\n . Seeding the random number generator..." ); ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers)); if(ret != 0) { printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* 3. generate an RSA keypair */ printf( "\n . Generate RSA keypair..." ); ret = mbedtls_rsa_gen_key(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537); if(ret != 0) { printf( " failed\n ! mbedtls_rsa_gen_key returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* shwo RSA keypair */ dump_rsa_key(&ctx); /* 4. sign */ printf( "\n . RSA pkcs1 sign..." ); ret = mbedtls_rsa_pkcs1_sign(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf); if(ret != 0) { printf( " failed\n ! mbedtls_rsa_pkcs1_sign returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* show sign result */ dump_buf(output_buf, sizeof(output_buf)); /* 5. verify sign*/ printf( "\n . RSA pkcs1 verify..." ); ret = mbedtls_rsa_pkcs1_verify(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf); if(ret != 0) { printf( " failed\n ! mbedtls_rsa_pkcs1_encrypt returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); exit: /* 5. release structure */ mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); mbedtls_rsa_free(&ctx); return ret; } #endif /* MBEDTLS_RSA_C */
4. 测试结果
在main.c中声明该测试函数:
extern int mbedtls_rsa_sign_test(void);
在main函数中调用该测试函数:
/* 8. rsa sign test */ mbedtls_rsa_sign_test();
编译、下载,在串口助手中查看测试结果:
① 生成秘钥对成功(需要几min的时间):
② 生成签名和验证签名成功:
三、ECDSA数字签名功能模块的配置与使用
1. 配置宏
使用ECDSA秘钥协商功能需要提前开启伪随机数生成器(依赖AES算法、SHA256算法、MD通用接口),其中伪随机数生成器的硬件适配移植实现已经讲述,请参考对应的第二篇博客,不再赘述。
综合上述,本实验中需要开启的宏如下表:
宏定义 功能 MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES 不使用默认熵源(如果已有、要屏蔽该宏) MBEDTLS_NO_PLATFORM_ENTROPY 不使用系统内置熵源 MBEDTLS_AES_C 使用AES算法 MBEDTLS_ENTROPY_C 使能熵源模块 MBEDTLS_CTR_DRBG_C 使能随机数模块 MBEDTLS_SHA256_C 使能SHA256算法 MBEDTLS_MD_C 开启MD通用接口 MBEDTLS_AES_ROM_TABLES 使能预定义S盒(节约内存空间) MBEDTLS_BIGNUM_C 开启大数运算 MBEDTLS_ECP_C 开启椭圆曲线基础运算 MBEDTLS_ECP_DP_SECP256R1_ENABLED 选择secp256r1曲线参数 MBEDTLS_ECDSA_C 开启ECDSA算法 MBEDTLS_ASN1_WRITE_C 开启ASN1格式写入 MBEDTLS_ASN1_PARSE_C 开启ASN1结构解析 下面补充几个一个第一次出现宏的定义。
① MBEDTLS_ECDSA_C
/** * \def MBEDTLS_ECDSA_C * * Enable the elliptic curve DSA library. * * Module: library/ecdsa.c * Caller: * * This module is used by the following key exchanges: * ECDHE-ECDSA * * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C */ #define MBEDTLS_ECDSA_C
② MBEDTLS_ASN1_WRITE_C
/** * \def MBEDTLS_ASN1_WRITE_C * * Enable the generic ASN1 writer. * * Module: library/asn1write.c * Caller: library/ecdsa.c * library/pkwrite.c * library/x509_create.c * library/x509write_crt.c * library/x509write_csr.c */ #define MBEDTLS_ASN1_WRITE_C
③ MBEDTLS_ASN1_PARSE_C
/** * \def MBEDTLS_ASN1_PARSE_C * * Enable the generic ASN1 parser. * * Module: library/asn1.c * Caller: library/x509.c * library/dhm.c * library/pkcs12.c * library/pkcs5.c * library/pkparse.c */ #define MBEDTLS_ASN1_PARSE_C
新建配置文件
mbedtls_config_ecdsa.h
,编写以下内容:/** * @brief Minimal configuration for ECDSA Function * @author mculover666 * @date 2020/10/03 */ #ifndef _MBEDTLS_CONFIG_ECDSA_H_ #define _MBEDTLS_CONFIG_ECDSA_H_ /* System support */ #define MBEDTLS_HAVE_ASM //#define MBEDTLS_HAVE_TIME /* mbed feature support */ #define MBEDTLS_ENTROPY_HARDWARE_ALT //#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES #define MBEDTLS_NO_PLATFORM_ENTROPY /* mbed modules */ #define MBEDTLS_AES_C #define MBEDTLS_AES_ROM_TABLES #define MBEDTLS_CTR_DRBG_C #define MBEDTLS_ENTROPY_C #define MBEDTLS_SHA256_C #define MBEDTLS_MD_C #define MBEDTLS_BIGNUM_C #define MBEDTLS_ECP_C #define MBEDTLS_ECP_DP_SECP256R1_ENABLED #define MBEDTLS_ECDSA_C #define MBEDTLS_ASN1_WRITE_C #define MBEDTLS_ASN1_PARSE_C #include "mbedtls/check_config.h" #endif /* _MBEDTLS_CONFIG_ECDSA_H_ */
在MDK中配置使用该配置文件:
2. ECDSA签名功能API说明
① 初始化ECDSA结构体:
/** * \brief This function initializes an ECDSA context. * * \param ctx The ECDSA context to initialize. * This must not be \c NULL. */ void mbedtls_ecdsa_init( mbedtls_ecdsa_context *ctx );
② ECDSA生成秘钥接口:
/** * \brief This function generates an ECDSA keypair on the given curve. * * \see ecp.h * * \param ctx The ECDSA context to store the keypair in. * This must be initialized. * \param gid The elliptic curve to use. One of the various * \c MBEDTLS_ECP_DP_XXX macros depending on configuration. * \param f_rng The RNG function to use. This must not be \c NULL. * \param p_rng The RNG context to be passed to \p f_rng. This may be * \c NULL if \p f_rng doesn't need a context argument. * * \return \c 0 on success. * \return An \c MBEDTLS_ERR_ECP_XXX code on failure. */ int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
③ ECDSA签名接口:
/** * \brief This function computes the ECDSA signature of a * previously-hashed message. * * \note The deterministic version implemented in * mbedtls_ecdsa_sign_det() is usually preferred. * * \note If the bitlength of the message hash is larger than the * bitlength of the group order, then the hash is truncated * as defined in <em>Standards for Efficient Cryptography Group * (SECG): SEC1 Elliptic Curve Cryptography</em>, section * 4.1.3, step 5. * * \see ecp.h * * \param grp The context for the elliptic curve to use. * This must be initialized and have group parameters * set, for example through mbedtls_ecp_group_load(). * \param r The MPI context in which to store the first part * the signature. This must be initialized. * \param s The MPI context in which to store the second part * the signature. This must be initialized. * \param d The private signing key. This must be initialized. * \param buf The content to be signed. This is usually the hash of * the original data to be signed. This must be a readable * buffer of length \p blen Bytes. It may be \c NULL if * \p blen is zero. * \param blen The length of \p buf in Bytes. * \param f_rng The RNG function. This must not be \c NULL. * \param p_rng The RNG context to be passed to \p f_rng. This may be * \c NULL if \p f_rng doesn't need a context parameter. * * \return \c 0 on success. * \return An \c MBEDTLS_ERR_ECP_XXX * or \c MBEDTLS_MPI_XXX error code on failure. */ int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s, const mbedtls_mpi *d, const unsigned char *buf, size_t blen, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
④ ECDSA验证签名接口
/** * \brief This function verifies the ECDSA signature of a * previously-hashed message. * * \note If the bitlength of the message hash is larger than the * bitlength of the group order, then the hash is truncated as * defined in <em>Standards for Efficient Cryptography Group * (SECG): SEC1 Elliptic Curve Cryptography</em>, section * 4.1.4, step 3. * * \see ecp.h * * \param grp The ECP group to use. * This must be initialized and have group parameters * set, for example through mbedtls_ecp_group_load(). * \param buf The hashed content that was signed. This must be a readable * buffer of length \p blen Bytes. It may be \c NULL if * \p blen is zero. * \param blen The length of \p buf in Bytes. * \param Q The public key to use for verification. This must be * initialized and setup. * \param r The first integer of the signature. * This must be initialized. * \param s The second integer of the signature. * This must be initialized. * * \return \c 0 on success. * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature * is invalid. * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX * error code on failure for any other reason. */ int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp, const unsigned char *buf, size_t blen, const mbedtls_ecp_point *Q, const mbedtls_mpi *r, const mbedtls_mpi *s);
⑤ 释放ECDSA结构体:
/** * \brief This function frees an ECDSA context. * * \param ctx The ECDSA context to free. This may be \c NULL, * in which case this function does nothing. If it * is not \c NULL, it must be initialized. */ void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx );
3. 编写测试函数
新建文件
mbedtls_ecdsa_test
,编写以下测试函数:/** * @brief ECDSA Function demo * @author mculover666 * @date 2020/10/03 */ #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif #if defined(MBEDTLS_ECDSA_C) #include <stdio.h> #include "string.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/ecdsa.h" uint8_t buf[97]; static void dump_buf(uint8_t *buf, uint32_t len) { int i; for (i = 0; i < len; i++) { printf("%s%02X%s", i % 16 == 0 ? "\r\n\t" : " ", buf[i], i == len - 1 ? "\r\n" : ""); } } int mbedtls_ecdsa_test(void) { int ret; size_t qlen, dlen; size_t rlen, slen; uint8_t hash[32]; const char *msg = "HelloWorld"; const char *pers = "ecdsa_test"; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_mpi r, s; mbedtls_ecdsa_context ctx; mbedtls_md_context_t md_ctx; /* 1. init structure */ mbedtls_md_init(&md_ctx); mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); mbedtls_mpi_init(&r); mbedtls_mpi_init(&s); /* 2. update seed with we own interface ported */ printf( "\n . Seeding the random number generator..." ); ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers)); if(ret != 0) { printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* 3. hash message */ printf( "\n . Hash message..." ); ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), (uint8_t *)msg, strlen(msg), hash); if(ret != 0) { printf( " failed\n ! mbedtls_md returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* show hash */ dump_buf(hash, sizeof(hash)); /* 4. generate keypair */ printf( "\n . Generate ecdsa keypair..." ); ret = mbedtls_ecdsa_genkey(&ctx, MBEDTLS_ECP_DP_SECP256R1, mbedtls_ctr_drbg_random, &ctr_drbg); if(ret != 0) { printf( " failed\n ! mbedtls_ecdsa_genkey returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* show keypair */ mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &qlen, buf, sizeof(buf)); dlen = mbedtls_mpi_size(&ctx.d); mbedtls_mpi_write_binary(&ctx.d, buf + qlen, dlen); dump_buf(buf, qlen + dlen); /* 5. ecdsa sign */ printf( "\n . ECDSA sign..." ); ret = mbedtls_ecdsa_sign(&ctx.grp, &r, &s, &ctx.d, hash, sizeof(hash), mbedtls_ctr_drbg_random, &ctr_drbg); if(ret != 0) { printf( " failed\n ! mbedtls_ecdsa_sign returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); /* show sign */ rlen = mbedtls_mpi_size(&r); slen = mbedtls_mpi_size(&s); mbedtls_mpi_write_binary(&r, buf, rlen); mbedtls_mpi_write_binary(&s, buf + rlen, slen); dump_buf(buf, rlen + slen); /* 6. ecdsa verify */ printf( "\n . ECDSA verify..." ); ret = mbedtls_ecdsa_verify(&ctx.grp, hash, sizeof(hash), &ctx.Q, &r, &s); if(ret != 0) { printf( " failed\n ! mbedtls_ecdsa_verify returned %d(-0x%04x)\n", ret, -ret); goto exit; } printf( " ok\n" ); exit: /* 7. release structure */ mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); mbedtls_mpi_free(&r); mbedtls_mpi_free(&s); mbedtls_md_free(&md_ctx); mbedtls_ecdsa_free(&ctx); return ret; } #endif /* MBEDTLS_DHM_C */
4. 测试结果
在main.c中声明该函数:
extern int mbedtls_ecdsa_test(void);
在main函数中调用该函数:
/* 9. ecdsa sign test */ mbedtls_ecdsa_test();
编译、下载,在串口助手中查看结果:
接收精彩文章及资源推送,请订阅我的微信公众号:『mculover666』。
-
基于BLS的盲签名与基于RSA的盲签名的实现与比较
2020-09-21 20:34:16这里写自定义目录标题利用PBC库实现盲签名 利用PBC库实现盲签名 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法...基于BLS的盲签名与基于RSA的盲签名的实现与比较
1. 利用PBC库实现基于BLS的盲签名
BLS(Boneh-Lynn-Shacham) 签名算法是一种可以实现签名聚合和密钥聚合的算法(即可以将多个密钥聚合成一把密钥,将多个签名聚合成一个签名)。可以通过这篇(理解BLS签名算法)CSDN来理解BLS签名。
PBC库(The Pairing-Based Cryptography Library)可以实现基于双线性对的密码编程,关于PBC库的细节可以参考我的PBC Library专栏。
盲签名算法如下图所示,其中 H ˉ ( ) \bar H() Hˉ()表示BLS哈希, r i r_i ri是从Zp群随机选择的元素。
在单个程序(不区分客户端和服务器端,不进行消息传递)中实现盲签名代码:
代码中多了一步操作,将文件块按照1KB大小进行了分块。#include <iostream> #include <fstream> // for pbc library #include "/usr/local/include/pbc/pbc.h" // for encrypt #include <openssl/md5.h> #include <openssl/sha.h> // for gmp library #include <gmp.h> using namespace std; // 全局参数 char source[] = "sourceFiles/"; #define PARAM "a.param" pairing_t pairing; element_t public_key; // 密钥服务器的公钥 element_t secret_key; // 密钥服务器的私玥 element_t g; // 系统参数g,生成元 int initPairing(){ // 读param参数文件 string s = PARAM; ifstream in(s.c_str()); istreambuf_iterator<char> beg(in), end; string strdata(beg, end); in.close(); // cout <<strdata<<endl; if(pairing_init_set_str(pairing, strdata.c_str())){ // 初始化pairing cout<<"failed"<<endl; } element_init_G2(g, pairing); element_init_G2(public_key, pairing); element_init_Zr(secret_key, pairing); // 生成系统参数g element_random(g); // element_printf("system parameter g = %B\n", g); // 生成私玥 element_random(secret_key); // element_printf("private key = %B\n", secret_key); // compute corresponding public key element_pow_zn(public_key, g, secret_key); // element_printf("public key = %B\n", public_key); return 0; } /* * 将文件块的hash值映射到群上 */ int BLSHash(element_t h, char *behindHash, int hashLen){ element_init_G1(h, pairing); element_from_hash(h, behindHash, hashLen); return 0; } int sigAndCheck256(char *databuf, long size){ int hash_length = SHA256_DIGEST_LENGTH; unsigned char digest[hash_length]; SHA256((unsigned char*)databuf, size, (unsigned char*)&digest); element_t h; BLSHash(h, (char*)digest, hash_length); element_t r; element_init_Zr(r, pairing); element_random(r); // h*g^r盲化 element_t h_blind; element_init_G1(h_blind, pairing); element_t blindFactor; element_init_G2(blindFactor, pairing); element_pow_zn(blindFactor, g, r); element_mul(h_blind, h, blindFactor); element_t h_blind_sig; element_init_G1(h_blind_sig, pairing); element_pow_zn(h_blind_sig, h_blind, secret_key); // 去盲化 element_t h_sig; // 去盲化后 element_init_G1(h_sig, pairing); element_t neg_r; element_t KSpk_negr; element_init_Zr(neg_r, pairing); element_init_G2(KSpk_negr, pairing); element_neg(neg_r, r); element_pow_zn(KSpk_negr, public_key, neg_r); // y^-r element_mul(h_sig, h_blind_sig, KSpk_negr); // 去盲化过程 // 验证 element_t temp1, temp2; element_init_GT(temp1, pairing); element_init_GT(temp2, pairing); element_pairing(temp1, h_sig, g); element_pairing(temp2, h, public_key); if(!element_cmp(temp1, temp2)){ printf("success\n"); return 1; }else{ printf("failed\n"); return 0; } } int readFile(string filename, long size){ string fullPath = source +filename; fstream in; in.open(fullPath.c_str(), ios::in|ios::binary); if(!in){ cout<<"error!"<<endl; } int temp = in.tellg(); in.seekg(0, ios_base::end); long filelen = in.tellg(); in.seekg(temp); int n; int lastLen; if((filelen % size) == 0){ // 整数倍时 n = filelen / size; lastLen = size; }else{ n = filelen / size; lastLen = filelen % size + size; } char *databuf = new char[size]; for(int i=0;i<n-1;i++){ // 前n-1个块 in.read(databuf,size*sizeof(char)); sigAndCheck256(databuf, size); } char *databufLast = new char[lastLen]; in.read(databufLast,lastLen*sizeof(char)); sigAndCheck256(databufLast, lastLen); return 0; } int main() { initPairing(); readFile("4k.txt", 1024); return 0; }
2. 利用Openssl库实现基于RSA的盲签名
基于RSA的盲签名思想与上面的思想类似,假设Alice想让Bob给她的文件m进行盲签名。
(1)Bob作为服务器有自己的公私钥,公钥为 ( n , e ) (n, e) (n,e),私钥为 ( n , d ) (n, d) (n,d)。
(2)Alice首先使用Hash函数对自己的消息m进行哈希得 h ( m ) h(m) h(m)。
(3)盲化处理。Alice选择一个随机数 k k k,生成盲化后的消息 m ′ = h ( m ) k e m o d n m' = h(m) k^e mod\ n m′=h(m)kemod n,然后将 m ′ m' m′发送给Bob。
(4)Bob收到后直接在收到的消息上进行签名, S ′ = S i g n ( m ′ ) S' = Sign(m') S′=Sign(m′),然后将签名 S ′ S' S′返回给Alice。
(5)去盲化。将(3)中的式子带入(4)中得, S ′ = h d ( m ) k m o d n S'=h^d(m)\ k \ mod\ n S′=hd(m) k mod n,只需要再把k去掉即可,即 S = S ′ k − 1 S=S' \ k^{-1} S=S′ k−1,其中 k − 1 k^{-1} k−1表示k在模n意义下的逆。
(6)签名验证。验证 S e S^e Se是否与 h ( m ) h(m) h(m)相等。#include <iostream> #include <string.h> #include <fstream> #include <openssl/bn.h> #include <openssl/evp.h> #include <openssl/rsa.h> #include <openssl/sha.h> using namespace std; // 全局变量 char source[] = "sourceFiles/"; RSA *r; BIGNUM *bne; unsigned long e = RSA_F4; // RSA公钥指数 int bits = 2048; // RSA密钥长度 int KeyGen(){ int ret; bne = BN_new(); ret = BN_set_word(bne, e); r = RSA_new(); ret = RSA_generate_key_ex(r, bits, bne, NULL); // 生成RSA密钥 if (ret != 1) { printf("RSA_generate_key_ex err!\n"); return -1; } else { // printf("生成了RSA密钥。\n"); } return 0; } int blindSig(char *databuf, long size){ // 对消息message进行sha256 hash,得到hm unsigned char hm[SHA256_DIGEST_LENGTH + 1]; SHA256((unsigned char*)databuf, size, (unsigned char*) hm); // for (int i = 0; i < 32; i++) { // printf("%02X", hm[i]); // } // printf("\n"); BIGNUM *nn; // 模数 nn = r->n; // 生成随机数k BIGNUM *k; // 随机数k k = BN_new(); BN_rand_range(k, nn); // 选取小于模数的随机数k BN_CTX*ctx; ctx = BN_CTX_new(); // 盲化因子blindF = k ^ bne (mod nn) // 相当于使用公钥加密k BIGNUM *blind; blind = BN_new(); BN_mod_exp(blind, k, bne, nn, ctx); // 将hm转为BN bn_hm BIGNUM *bn_hm; bn_hm = BN_new(); BN_bin2bn(hm, SHA256_DIGEST_LENGTH, bn_hm); // BN_print_fp(stdout, bn_hm); // printf("\n"); // 对bn_hm进行盲化 m_blind = bn_hm * blind (mod nn) BIGNUM *m_blind; m_blind = BN_new(); BN_mod_mul(m_blind, bn_hm, blind, nn, ctx); // 服务器使用私钥对m_blind进行签名为signed_blind_bn BIGNUM *signed_blind_bn; signed_blind_bn = BN_new(); BN_mod_exp(signed_blind_bn, m_blind, r->d, nn, ctx); // 盲签名后结果发回客户端 // 求得k模nn下的逆 BIGNUM *k_inverse; k_inverse = BN_new(); BN_mod_inverse(k_inverse, k, nn, ctx); // 去盲化得signed_bn BIGNUM *signed_bn; signed_bn = BN_new(); BN_mod_mul(signed_bn, signed_blind_bn, k_inverse, nn, ctx); // 公钥验证签名 BIGNUM *hm2_bn; hm2_bn = BN_new(); BN_mod_exp(hm2_bn, signed_bn, bne, nn, ctx); // BN_print_fp(stdout, hm2_bn); // printf("\n"); if(!BN_cmp(bn_hm, hm2_bn)){ printf("success!\n"); }else{ printf("Failed!\n"); } return 0; } int readFile(string filename, long size){ string fullPath = source +filename; fstream in; in.open(fullPath.c_str(), ios::in|ios::binary); if(!in){ cout<<"error!"<<endl; } int temp = in.tellg(); in.seekg(0, ios_base::end); long filelen = in.tellg(); in.seekg(temp); int n; int lastLen; if((filelen % size) == 0){ // 整数倍时 n = filelen / size; lastLen = size; }else{ n = filelen / size; lastLen = filelen % size + size; } char *databuf = new char[size]; for(int i=0;i<n-1;i++){ // 前n-1个块 in.read(databuf,size*sizeof(char)); blindSig(databuf, size); } char *databufLast = new char[lastLen]; in.read(databufLast,lastLen*sizeof(char)); blindSig(databufLast, lastLen); return 0; } int main() { KeyGen(); readFile("4k.txt", 1024); return 0; }
3.比较
首先比较时间,虽然说基于椭圆曲线的公钥密码体制的速度要比RSA安全的多且同安全程度下速度更快,160位的ECC就可以达到1024位RSA的安全,就像NITS给出的比较结果。
RSA key size (bits) ECC key size (bits) 1024 160 2048 224 3072 256 7680 384 但是双线性对的运算我也不是很懂,不多作说明,只能进行简单的时间测试。
分别测试了基于1024位、2048位RSA和BLS的盲签名算法,随着文件大小的增大时间花费(文件大小主要影响的是签名次数)。
从图中可以看出,基于BLS的盲签名算法效率远不如RSA(据PBC Library给出的安全性,A曲线大体与1024位RSA差不多)(注意需要时重新查证)。但是BLS签名的优点远不是在这里,其可以进行签名聚合、密钥聚合等,多用户时会节省很多时间和空间。
注:
数据都是自己通过实验自己测出来的,具体理论目前只是稍微了解,参考时请注意求证,如有大神发现问题请及时提出,不想误导他人。
-
RSA算法原理及数字签名技术
2021-12-24 23:22:251.前言: 非对称加密算法的经典算法———RSA算法的应用...RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。但是对于量 -
论文研究-基于RSA的XML不可否认签名方法研究.pdf
2019-07-22 18:28:55针对普通XML数字签名的可否认问题,首先介绍了XML数字签名原理,然后分析了RSA签名机制和不可否认签名方案,在此基础上提出了一种改进的不可否认签名算法,并将其应用到XML数字签名结构的签名方法中,从而提出了一种... -
基于RSA的实用门限签名
2020-04-22 17:31:292 基于RSA的门限签名 2.1 系统初始化 系统中有lll个参与者,编号分别为1,...,l1,...,l1,...,l,有一个可信的dealer和一个敌手adversary。dealer选择两个长度(512bit)相等的素数ppp和qqq,设p=2p′+1,q=2q′+1p=2p... -
基于Python使用RSA算法进行签名
2022-05-10 14:26:29借助python的pycrypto库,使用公/私钥RSA加密和AES对称会话密钥加密,使用RSA算法进行签名。 具体实现: 事先生成好两对RSA公钥和密钥,分别保存在客户端和服务端本地,AES会话密钥由双方沟通确定。定义AESUtil类和... -
RSA算法和RSA数字签名算法的实现
2018-05-04 15:26:15RSA算法和RSA数字签名算法的实现http://blog.chinaunix.net/uid-21880738-id-1813146.html...实现RSA算法包括生成RSA密钥,用RSA加密规则和解密规则处理数据.RSA数字签名算法利用RSA算法实现数字签名.本文详述了R... -
C++实践(六):基于RSA与HMAC的数字签名算法及其原理
2018-03-14 12:45:44基于RSA与HMAC的数字签名算法及其原理 数字签名是一种认证机制,它使得消息的产生者可以添加一个起签名作用的码字。通过计算消息的Hash值并使用产生者的私钥加密Hash值来生成签名。签名保证了消息来源和... -
论文研究-基于语义水印的数字签名算法.pdf
2019-07-22 22:16:52针对数字签名技术中签名信息易被移除的问题,在语义技术的基础上提出了基于语义水印的数字签名算法。该算法的基本思想是在不改变文本语义的前提下,通过同义词替换算法嵌入签名信息,并在数据加密阶段,综合运用DES... -
利用RSA生成数字签名以及验签核心代码实现
2020-04-05 18:05:29} /** * 随机生成密钥对 * @throws NoSuchAlgorithmException */ public static void genKeyPair() throws NoSuchAlgorithmException { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 ... -
实验项目2 :RSA算法
2021-05-30 16:38:55实验二 RSA数字签名 一、实验目的 (1)了解RSA算法的特点 (2)掌握RSA算法的加解密原理 (3)掌握RSA数字签名算法的原理 二、实验内容 RSA 公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥... -
论文研究-改进的RSA算法在数字签名中的应用.pdf
2019-09-07 15:00:07针对传统RSA密码算法运算...然后通过仿真实验,将其与传统RSA算法以及基于乘同余对称特性的SMM算法和指数2k进制化相结合的组合优化算法相比较,实验结果表明新的RSA密码优化算法在提升运算速度方面达到了较高的水平。 -
改进的RSA算法在数字签名中的应用 (2014年)
2021-05-11 23:35:30针对传统RSA密码算法运算...然后通过仿真实验,将其与传统RSA算法以及基于乘同余对称特性的SMM算法和指数2k进制化相结合的组合优化算法相比较,实验结果表明新的RSA密码优化算法在提升运算速度方面达到了较高的水平。 -
基于DSA的电力调度数字签名方案
2020-07-30 18:09:17为了解决电力调度系统的安全性问题,克服传统公钥密码体制证书管理复杂的缺陷,在无双线性对思想的基础上,构造了一种基于DSA的无证书数字签名方案。方案利用零知识方式对调度用户身份进行认证,将签名身份与公钥... -
RSA算法C++实现
2013-12-13 19:48:45RSA算法C++实现 报告+源代码bool prime(int n) { int m=sqrt(n); for(int i=2;i;i++) { if(n%i==0) break; } if(i>=m) return 1; else return 0; } -
RSA算法实验报告 通过对RSA算法的实现,深入了解RSA原理及应用
2011-02-27 22:18:29RSA算法是第一个既能用于数据加密也能用于数字签名的算法,因此它为公用网络上信息的加密和鉴别提供了一种基本的方法。它通常是先生成一对RSA 密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开... -
基于多维Hash链的无线ad-hoc安全路由数字签名方案 (2009年)
2021-05-10 12:51:52为了解决目前无线ad-hoc网络安全路由协议中的签名算法运算效率较低,从而导致可实现性较差的问题,借鉴多维Hash链的思想,提出一种数字签名算法,并基于这种数字签名算法,给出一套无线ad-hoc安全路由协议的实例。该数字... -
实验三 DSA数字签名算法.doc
2022-05-29 22:38:49实验三 DSA数字签名算法.doc -
基于椭圆曲线的数字签名和加密算法 (2011年)
2021-05-22 03:57:24直接将ElGamal 签名方案移植到椭圆曲线密码系统上...对MV 加密算法进行改进,降低其膨胀率,通过实验证明其执行速度快于RSA 和ECC-E 算法。执行效率及密钥长度方面的优势使2 种改进算法能更有效地应用于智能卡计算中。 -
[系统安全] 二十.PE数字签名之(上)什么是数字签名及Signtool签名工具详解
2021-02-07 17:15:31本文将详细介绍什么是数字签名,并采用Signtool工具对EXE文件进行签名,后续深入分析数字签名的格式及PE病毒内容。这些基础性知识不仅和系统安全相关,同样与我们身边常用的软件、文档、操作系统紧密联系,希望这些... -
密码编码学之数字签名
2022-02-11 16:02:32目录一、数字签名1、 数字签名关键部分的描述2、 数字签名的要求3、 DSA数字签名算法4、 椭圆曲线数字签名算法(ECDSA)5、 RSA-PSS数字签名算法 一、数字签名 1、 数字签名关键部分的描述 数字签名具有认证功能,下面... -
RSA算法加解密实验
2021-05-20 18:14:29一、RSA算法加解密简述在公开密钥密码...正是基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA 密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册... -
DSA数字签名算法
2021-04-19 19:51:32数字签名算法 -
yjy的分享 -数字签名.pdf
2020-07-23 21:09:09从基于RSA签名算法进行数字签名算法,采用MD5哈希函数,实现对文件的数字签名。程序分为签名生成部分和验证部分。这是自己独立完成的一个实验,过程是输入文件路径,利用入参字符串和私钥进行签名,再输入私钥进行... -
数字签名原理及其应用
2017-06-25 15:40:34签名的作用无非就是证明某个文件上的内容确实是我写的/我认同的,别人不能冒充我的签名(不可伪造),我也不能否认上面的签名是我的(不可抵赖)。 我们知道,手写签名之所以不能伪造,是因为每一个人的...而数字签名