RSA签名及验签原理解析
一、名词解释
公钥
RSA 密钥体系中对外公开的部分,通常用于数据加密、验证数字签名。
私钥
RSA 密钥体系中非公开的部分,通常用于数据解密、数据签名。
数字签名
就是只有信息的发送者才能产生的,别人无法伪造的一段数字串,它同时也是对发送者发送的信息的真实性的一个证明。
二、为什么要使用签名
在系统间报文交互为保证上送报文可靠性[防止中间人攻击],通常做法是在报文中加上校验字[数字签名],并约定好相关算法。
三、实现
为方便系统间交互,本文直接讲述原理,对于RSA_sign 及 RSA_verify的使用下期进行介绍。
签名
- 对需要上送的报文[en_data]计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等]。
- 使用私钥[pri_key]对sign_block加密获得数字签名[sign]。
- 将sign与en_data打包发送给对方。
验签
- 解析收到的报文,拆分为sign 及 en_data。
- 对en_data计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等双方协商]。
- 使用公钥[pub_key]对sign解密,获得sign_block1。
- 比较sign_block 和 sign_block1,若匹配则验证成功,报文未被篡改。
生成公私钥对
int rsa_gen_der_key(int KEY_LENGTH, unsigned char * prik, unsigned char * pubk) {
int ret = 1;
RSA * keypair = RSA_generate_key(KEY_LENGTH, RSA_F4, NULL, NULL);
if (!RSA_check_key(keypair)) {
ret = 0;
}
/* public key */
int size = i2d_RSAPublicKey(keypair, NULL); //提取公钥大小
if (!size) {
ret = 0;
}
unsigned char* pubbuf = (unsigned char*) malloc(size + 1); //生成公钥存储空间
unsigned char *p = pubbuf;
if (pubbuf == NULL) {
ret = 0;
}
size = i2d_RSAPublicKey(keypair, &p); //必须用p,不能直接&pubbuf,因为参数会改变
if (!size) {
ret = 0;
}
binToStr(pubbuf, pubk, size);
free(pubbuf);
/* private key*/
size = i2d_RSAPrivateKey(keypair, NULL); //提取公钥大小
if (!size) {
ret = 0;
}
unsigned char* pribuf = (unsigned char*) malloc(size + 1); //生成公钥存储空间
p = pribuf;
if (pribuf == NULL) {
ret = 0;
}
size = i2d_RSAPrivateKey(keypair, &p);
if (!size) {
ret = 0;
}
binToStr(pribuf, prik, size);
free(pribuf);
RSA_free(keypair);
return 0;
}私钥加密[签名]代码
int prik_encrypt(unsigned char * pri_key, unsigned char * ming,unsigned char * mi) {
int iRet = 0;
int pubk_len = strlen(pri_key) / 2;
unsigned char * prik;
prik = (unsigned char*) malloc(pubk_len + 1);
strToBin(pri_key, prik, pubk_len);
RSA* prikey;
unsigned char *p = prik;
prikey = d2i_RSAPrivateKey(NULL, (const unsigned char**) &p, pubk_len);
int len_patch = 116;
int in_len = strlen(ming);
int in_len_x = in_len % len_patch;
int in_len_y = (int)(in_len / len_patch);
if(in_len_x != 0) in_len_y += 1;
unsigned char rsa_in[1024],rsa_out[1024];
int rsa_i;
int rsa_j=0;
int start_index=0;
for(rsa_i=0;rsa_i<in_len_y;rsa_i++){
memset(rsa_in, 0x00, sizeof(rsa_in));
memset(rsa_out, 0x00, sizeof(rsa_out));
int deal_len = in_len >= len_patch ? len_patch : in_len;
memcpy(rsa_in, ming + start_index, deal_len);
start_index += deal_len;
in_len -= deal_len;
iRet = RSA_private_encrypt(deal_len, (unsigned char*) rsa_in,
(unsigned char*) rsa_out, prikey, RSA_PKCS1_PADDING);
if(iRet < 0){
iRet = -1;
break;
}else{
binToStr(rsa_out,mi + rsa_j,iRet);
rsa_j += iRet * 2;
}
}
if (prik)
free(prik);
if (prikey)
RSA_free(prikey);
return iRet;
}公钥解密[验签]代码
int pubk_decrypt(unsigned char * pub_key, unsigned char * mi,unsigned char * ming) {
int iRet = 0;
int prik_len = strlen(pub_key) / 2;
unsigned char * pubk;
pubk = (unsigned char*) malloc(prik_len + 1);
strToBin(pub_key, pubk, prik_len);
RSA* pubkey;
unsigned char *p = pubk;
pubkey = d2i_RSAPublicKey(NULL, (const unsigned char**) &p, prik_len);
int in_len = strlen(mi) / 2;
int len_patch = 128;
int in_len_x = in_len % len_patch;
int in_len_y = (int)(in_len / len_patch);
if(in_len_x != 0) in_len_y += 1;
unsigned char rsa_128_in[130];
unsigned char rsa_out[1024];
int rsa_i;
int rsa_j=0;
int start_index = 0;
for(rsa_i=0;rsa_i<in_len_y;rsa_i++){
memset(rsa_128_in,0x00,sizeof(rsa_128_in));
memset(rsa_out,0x00,sizeof(rsa_out));
int deal_len = in_len >= len_patch ? len_patch : in_len;
strToBin(mi + start_index, rsa_128_in, deal_len);
start_index += deal_len * 2;
in_len -= deal_len;
iRet = RSA_public_decrypt(deal_len, (unsigned char*) rsa_128_in,
(unsigned char*) rsa_out, pubkey, RSA_PKCS1_PADDING);
if(iRet < 0){
iRet = -1;
break;
}else{
memcpy(ming + rsa_j,rsa_out,iRet);
rsa_j += iRet;
}
}
if (pubk)
free(pubk);
if (pubkey)
RSA_free(pubkey);
return iRet;
}误区
公钥[pub_key]、私钥[pri_key]在密钥体系中是平等的;并不是公钥只能加密,私钥只能解密;私钥也可以用来加密,然后使用公钥解密,只不过我们对这一操作区了个别名叫[签名]和[验签]。