前言
数字签名是公钥密码学发展过程中最重要的概念之一,它可以提供其他方法难以实现的安全性,是用于鉴别数字信息的方法,它可以实现别人无法伪造的一段字符串,同时这段字符串可以信息发送者发送消息的真实性进行验证。
数字签名可以用的算法很多,这里实现了RSA-PSS算法。
RSA-PSS 数字签名算法
顾名思义,这个算法是基于RSA的,RSA的算法简单论述如下:
RSA算首先产生素数p,q,计算n = p * q
然后计算f(n) = (p-1) * (q-1)
选择一个整数e,e和f(n)的最大公约数是1
计算d为e mod f(n) 的逆
这就产生了公钥{e,n},私钥{d,n}
在公钥密码学中的加密是将消息M转换为密文C = Me来进行保密的
而解密则是M = Cd
而在数字签名之中则是相反的,是使用私钥来进行加密,公钥进行解密的。
数字签名过程
上图是编码的过程。
我们有的函数:
Hash 函数:输出长度是hLen字节,在SHA-1 中产生20字节的Hash值
MGF :掩码产生函数
选项:
sLen 随机数(盐)的字节长度
输入
M 用于待编码的消息
emBits 比RSA模数n位长度小的值
输出
EM编码后的消息
参数
emLen EM 的字节长度 = emBits / 8 向上取整
填充1 padding1 十六进制字符串00 00 00 00 00 00 00 00; 64位的0
填充2 padding2 十六进制若干个00后面跟着01, 其长度为(emLen - sLen - hLen - 2)字节
盐 salt 一组伪随机数
bc BC的十六进制值
编码过程有如下步骤:
- 生成消息M 的Hash值,mHash = Hash(M)
- 生成伪随机字节串作为盐,得到M’ = padding1 || mHash || salt 的数据块
- 生成M’ 的Hash值,H = Hash(M’)
- 构造数据块DB = padding2 || salt
- 计算H 的MGF值:dbMask = MGF(H, emLen - hLen - 1)
- 计算maskedDB = DB xor dbMask
- 将maskedDB 的最左8emLen - emBits设为0
- EM = maskedDB || H || BC
代码过程如下:
string RSA_PSS(string input){
int emBits = 512,emLen = emBits / 8;
SHA1 checksum;
checksum.update(input);
const string hash = checksum.final();
cout << "The mHash is: " << hash << endl;
srand(time(0));
for(int i = 0 ; i < 40 ; i++)s[i] = int2Char(rand()%16);
s[40] = '\0';
cout << " 产生的随机数是: " << s << endl;
string M1 = padString(hash, s);
checksum.update(M1);
const string newHash = checksum.final();
string DB = padString2(2 * emLen - 40 - 40 - 4, s);
string mgf = MGF1(newHash,2 * emLen - 40 - 3);
int DBInt[1024];
memset(DBInt,-1,1024);
int mgfLen = 0 , DBLen = 0;
for(int i = 0 ; i < DB.length() && DB.c_str()[i] != '\0' ; i++){
DBInt[i] = char2Int(DB.c_str()[i]);
DBLen ++ ;
}
int mgfInt[1024];
memset(mgfInt,-1,1024);
for(int i = 0 ; i < mgf.length() && mgf.c_str()[i] != '\0' ; i++){
mgfInt[i] = char2Int(mgf.c_str()[i]);
mgfLen ++ ;
}
for(int i = mgfLen,j = DBLen ; i > 0 && j > 0;i--, j--){
if(mgfLen > DBLen){
mgfInt[i-1] ^= DBInt[j-1];
}else {
DBInt[j-1] ^= mgfInt[i-1];
}
}
string maskedDB = "";
if(mgfLen > DBLen){
for(int i = 0 ; i < mgfLen ; i++){
maskedDB += dec2hex(mgfInt[i]);
}
}else {
for(int i = 0 ; i < DBLen ; i++){
maskedDB += dec2hex(DBInt[i]);
}
}
maskedDB = setZero(maskedDB.c_str(),8 * emLen - emBits);
maskedDB += newHash + "BC";
return maskedDB;
}
string toUpper(string lower){
string upper = "";
for(int i = 0 ; i < lower.length() ; i++){
if(lower.c_str()[i] >= 'a' && lower.c_str()[i] <= 'z'){
upper += (lower.c_str()[i] - 32);
}else {
upper += lower.c_str()[i];
}
}
return upper;
}
数字签名的验证
验证过程如下所示:
选项:
Hash 输出长度是hLen 字节的Hash函数
MGF 掩码产生函数
sLen 随机数(盐)的字节长度
输入:
M 用于验证的消息
EM 签名解密后的字符串,长度emLen = emBits / 8 向上取整
参数:
填充1 padding1 十六进制串00 00 00 00 00 00 00 00; 即64位的0
填充2 padding2 十六进制串若干个00后面跟着01,其长度为(emLen - sLen - 2)字节
步骤:
- 生成M 的Hash值:mHash = Hash(M)
- 如果emLen < hLen + sLen +2,则输出“不一致”停止
- 如果EM的最右字节不是十六进制BC,则输出不一致停止
- 令maskedDB等于EM的最坐emLen - hLen - 1字节,H为接下来的hLen字节
- 如果maskedDB最左字节中的最左8emLen - emBits位不是全0,则输出“不一致”停止
- 计算dbMask = MGF(H, emLen - hLen - 1)
- 计算DB = maskedDB XOR dbMask
- 设置DB的最左字节的最左8emLen - emBits为0
- 如果DB的最左(emLen - hLen - sLen - 1)字节不等于填充2,则输出不一致停止
- 将DB的最后sLen 字节设为盐值
- 构造M’ = padding1 || mHash || salt的数据快
- 生成M’ 的Hash值:H’= Hash(M’)
- 如果H = H’, 输出“一致”。否则,输出“不一致”
代码过程:
string testRSA_PSS(string input,string EM){
cout << " 接收到的EM : " << EM << endl;
SHA1 checksum;
checksum.update(input);
string inputHash = checksum.final();// 这里是mHash
int emBits = EM.length() * 8;
int emLen = emBits / 8;
if(emBits % 8 != 0)emLen += 1;
int len = emLen - 40 - 40 - 2;
if(len < 0)return "不一致";
string maskDB = EM.substr(0,emLen - 40 - 2);
string EMH = EM.substr(emLen - 40 - 2,40);
EMH = MGF1(EMH,emLen - 40 - 2);
int maskInt[1024],EMHInt[1024];
memset(maskInt,-1,1024);
memset(EMHInt,-1,1024);
int maskLen = 0 , EMHLen = 0;
for(int i = 0 ; i < maskDB.length() ; i++){
maskInt[i] = char2Int(maskDB.c_str()[i]);
maskLen ++ ;
}
for(int i = 0 ; i < EMH.length() ; i++){
EMHInt[i] = char2Int(EMH.c_str()[i]);
EMHLen ++ ;
}
for(int i = maskLen,j = EMHLen ; i > 0 && j > 0;i--, j--){
if(maskLen > EMHLen){
maskInt[i-1] ^= EMHInt[j-1];
}else {
EMHInt[j-1] ^= maskInt[i-1];
}
}
string DB = "";
if(maskLen > EMHLen){
for(int i = 0 ; i < maskLen ; i++){
DB += dec2hex(maskInt[i]);
}
}else {
for(int i = 0 ; i < EMHLen ; i++){
DB += dec2hex(EMHInt[i]);
}
}
string salt = "";
for(int i = 0 ; i < DB.length() ; i++){
if(DB.c_str()[i] == '1'){
salt = DB.substr(i+1,DB.length());
break;
}
}
salt = toUpper(salt);
cout << "计算得出的盐值是:" << salt << endl;
string M1 = padString(inputHash,salt);
checksum.update(M1);
return checksum.final();// 这里是mHash
}
补充一下RSA部分代码:
生成密钥和调用加密解密函数部分:
// 生成RSA的密钥
mpz_t e,d,n,p,q,fn;
mpz_init(e);
mpz_init(d);
mpz_init(n);
mpz_init(p);
mpz_init(q);
mpz_init(fn);
gmp_randstate_t state;
gmp_randinit_default(state);
// p, q 是两个素数
while(1){
mpz_urandomb(p,state,EM.length() * 10);
if(mpz_probab_prime_p(p,10) != 0)break;
}
while(1){
mpz_urandomb(q,state,EM.length() * 10);
if(mpz_probab_prime_p(q,10) != 0)break;
}
// n = p * q
mpz_mul(n,p,q);
// fn = (p-1) * (q-1)
mpz_sub_ui(p,p,1);
mpz_sub_ui(q,q,1);
mpz_mul(fn,p,q);
mpz_t temp;
mpz_init(temp);
while(1){
mpz_urandomm(e,state,fn);
mpz_gcd(temp,fn,e);
if(mpz_cmp_ui(temp,1) == 0)break;
}
mpz_invert(d,e,fn);
// RSA 加密
encryption(EM,d,n);
gmp_printf("RSA加密后的结果是:%Zd\n",en);
decryption(en,e,n);
gmp_printf(" RSA解密后的结果是:%Zd\n",de);// de是接受到的消息的解密,即EM
加密函数部分及解密函数:
mpz_t en;
void encryption(string input,mpz_t e,mpz_t n){
mpz_t m;
mpz_init(en);
mpz_init(m);
mpz_set_str(m,input.c_str(),16);
char *enStr = new char[mpz_sizeinbase(m,10) + 4];
mpz_get_str(enStr,10,m);
cout << "数字签名转10进制是:" << enStr << endl;
free(enStr);
mpz_powm(en,m,e,n);
}
mpz_t de;
void decryption(mpz_t en,mpz_t d,mpz_t n){
mpz_init(de);
mpz_powm(de,en,d,n);
}
使用了GMP库进行RSA部分的计算。