關於openssl幾個API的一點小收穫

    今天心血來潮忽然想搞搞openssl了,趁着端午小假,恰好有空能夠鼓搗孤島本身喜歡的東西,出去東奔西跑的實在太造孽了,仍是宅起來給本身充充電吧。下載openssl最新代碼1.0.1g,修復了「心血漏洞」那個版本。編譯安裝那些小兒科的東西就再也不浪費筆墨了,若是出現頭文件或者庫文件之類的錯誤,請在本人博客裏尋找相關文章,應該主要集中在動態庫那幾篇博文。反正我在本身虛擬機裏安裝的時候是妥妥滴。

    由於我主要對非對稱加密的RSA算法比較感興趣,網上最多的就是這麼用的:
   生成私鑰文件(其中已經包含了公鑰):
[root@localhost release]#openssl genrsa -out plainPrv.key 1024
   
   而後再從這個私鑰文件裏將公鑰提取出來,保存到文件裏:
[root@localhost release]#openssl rsa -in plainPrv.key -pubout -out plainPub.key

    RSA通常有兩種應用場景:
   一、公鑰加密、私鑰解密:這是數據安全通訊領域最多見情形;
   二、私鑰加密、公鑰解密:這主要用於數字簽名。
   兩種方式,一通百通,本文只看第一種場景。
  關於測試代碼,網上處處都是,也都基本能用,我就先不摘抄了。你們問的最多的問題就是在讀取公鑰文件時, PEM_read_RSA_PUBKEY()函數和 PEM_read_RSAPublicKEY()的疑惑。爲何讀取私鑰文件用的PEM_read_RSAPrivateKey(),針對上述openssl命令生成的公鑰文件,在讀取其內容時用對稱的 PEM_read_RSAPublicKEY()接口卻會報錯,必需要用 PEM_read_RSA_PUBKEY()才能夠。

   其實,咱們要是看看一兩個文件內容就明白了:
[root@localhost release]# cat plainPrv.key
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDlGVxzTDVhnC16SW+D0WG8hvm1wztmr0vBh2VK6CU7k90mdrCx
4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBHKttjIx5Diq3wLXDP2qU4mjSI
vHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFnicMugj+0sXik1pLWwIwIDAQAB
AoGBAKoB5OomfmJ92/2oKxmdsjKN0xY/13++y6/EgrVQifipJG5bm4mVI01F7Ket
ai3AuHpWy+DPUy3BndSWFyfAsyatULiK3cJnIZumxmWP8G9odfO1pH/KcZB2Vi61
HcbioDuJRCcF3jpbGMun3lCwkdG/qVfsFmOElbzSbNMDbwkJAkEA/K9mOSKrP+lu
6bsIuD6/n2XQkz8XE2lPuPwKhVLX+ljXqRyxJZH0n+2EC8pUi694Q2Zhgn0uPdEl
KCYtlBaLXQJBAOgawH01Xc0r63+XVif6rLZfwJGBAP8921e2dRDFYhYLP3riflY8
xvFQsh4n7kbAXt4xZ3pDA/J1INnE01Rk8X8CQCmzyOslDZ4+qE9qzsWZlYZ5BzNF
9kj92GpvLk1SntJyVyVR1uqcbAL48BICEnH7Q53cB7vBbSBGpBs8Mcl+7wECQQCF
Dbjkze/sys2ggd+44WGa1n8sqhgpOYuA1656I7ybyGzmg+pKg2LEOS8yTE+yrVp0
4ztfggVEO1LOo59F1Ov/AkEApfUtgKHB4YCPy70syFaQoAWjiaxOWq/FLM7FBntP
ikz1X7gNsRkb4I/be15ZN8E/2Z0Q95FOpsgqw76Bi4Yynw==
-----END RSA PRIVATE KEY-----

[root@localhost release]# cat plainPub.key
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlGVxzTDVhnC16SW+D0WG8hvm1
wztmr0vBh2VK6CU7k90mdrCx4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBH
KttjIx5Diq3wLXDP2qU4mjSIvHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFni
cMugj+0sXik1pLWwIwIDAQAB
-----END PUBLIC KEY-----

    當咱們在用 PEM_read_RSAPublicKEY()讀取公鑰文件plainPub.key時報的錯誤是醬紫滴:
3077879432:error:0906D06C:lib(9):func(109):reason(108):pem_lib.c:698:Expecting: RSA PUBLIC KEY

    因此我就天真地將公鑰文件頭和尾分別改爲「-----BEGIN RSA PUBLIC KEY-----」和「-----BEGIN RSA PUBLIC KEY-----」,理想很豐滿,顯示很骨感。實踐證實openssl是不能那麼輕易就被忽悠過去的。沒辦法,查看openssl源碼發現,提取公鑰文件時除了-pubout參數能夠設置外,還有有個參數叫作-RSAPublicKey_out,可是命令行提示和man手冊裏竟然沒有任何說起。幸虧我還會讀C代碼,因此提取公鑰時我改用下面的命令:
[root@localhost release]#openssl rsa -in plainPrv.key -RSAPublicKey_out -out plainPub.key

   這樣作完的結果是,首先公鑰文件的內容有點變化:
[root@localhost test_openssl]# cat plainPub2.key
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAOUZXHNMNWGcLXpJb4PRYbyG+bXDO2avS8GHZUroJTuT3SZ2sLHhvLVR
FxnqJLorGibFKpaYr2DYLSJF3zHHs7LKQEcq22MjHkOKrfAtcM/apTiaNIi8c/cz
CCs5HYnBriY2vW94zZzWnJefnEh9mzYgWeJwy6CP7SxeKTWktbAjAgMBAAE=
-----END RSA PUBLIC KEY-----
[root@localhost release]# cat plainPub.key
----- BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlGVxzTDVhnC16SW+D0WG8hvm1
wztmr0vBh2VK6CU7k90mdrCx4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBH
KttjIx5Diq3wLXDP2qU4mjSIvHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFni
cMugj+0sXik1pLWwIwIDAQAB
----- END PUBLIC KEY-----

   其次,當我再用PEM_read_RSAPublicKEY()接口來讀取公鑰文件plainPub2.key時,竟然成功了。說明RSA PUBLIC KEY和PUBLIC KEY的兩種公鑰文件其存儲方式是不同的,PEM_read_RSAPublicKEY()只能讀取RSA PUBLIC KEY形式的公鑰文件;而PEM_read_RSA_PUBKEY()只能讀取PUBLIC KEY格式的公鑰文件。因爲本人密碼學基礎較薄弱,如今還不能說出二者的區別,請各位見諒,還望密碼方面的大牛們予以點撥。演示代碼以下:

點擊(此處)摺疊或打開 css

  1. /* filename: tmp.c
  2. */
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<string.h>
  6. #include<openssl/rsa.h>
  7. #include<openssl/pem.h>
  8. #include<openssl/err.h>

  9. void hexprint(char *str,int len)
  10. {
  11.     int i=0;
  12.     for(i=0;i<len;i++){
  13.         printf("%s%02x%s",((i%16==0?"|":"")),*((unsigned char*)str+i),(((i+1)%16==0)?"|\n":" "));
  14.     }
  15.     if(i%16!=0)
  16.         printf("|\n");
  17. }

  18. static int do_operation(RSA* rsa_ctx,char *instr,char* path_key,int inlen,char** outstr,int type)
  19. {
  20.     if(rsa_ctx == NULL || instr == NULL || path_key == NULL)
  21.     {
  22.         perror("input elems error,please check them!");
  23.         return -1;
  24.     }
  25.     int rsa_len,num;
  26.     rsa_len=RSA_size(rsa_ctx);
  27.     *outstr=(unsigned char *)malloc(rsa_len+1);
  28.     memset(*outstr,0,rsa_len+1);
  29.     switch(type){
  30.         case 1: //pub enc
  31.         if(inlen == 0){
  32.             perror("input str len is zero!");
  33.             goto err;
  34.         }
  35.         num = RSA_public_encrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);
  36.         break;
  37.         case 2: //prv dec
  38.         num = RSA_private_decrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);        
  39.         default:
  40.         break;
  41.     }

  42.     if(num == -1)
  43.     {
  44.         printf("Got error on enc/dec!\n");
  45. err:
  46.         free(*outstr);
  47.         *outstr = NULL;
  48.         num = -1;
  49.     }
  50.     return num;
  51. }

  52. int rsa_pub_encrypt(char *str,char *path_key,char** outstr){
  53.     RSA *p_rsa;
  54.     FILE *file;
  55.     int flen,rsa_len,num;
  56.     if((file=fopen(path_key,"r"))==NULL){
  57.         perror("open key file error");
  58.         return -1;
  59.     }
  60. #ifdef RSAPUBKEY
  61.     if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
  62. #else
  63.     if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){
  64. #endif
  65.         ERR_print_errors_fp(stdout);
  66.         return -1;
  67.     }

  68.     num = do_operation(p_rsa,str,path_key,strlen(str),outstr,1);
  69.     RSA_free(p_rsa);
  70.     fclose(file);
  71.     return num;
  72. }

  73. int rsa_prv_decrypt(char *str,char *path_key,int inlen,char** outstr){    
  74.     RSA *p_rsa;
  75.     FILE *file;
  76.     int rsa_len,num;
  77.         
  78.     if((file=fopen(path_key,"r"))==NULL){
  79.         perror("open key file error");
  80.         return -1;
  81.     }
  82.     if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
  83.         ERR_print_errors_fp(stdout);
  84.         return -1;
  85.     }    
  86.     num = do_operation(p_rsa,str,path_key,inlen,outstr,2);
  87.     RSA_free(p_rsa);
  88.     fclose(file);
  89.     return num;
  90. }

  91. int main(int argc,char** argv){
  92.     char *ptr_en,*ptr_de;
  93.     int len;
  94.    
  95.     printf("source is :%s\n",argv[1]);
  96.     len=rsa_pub_encrypt(argv[1],argv[2],&ptr_en);
  97.     printf("pubkey encrypt:\n");
  98.     hexprint(ptr_en,len);
  99.     
  100.     rsa_prv_decrypt(ptr_en,argv[3],len,&ptr_de);
  101.     printf("prvkey decrypt:%s\n",ptr_de==NULL?"NULL":ptr_de);
  102.     
  103.     if(ptr_en!=NULL){
  104.         free(ptr_en);
  105.     }
  106.     if(ptr_de!=NULL){
  107.         free(ptr_de);
  108.     }
  109.     
  110.     return 0;
  111. }

    若是開啓RSAPUBKEY宏,則用PEM_read_RSA_PUBKEY()來讀取公鑰文件;不然用PEM_read_RSAPublicKey()讀取:
[root@localhost release]#gcc -o pub rsatest.c -lcrypto -g -DRSAPUBKEY
[root@localhost release]#gcc -o nopub rsatest.c -lcrypto -g 

   測試結果以下:

   實際應用中,出於安全考慮咱們通常會對私鑰文件加密。咱們能夠用以下的方式來從新生成經3DES加密後私鑰文件:
[root@localhost release]#openssl genrsa -des3 -out cipherPrv.key 1024

   這樣生成的私鑰文件使用3DES加過密的,看看內容就曉得和以前的有什麼不一樣了。頭部多了一些信息:Proc-Type和DEK-Info,猜測這確定是某種加密信息(這TM不廢話麼),可是我看不懂,現階段「會用」是首要問題:

   上述加密私鑰文件的口令是123456,分別提取RSA PUBLIC KEY和PUBLIC KEY格式的公鑰文件:
[root@localhost release]# openssl rsa -in cipherPrv.key -pubout -out cipherPub.key
[root@localhost release]# openssl rsa -in cipherPrv.key -RSAPublicKey_out -out cipherPub2.key

   在代碼中咱們須要經過下面的方式來讀取經3DES加密處理後的私鑰文件:

點擊(此處)摺疊或打開 算法

  1. RSA* getPRV(char *path_key_fullname,char* pwd)
  2. {
  3.     RSA *rsaK=RSA_new();
  4.     OpenSSL_add_all_algorithms();
  5.     BIO *BP = BIO_new_file(path_key_fullname,"rb");
  6.     if(NULL == BP)
  7.         return NULL;

  8.     rsaK=PEM_read_bio_RSAPrivateKey(BP,NULL,NULL,pwd);
  9.     return rsaK;
  10. }
    而後將tmp.c中第85行從:
   if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
   替換成:
   if((p_rsa=getPRV(path_key,"123456"))==NULL){
   從新編譯,運行後結果以下:
   關於openssl有不少值得學習的地方,空了再慢慢研究。
相關文章
相關標籤/搜索