轉: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中:
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提供的默認方法。
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編程示例
?????? #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
?????? #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???
????????????? 簽名運算:
????????????? #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)用來驗證簽名。