基于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群随机选择的元素。
基于BLS的盲签名算法
在单个程序(不区分客户端和服务器端,不进行消息传递)中实现盲签名代码:
代码中多了一步操作,将文件块按照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 k1,其中 k − 1 k^{-1} k1表示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)
1024160
2048224
3072256
7680384

但是双线性对的运算我也不是很懂,不多作说明,只能进行简单的时间测试。
分别测试了基于1024位、2048位RSA和BLS的盲签名算法,随着文件大小的增大时间花费(文件大小主要影响的是签名次数)。
在这里插入图片描述

从图中可以看出,基于BLS的盲签名算法效率远不如RSA(据PBC Library给出的安全性,A曲线大体与1024位RSA差不多)(注意需要时重新查证)。但是BLS签名的优点远不是在这里,其可以进行签名聚合、密钥聚合等,多用户时会节省很多时间和空间。

注:

数据都是自己通过实验自己测出来的,具体理论目前只是稍微了解,参考时请注意求证,如有大神发现问题请及时提出,不想误导他人。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值