数字签名:RSA-PSS 实现

前言

数字签名是公钥密码学发展过程中最重要的概念之一,它可以提供其他方法难以实现的安全性,是用于鉴别数字信息的方法,它可以实现别人无法伪造的一段字符串,同时这段字符串可以信息发送者发送消息的真实性进行验证。

数字签名可以用的算法很多,这里实现了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的十六进制值

编码过程有如下步骤:

  1. 生成消息M 的Hash值,mHash = Hash(M)
  2. 生成伪随机字节串作为盐,得到M’ = padding1 || mHash || salt 的数据块
  3. 生成M’ 的Hash值,H = Hash(M’)
  4. 构造数据块DB = padding2 || salt
  5. 计算H 的MGF值:dbMask = MGF(H, emLen - hLen - 1)
  6. 计算maskedDB = DB xor dbMask
  7. 将maskedDB 的最左8emLen - emBits设为0
  8. 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)字节

步骤:

  1. 生成M 的Hash值:mHash = Hash(M)
  2. 如果emLen < hLen + sLen +2,则输出“不一致”停止
  3. 如果EM的最右字节不是十六进制BC,则输出不一致停止
  4. 令maskedDB等于EM的最坐emLen - hLen - 1字节,H为接下来的hLen字节
  5. 如果maskedDB最左字节中的最左8emLen - emBits位不是全0,则输出“不一致”停止
  6. 计算dbMask = MGF(H, emLen - hLen - 1)
  7. 计算DB = maskedDB XOR dbMask
  8. 设置DB的最左字节的最左8emLen - emBits为0
  9. 如果DB的最左(emLen - hLen - sLen - 1)字节不等于填充2,则输出不一致停止
  10. 将DB的最后sLen 字节设为盐值
  11. 构造M’ = padding1 || mHash || salt的数据快
  12. 生成M’ 的Hash值:H’= Hash(M’)
  13. 如果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部分的计算。

  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值