《openssl 编程》之 RSA(转)
17.1 RSA介绍
RSA算法是一个广泛使用的公钥算法。其密钥包括公钥和私钥。它能用于数字签名、身份认证以及密钥交换。RSA密钥长度一般使用1024位或者更高。RSA密钥信息主要包括[1]:
Ø n:模数
Ø e:公钥指数
Ø d:私钥指数
Ø p:最初的大素数
Ø q:最初的大素数
Ø dmp1:e*dmp1 = 1 (mod (p-1))
Ø dmq1:e*dmq1 = 1 (mod (q-1))
Ø iqmp:q*iqmp = 1 (mod p )
其中,公钥为n和e;私钥为n和d。在实际应用中,公钥加密一般用来协商密钥;私钥加密一般用来签名。
17.2 openssl的RSA实现
Openssl的RSA实现源码在crypto/rsa目录下。它实现了RSA PKCS1标准。主要源码如下:
1) rsa.h
定义RSA数据结构以及RSA_METHOD,定义了RSA的各种函数。
2) rsa_asn1.c
实现了RSA密钥的DER编码和解码,包括公钥和私钥。
3) rsa_chk.c
RSA密钥检查。
4) rsa_eay.c
Openssl实现的一种RSA_METHOD,作为其默认的一种RSA计算实现方式。此文件未实现rsa_sign、rsa_verify和rsa_keygen回调函数。
5)rsa_err.c
RSA错误处理。
6)rsa_gen.c
RSA密钥生成,如果RSA_METHOD中的rsa_keygen回调函数不为空,则调用它,否则调用其内部实现。
7)rsa_lib.c
主要实现了RSA运算的四个函数(公钥/私钥,加密/解密),它们都调用了RSA_METHOD中相应都回调函数。
8)rsa_none.c
实现了一种填充和去填充。
9)rsa_null.c
实现了一种空的RSA_METHOD。
10) rsa_oaep.c
实现了oaep填充与去填充。
11)rsa_pk1.
实现了pkcs1填充与去填充。
12)rsa_sign.c
实现了RSA的签名和验签。
13)rsa_ssl.c
实现了ssl填充。
14)rsa_x931.c
实现了一种填充和去填充。
17.3 RSA签名与验证过程
RSA签名过程如下:
1) 对用户数据进行摘要;
2) 构造X509_SIG结构并DER编码,其中包括了摘要算法以及摘要结果。
3) 对2)的结果进行填充,填满RSA密钥长度字节数。比如1024位RSA密钥必须填满128字节。具体的填充方式由用户指定。
4) 对3)的结果用RSA私钥加密。
RSA_eay_private_encrypt函数实现了3)和4)过程。
RSA验签过程是上述过程的逆过程,如下:
1) 对数据用RSA公钥解密,得到签名过程中2)的结果。
2) 去除1)结果的填充。
3) 从2)的结果中得到摘要算法,以及摘要结果。
4) 将原数据根据3)中得到摘要算法进行摘要计算。
5) 比较4)与签名过程中1)的结果。
RSA_eay_public_decrypt实现了1)和2)过程。
17.4 数据结构
RSA主要数据结构定义在crypto/rsa/rsa.h中:
17.4.1 RSA_METHOD
struct rsa_meth_st
{
const char *name;
int (*rsa_pub_enc)(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding);
int (*rsa_pub_dec)(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding);
int (*rsa_priv_enc)(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding);
int (*rsa_priv_dec)(int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding);
/* 其他函数 */
int (*rsa_sign)(int type,const unsigned char *m, unsigned int m_length,unsigned char *sigret, unsigned int *siglen, const RSA *rsa);
int (*rsa_verify)(int dtype,const unsigned char *m, unsigned int m_length,unsigned char *sigbuf, unsigned int siglen, const RSA *rsa);
int (*rsa_keygen)(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
};
主要项说明:
name:RSA_METHOD名称;
rsa_pub_enc:公钥加密函数,padding为其填充方式,输入数据不能太长,否则无法填充;
rsa_pub_dec:公钥解密函数,padding为其去除填充的方式,输入数据长度为RSA密钥长度的字节数;
rsa_priv_enc:私钥加密函数,padding为其填充方式,输入数据长度不能太长,否则无法填充;
rsa_priv_dec:私钥解密函数,padding为其去除填充的方式,输入数据长度为RSA密钥长度的字节数;
rsa_sign:签名函数;
rsa_verify:验签函数;
rsa_keygen:RSA密钥对生成函数。
用户可实现自己的RSA_METHOD来替换openssl提供的默认方法。
17.4.2 RSA
RSA数据结构中包含了公/私钥信息(如果仅有n和e,则表明是公钥),定义如下:
struct rsa_st
{
/* 其他 */
const RSA_METHOD *meth;
ENGINE *engine;
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
BIGNUM *dmp1;
BIGNUM *dmq1;
BIGNUM *iqmp;
CRYPTO_EX_DATA ex_data;
int references;
/* 其他数据项 */
};
各项意义:
meth:RSA_METHOD结构,指明了本RSA密钥的各种运算函数地址;
engine:硬件引擎;
n,e,d,p,q,dmp1,dmq1,iqmp:RSA密钥的各个值;
ex_data:扩展数据结构,用于存放用户数据;
references:RSA结构引用数。
17.5 主要函数
1) RSA_check_key
检查RSA密钥。
2)RSA_new
生成一个RSA密钥结构,并采用默认的rsa_pkcs1_eay_meth RSA_METHOD方法。
3)RSA_free
释放RSA结构。
4) RSA *RSA_generate_key(int bits, unsigned long e_value,
void (*callback)(int,int,void *), void *cb_arg)
生成RSA密钥,bits是模数比特数,e_value是公钥指数e,callback回调函数由用户实现,用于干预密钥生成过程中的一些运算,可为空。
5) RSA_get_default_method
获取默认的RSA_METHOD,为rsa_pkcs1_eay_meth。
6) RSA_get_ex_data
获取扩展数据。
7) RSA_get_method
获取RSA结构的RSA_METHOD。
8) RSA_padding_add_none
RSA_padding_add_PKCS1_OAEP
RSA_padding_add_PKCS1_type_1(私钥加密的填充)
RSA_padding_add_PKCS1_type_2(公钥加密的填充)
RSA_padding_add_SSLv23
各种填充方式函数。
9) RSA_padding_check_none
RSA_padding_check_PKCS1_OAEP
RSA_padding_check_PKCS1_type_1
RSA_padding_check_PKCS1_type_2
RSA_padding_check_SSLv23
RSA_PKCS1_SSLeay
各种去除填充函数。
10)int RSA_print(BIO *bp, const RSA *x, int off)
将RSA信息输出到BIO中,off为输出信息在BIO中的偏移量,比如是屏幕BIO,则表示打印信息的位置离左边屏幕边缘的距离。
11)int DSA_print_fp(FILE *fp, const DSA *x, int off)
将RSA信息输出到FILE中,off为输出偏移量。
12)RSA_public_decrypt
RSA公钥解密。
13)RSA_public_encrypt
RSA公钥加密。
14)RSA_set_default_method/ RSA_set_method
设置RSA结构中的method,当用户实现了一个RSA_METHOD时,调用此函数来设置,使RSA运算采用用户的方法。
15)RSA_set_ex_data
设置扩展数据。
16)RSA_sign
RSA签名。
17)RSA_sign_ASN1_OCTET_STRING
另外一种RSA签名,不涉及摘要算法,它将输入数据作为ASN1_OCTET_STRING进行DER编码,然后直接调用RSA_private_encrypt进行计算。
18)RSA_size
获取RSA密钥长度字节数。
19)RSA_up_ref
给RSA密钥增加一个引用。
20)RSA_verify
RSA验证。
21)RSA_verify_ASN1_OCTET_STRING
另一种RSA验证,不涉及摘要算法,与RSA_sign_ASN1_OCTET_STRING对应。
22)RSAPrivateKey_asn1_meth
获取RSA私钥的ASN1_METHOD,包括i2d、d2i、new和free函数地址。
23)RSAPrivateKey_dup
复制RSA私钥。
24)RSAPublicKey_dup
复制RSA公钥。
17.6编程示例
r=RSA_generate_key(bits,e,NULL,NULL);
ret=RSA_generate_key_ex(r,bits,bne,NULL);
printf("RSA_generate_key_ex err!\n");
调用RSA_generate_key和RSA_generate_key_ex函数生成RSA密钥,
00:d0:93:40:10:21:dd:c2:0b:6a:24:f1:b1:d5:b5:
77:79:ed:a9:a4:10:66:6e:88:d6:9b:0b:4c:91:7f:
23:6f:8f:0d:9e:9a:b6:7c:f9:47:fc:20:c2:12:e4:
b4:d7:ab:66:3e:73:d7:78:00:e6:5c:98:35:29:69:
00:8b:0c:d5:60:16:93:d6:b2:46:c3:4b:cb:e3:ce:
4f:a6:9e:71:18:0a:ee:f4:5b:39:bc:b2:33:0b:aa:
17:9f:b3:7e:f0:0f:2a:24:b6:e4:73:40:ba:a0:65:
d3:19:0f:c5:b5:4f:59:51:e2:df:9c:83:47:da:8d:
00:f7:4c:fb:ed:32:a6:74:5c:2d:6c:c1:c5:fe:3a:
59:27:6a:53:5d:3e:73:49:f9:17:df:43:79:d4:d0:
00:d7:e9:88:0a:13:40:7c:f3:12:3d:60:85:f9:f7:
ba:96:44:29:74:3e:b9:4c:f8:bb:6a:1e:1b:a7:b4:
00:a4:dd:fd:48:cc:6e:f8:3d:73:9d:d6:83:fe:d1:
90:c4:f1:8c:e8:d4:4c:db:fb:65:3f:82:51:38:8a:
00:8f:f1:05:5c:0c:d5:a8:a2:0c:28:eb:03:fb:fa:
7c:64:2d:70:f8:29:d0:dd:fb:27:9c:14:12:6f:cd:
00:d3:fa:ea:a0:21:7e:8a:e1:ab:c7:fd:e9:3d:cb:
5d:10:96:17:69:75:cd:71:d5:e5:07:26:93:e8:35:
17.6.2 RSA加解密运算
#include <openssl/rsa.h>
#include <openssl/sha.h>
int main()
{
RSA *r;
int bits=1024,ret,len,flen,padding,i;
unsigned long e=RSA_3;
BIGNUM *bne;
unsigned char *key,*p;
BIO *b;
unsigned char from[500],to[500],out[500];
bne=BN_new();
ret=BN_set_word(bne,e);
r=RSA_new();
ret=RSA_generate_key_ex(r,bits,bne,NULL);
if(ret!=1)
{
printf("RSA_generate_key_ex err!\n");
return -1;
}
/* 私钥i2d */
b=BIO_new(BIO_s_mem());
ret=i2d_RSAPrivateKey_bio(b,r);
key=malloc(1024);
len=BIO_read(b,key,1024);
BIO_free(b);
b=BIO_new_file("rsa.key","w");
ret=i2d_RSAPrivateKey_bio(b,r);
BIO_free(b);
/* 私钥d2i */
/* 公钥i2d */
/* 公钥d2i */
/* 私钥加密 */
flen=RSA_size(r);
printf("please select private enc padding : \n");
printf("1.RSA_PKCS1_PADDING\n");
printf("3.RSA_NO_PADDING\n");
printf("5.RSA_X931_PADDING\n");
scanf("%d",&padding);
if(padding==RSA_PKCS1_PADDING)
flen-=11;
else if(padding==RSA_X931_PADDING)
flen-=2;
else if(padding==RSA_NO_PADDING)
flen=flen;
else
{
printf("rsa not surport !\n");
return -1;
}
for(i=0;i<flen;i++)
memset(&from[i],i,1);
len=RSA_private_encrypt(flen,from,to,r,padding);
if(len<=0)
{
printf("RSA_private_encrypt err!\n");
return -1;
}
len=RSA_public_decrypt(len,to,out,r,padding);
if(len<=0)
{
printf("RSA_public_decrypt err!\n");
return -1;
}
if(memcmp(from,out,flen))
{
printf("err!\n");
return -1;
}
/* */
printf("please select public enc padding : \n");
printf("1.RSA_PKCS1_PADDING\n");
printf("2.RSA_SSLV23_PADDING\n");
printf("3.RSA_NO_PADDING\n");
printf("4.RSA_PKCS1_OAEP_PADDING\n");
scanf("%d",&padding);
flen=RSA_size(r);
if(padding==RSA_PKCS1_PADDING)
flen-=11;
else if(padding==RSA_SSLV23_PADDING)
flen-=11;
else if(padding==RSA_X931_PADDING)
flen-=2;
else if(padding==RSA_NO_PADDING)
flen=flen;
else if(padding==RSA_PKCS1_OAEP_PADDING)
flen=flen-2 * SHA_DIGEST_LENGTH-2 ;
else
{
printf("rsa not surport !\n");
return -1;
}
for(i=0;i<flen;i++)
memset(&from[i],i+1,1);
len=RSA_public_encrypt(flen,from,to,r,padding);
if(len<=0)
{
printf("RSA_public_encrypt err!\n");
return -1;
}
len=RSA_private_decrypt(len,to,out,r,padding);
if(len<=0)
{
printf("RSA_private_decrypt err!\n");
return -1;
}
if(memcmp(from,out,flen))
{
printf("err!\n");
return -1;
}
printf("test ok!\n");
RSA_free(r);
return 0;
}
上述程序中当采用公钥RSA_SSLV23_PADDING加密,用私钥RSA_SSLV23_PADDING解密时会报错,原因是openssl源代码错误:
rsa_ssl.c函数RSA_padding_check_SSLv23有:
if (k == -1) /* err */
{
RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_SSLV3_ROLLBACK_ATTACK);
return (-1);
}
修改为k!=-1即可。
各种padding对输入数据长度的要求:
私钥加密:
RSA_PKCS1_PADDING RSA_size-11
RSA_NO_PADDING RSA_size-0
RSA_X931_PADDING RSA_size-2
公钥加密
RSA_PKCS1_PADDING RSA_size-11
RSA_SSLV23_PADDING RSA_size-11
RSA_X931_PADDING RSA_size-2
RSA_NO_PADDING RSA_size-0
RSA_PKCS1_OAEP_PADDING RSA_size-2 * SHA_DIGEST_LENGTH-2
int i,bits=1024,signlen,datalen,alg,nid;
unsigned char data[100],signret[200];
ret=RSA_generate_key_ex(r,bits,bne,NULL);
printf("RSA_generate_key_ex err!\n");
printf("please select digest alg: \n");
ret=RSA_sign(nid,data,datalen,signret,&signlen,r);
ret=RSA_verify(nid,data,datalen,signret,signlen,r);
被签名数据应该是摘要之后的数据,而本例没有先做摘要,直接将数据拿去做运算。因此datalen不能太长,要保证RSA_PKCS1_PADDING私钥加密运算时输入数据的长度限制。
ret=RSA_verify(nid,data,datalen,signret,signlen,r)用来验证签名。
《openssl 编程》之 RSA(转)相关推荐
- 17.openssl编程——RSA介绍
17.1 RSA介绍 RSA算法是一个广泛使用的公钥算法.器密钥包括公钥和私钥.他能用于数字签名.身份认证以及密钥交换.RSA密钥信息主要包括: * n:模数 * e:公钥指数 * d:私钥指数 * ...
- 笔记整理——使用openssl编程
error: openssl 的所有解决方案 (2013/6/22 17:39:00) error: openssl/crypto.h: No such file or directory 解决方案 ...
- openssl 编程。 证书制作
首页 博客 学院 下载 GitChat TinyMind 论坛 问答 商城 VIP 活动 写博客 发Chat 登录注册 么刚的专栏 RSS订阅 原 openssl证书制作及编程 2010年07月29日 ...
- openssl qt 生成秘钥_关于openssl作的rsa生成密钥及加解密
关于openssl做的rsa生成密钥及加解密 谁能给个在QtCreator上用openssl做的rsa生成密钥及加解密的例子参考下 网上找的都是片段 不全 谢谢! RSA openssl qtc ...
- 31.openssl编程——SSL实现
31.1 概述 SSL协议最先由netscape公司提出,包括sslv2和sslv3两个版本.当前形成标准为tls协议(rfc2246规范)和DTLS(rfc4347,用于支持UDP协议).sslv3 ...
- LKT系列加密芯片如何预置openssl生成的rsa密钥完成运算(二)
第二篇 openssl生成的rsa密钥如何写入到加密芯片中 1.测试目标 将openssl生成的RSA1024位密钥写入到LKT系列加密芯片的公私钥文件中 2.测试环境 本示例运行环境为windows ...
- 基础练习: 使用openssl命令创建RSA密钥
本文大部分内容参考自: http://blog.csdn.net/zhymax/article/details/7683925 <使用openssl命令剖析RSA私钥文件格式> 原作者博客 ...
- 数字内容安全实验一:使用Matlab编程实现 RSA 加密算法
一.实验目的 了解数据加密的原理,掌握公钥加密算法 二.实验内容 使用 matlab 编程实现 RSA 加密 三.实验过程 题目:RSA 算法 内容: rsa_pq.m 文件代码如下: 本代码主要作用 ...
- 【加密与解密】Openssl 生成的RSA秘钥如被C#使用解密
openssl生成的RSA公私钥对保存一个pem文件中,生成时可以选择加密与不加密,如果要提取私钥需要用到openssl rsa进行提取,之后用c#转换为xml之后再对加密数据进行解密,详细如下: 1 ...
- MacOS下使用C语言基于openssl库进行RSA加密解密
MacOS下使用C语言基于openssl库进行RSA加密解密 1 安装openssl并生成密钥 首先当然要安装openssl(这里记得看一下安装路径,应该是/usr/local/Cellar/open ...
最新文章
- Python爬虫学习(1)
- android 快速 顶部,Android RecyclerView 快速滑到顶部
- 绝地求生手游7月服务器维护,绝地求生维护公告7月22 | 手游网游页游攻略大全...
- LTE各场景下的密钥处理
- 判断js中的数据类型的方法
- NAND FLASH ECC
- 为什么需要切换到在线签署文档和合同
- 【Python3网络爬虫开发实战】1.2.1-Requests的安装
- 香港理工大学ASSIST2011会议(2011-09-30)
- PDF转JPG的工具
- Android开发系列(十一) QQ登陆界面——Android控件使用实例
- 传智播客学习日记Day9
- VM虚拟机网络设置问题
- Web服务器群集——Nginx企业级优化
- lzg_ad:XPE中的Remote Procedure Call (RPC) Locator服务无法启动
- 【云计算基础服务-Nginx WEB服务器---原理及简介详解】
- Openpyxl 修改单元格长宽
- 冒险者传说pc6java_我的世界之冒险者传说整合包
- word转pdf时图片模糊/分辨率不高解决方案
- BSD是什么,车辆为什么要装BSD?