openssl编程RSA

转:http://caisenchen.blog.163.com/blog/static/552865502008763587344/

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编程示例

17.6.1密钥生成

       #include <openssl/rsa.h>

       int    main()

       {

              RSA                      *r;

              int                         bits=512,ret;

              unsigned long  e=RSA_3;

              BIGNUM               *bne;


              r=RSA_generate_key(bits,e,NULL,NULL);

              RSA_print_fp(stdout,r,11);

              RSA_free(r);

              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;

              }

              RSA_free(r);

              return 0;

       }

       说明:

       调用RSA_generate_key和RSA_generate_key_ex函数生成RSA密钥,

       调用RSA_print_fp打印密钥信息。

       输出:

       Private-Key: (512 bit)

       modulus:

           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:

           c2:9b:c7:e2:c3

       publicExponent: 3 (0x3)

       privateExponent:

           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:

           84:0f:26:df:1b

       prime1:

           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:

           46:2f:0d

       prime2:

           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:

           c7:65:0f

       exponent1:

           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:

           d9:74:b3

       exponent2:

           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:

           da:43:5f

       coefficient:

           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:

           ca:e3:49

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   

17.6.3签名与验证

              签名运算:

              #include <string.h>

              #include <openssl/objects.h>

              #include <openssl/rsa.h>


              int    main()

              {

              int                         ret;

              RSA                      *r;

              int                         i,bits=1024,signlen,datalen,alg,nid;

              unsigned long  e=RSA_3;

              BIGNUM               *bne;

              unsigned char data[100],signret[200];


              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;

              }


              for(i=0;i<100;i++)

                     memset(&data[i],i+1,1);

              printf("please select digest alg: \n");

              printf("1.NID_md5\n");

              printf("2.NID_sha\n");

              printf("3.NID_sha1\n");

              printf("4.NID_md5_sha1\n");

              scanf("%d",&alg);

              if(alg==1)

              {

                     datalen=55;

                     nid=NID_md5;

              }

              else if(alg==2)

              {

                     datalen=55;

                     nid=NID_sha;

              }

              else if(alg==3)

              {

                     datalen=55;

                     nid=NID_sha1;

              }

              else if(alg==4)

              {

                     datalen=36;

                     nid=NID_md5_sha1;

              }

              ret=RSA_sign(nid,data,datalen,signret,&signlen,r);

              if(ret!=1)

              {

                     printf("RSA_sign err!\n");

                     RSA_free(r);

                     return -1;

              }

              ret=RSA_verify(nid,data,datalen,signret,signlen,r);

              if(ret!=1)

              {

                     printf("RSA_verify err!\n");

                     RSA_free(r);

                     return -1;

              }

              RSA_free(r);

              printf("test ok!\n");

              return 0;

       }

       注意:本示例并不是真正的数据签名示例,因为没有做摘要计算。

       ret=RSA_sign(nid,data,datalen,signret,&signlen,r)将需要运算的数据放入X509_ALGOR数据结构并将其DER编码,对编码结果做RSA_PKCS1_PADDING再进行私钥加密。

被签名数据应该是摘要之后的数据,而本例没有先做摘要,直接将数据拿去做运算。因此datalen不能太长,要保证RSA_PKCS1_PADDING私钥加密运算时输入数据的长度限制。

       ret=RSA_verify(nid,data,datalen,signret,signlen,r)用来验证签名。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 转:http://caisenchen.blog.163.com/blog/static/552865502008...
    right_33cb阅读 4,674评论 0 0
  • 嘟哝嘟哝:最近接到一个任务:在客户端动态生成RSA密钥对,然后向服务器发送这个密钥对中的公钥字符串,由服务器进行公...
    TimmyR阅读 8,009评论 19 21
  • 一、源文件三个rsa.h , rsa.c, main.c //rsa.h #include #define MAX...
    火狼o阅读 7,573评论 1 6
  • 姓名:于川皓 学号:16140210089 转载自:https://baike.baidu.com/item/RS...
    道无涯_cc76阅读 2,532评论 0 1
  • 由于最近出现了核桃英语团队多人来寻求设计源文件,在这里做统一说明。 一.“有空把手上有的设计图原稿都给我们一下吧”...
    Yunishere阅读 2,004评论 1 1