1. 程式人生 > >關於openssl幾個API的一點小收穫

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

    今天心血來潮突然想搞搞openssl了,趁著端午小假,剛好有空可以鼓搗孤島自己喜歡的東西,出去東奔西跑的實在太造孽了,還是宅起來給自己充充電吧。下載openssl最新程式碼1.0.1g,修復了“心血漏洞”那個版本。編譯安裝那些小兒科的東西就不再浪費筆墨了,如果出現標頭檔案或者庫檔案之類的錯誤,請在本人部落格裡尋找相關文章,應該主要集中在動態庫那幾篇博文。反正我在自己虛擬機器裡安裝的時候是妥妥滴。

    因為我主要對非對稱加密的RSA演算法比較感興趣,網上最多的就是這麼用的:
   生成私鑰檔案(其中已經包含了公鑰):
[[email protected] release]#openssl genrsa -out plainPrv.key 1024

   
   然後再從這個私鑰檔案裡將公鑰提取出來,儲存到檔案裡:
[[email protected] release]#openssl rsa -in plainPrv.key -pubout -out plainPub.key

    RSA一般有兩種應用場景:
   1、公鑰加密、私鑰解密:這是資料安全通訊領域最常見情形;
   2、私鑰加密、公鑰解密:這主要用於數字簽名。
   兩種方式,一通百通,本文只看第一種場景。
  關於測試程式碼,網上到處都是,也都基本能用,我就先不摘抄了。大家問的最多的問題就是在讀取公鑰檔案時,PEM_read_RSA_PUBKEY()
函式和PEM_read_RSAPublicKEY()的疑惑。為什麼讀取私鑰檔案用的PEM_read_RSAPrivateKey(),針對上述openssl命令生成的公鑰檔案,在讀取其內容時用對稱的PEM_read_RSAPublicKEY()介面卻會報錯,必須要用PEM_read_RSA_PUBKEY()才可以。

   其實,我們要是看看一兩個檔案內容就明白了:
[[email protected] 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-----

[
[email protected]
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程式碼,所以提取公鑰時我改用下面的命令:
[[email protected] release]#openssl rsa -in plainPrv.key -RSAPublicKey_out -out plainPub.key

   這樣做完的結果是,首先公鑰檔案的內容有點變化:
[[email protected] test_openssl]# cat plainPub2.key
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAOUZXHNMNWGcLXpJb4PRYbyG+bXDO2avS8GHZUroJTuT3SZ2sLHhvLVR
FxnqJLorGibFKpaYr2DYLSJF3zHHs7LKQEcq22MjHkOKrfAtcM/apTiaNIi8c/cz
CCs5HYnBriY2vW94zZzWnJefnEh9mzYgWeJwy6CP7SxeKTWktbAjAgMBAAE=
-----END RSA PUBLIC KEY-----
[[email protected] 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格式的公鑰檔案。由於本人密碼學基礎較薄弱,現在還不能說出兩者的區別,請各位見諒,還望密碼方面的大牛們予以點撥。演示程式碼如下:

點選(此處)摺疊或開啟

  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.     if((file=fopen(path_key,"r"))==NULL){
  78.         perror("open key file error");
  79.         return -1;
  80.     }
  81.     if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
  82.         ERR_print_errors_fp(stdout);
  83.         return -1;
  84.     }    
  85.     num = do_operation(p_rsa,str,path_key,inlen,outstr,2);
  86.     RSA_free(p_rsa);
  87.     fclose(file);
  88.     return num;
  89. }
  90. int main(int argc,char** argv){
  91.     char *ptr_en,*ptr_de;
  92.     int len;
  93.     printf("source is :%s\n",argv[1]);
  94.     len=rsa_pub_encrypt(argv[1],argv[2],&ptr_en);
  95.     printf("pubkey encrypt:\n");
  96.     hexprint(ptr_en,len);
  97.     rsa_prv_decrypt(ptr_en,argv[3],len,&ptr_de);
  98.     printf("prvkey decrypt:%s\n",ptr_de==NULL?"NULL":ptr_de);
  99.     if(ptr_en!=NULL){
  100.         free(ptr_en);
  101.     }
  102.     if(ptr_de!=NULL){
  103.         free(ptr_de);
  104.     }
  105.     return 0;
  106. }

    如果開啟RSAPUBKEY巨集,則用PEM_read_RSA_PUBKEY()來讀取公鑰檔案;否則用PEM_read_RSAPublicKey()讀取:
[[email protected] release]#gcc -o pub rsatest.c -lcrypto -g -DRSAPUBKEY
[[email protected] release]#gcc -o nopub rsatest.c -lcrypto -g 

   測試結果如下:

   實際應用中,出於安全考慮我們一般會對私鑰檔案加密。我們可以用如下的方式來重新生成經3DES加密後私鑰檔案:
[[email protected] release]#openssl genrsa -des3 -out cipherPrv.key 1024

   這樣生成的私鑰檔案使用3DES加過密的,看看內容就曉得和之前的有什麼不同了。頭部多了一些資訊:Proc-Type和DEK-Info,猜想這肯定是某種加密資訊(這TM不廢話麼),但是我看不懂,現階段“會用”是首要問題:

   上述加密私鑰檔案的口令是123456,分別提取RSA PUBLIC KEY和PUBLIC KEY格式的公鑰檔案:
[[email protected] release]# openssl rsa -in cipherPrv.key -pubout -out cipherPub.key
[[email protected] 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有很多值得學習的地方,空了再慢慢研究。
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script> 閱讀(14329) | 評論(2) | 轉發(2) | 給主人留下些什麼吧!~~ 13_avatar_small.jpg

2015-12-23 14:05:21

太感謝了,解決了大問題。PEM_read_RSA_PUBKEY 不知道這個地方為什麼這麼用,有啥研究成果木有

回覆 | 舉報 81_avatar_small.jpg

2014-11-25 17:05:41

太棒了,謝謝分享~1.gif

回覆 | 舉報 評論熱議

相關推薦

關於opensslAPI一點收穫

    今天心血來潮突然想搞搞openssl了,趁著端午小假,剛好有空可以鼓搗孤島自己喜歡的東西,出去東奔西跑的實在太造孽了,還是宅起來給自己充充電吧。下載openssl最新程式碼1.0.1g,修復了“心血漏洞”那個版本。編譯安裝那些小兒科的東西就不再浪費筆墨了,如果出現標頭

【Linux】linux常用基本命令 白專區簡單易懂

ls -l 17. 命令補全 linu 刪除目錄 poweroff 環境 密碼 family 顯示日期的命令 date顯示日歷的命令 cal -s, --hctosys以硬件時鐘為準,校正系統時鐘hwclock,clock:顯示硬件時鐘 -w, --sy

使用Formik輕松開發更高質量的React表單(四)其他API解析

else errors method however obj disable user etc gree (下面部分內容正處於翻譯中,敬請稍等......) <Field /> <Field /> will automagically hook up

實現毫秒級和納秒級計數的API--timeGetTime、GetTickCount、QueryPerformanceCounter

Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declar

JavaWeb基礎總結之Js經典的案例

 (1)動態顯示當前系統時間 <body> <p>當前時間:<span id="times"></span></p> </body> <script> function get(){

深入 TypeScript - 2( 常用的技巧)

寫在最前面 剛開始寫 typescript 遇到的問題和簡單的解決方案。 Q&A 1、是否所有變數都需要做型別註解? 這個分情況,原則上來說,我們希望能對所有的值都做型別註解。 對於TS編譯器來說,如果宣告變數時沒有做型別註解,那麼TS會根據賦值自動推匯出變數型別。這

測試中實用的工具

1 身份證號碼生成器 我是做p2p理財業務的,測試中會用到身份證。自己編的話太隨意,有時候還無法通過一些規則 身份證號碼和姓名_身份證號碼和真實姓名大全_身份證號碼大全防沉迷_身份證號碼查詢​sfz.ckd.cc 2密碼生成器 公司用阿里雲,阿里雲的密碼我一般就隨機生成,可以設定你要的規則,生成符合規

source insight下實用的工具

1、SourceMonitor使用 C語言度量值(C Metrics)、  總函式(Lines):包括空行在內的程式碼行數; 語句數目(Statements):在C語言中語句就是以分號結尾的。分支語句IF,迴圈語句FOR,跳轉語句都被計算在內,預處理語句#include

胡八一之Java(六):表示式的簡單的陷阱

1、複合賦值運算子的陷阱 a=a+5與a +=5 是有區別的。a +=5等價於 a=(a的型別)(a+5);這就是複合運算子中包含的隱式型別轉換。 在什麼時候會遇到此型別的錯誤呢? short a =5; a = (a-2); 此句編譯不通過,把一個int

yum源使用的報錯總結

伺服器上的yum突然不好使用,使用yum時有如下幾個保持,解決方案如下: 1)Error: Cannot retrieve repository metadata (repomd.xml) for repository: rpmforge. [[email protected] src

關於CString的一點收穫,CString在控制檯程式中輸出到螢幕

CString是一個非常好用的類,這就不用多說了,但是由於只能在MFC中使用,使得他的使用變得非常有限。筆者通過上網研究和親自實踐發現在控制檯程式中也可以輕鬆使用CString。 首先,要在控制檯中使用CString必須要加入標頭檔案 #include <atlst

使用者行為分析需要知道的埋點技巧

使用者行為分析系統是指由第三方提供的集合了資料採集SDK、資料分析模型、分散式演算法與儲存架構的使用者屬性與行為事件資料分析的系統。比如國外MixPanel、Heap等,使用者行為資料分析的前提是在前期埋點時打好紮實的基礎。 事件觸發的條件、需要追蹤的屬性以及想要分析的

初識PLSQL 簡單的程式

1.順序程式程式碼 declare V_counter number:=1; begin loop dbms_output.put_line('V_counter當前的值為

剛接觸SkyLine的一點收穫與感觸

因為剛接觸Skyline不到一個星期,也怕把學習到的忘記掉,所以寫一點學習到的一些皮毛的東西,趕緊記錄一下,怕回頭忘記 1.網上關於web端的開發非常多,也有很多牛人分享自己的經驗,所以學習起來也相對更便捷 2.關於外掛的開發,網上基本上沒有,我也只找到一個是用C#做的,顯然和Skyline的html外掛

新手學Python必看的練手專案,輕鬆不枯燥哦!

Python是一種面向物件的解釋型程式語言,原始碼與直譯器CPython遵守GPL協議,Python語法簡潔清晰。 語法簡潔清晰,那麼我們用少量的Python程式碼能做哪些有趣的東西?溫馨提示:文末必看。   一、畫愛心表白 1、圖形都是由一系列的點(X,Y)構成的曲線,由於X

爬蟲-簡單的檔案

本週第一次接觸爬蟲,在https://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html上簡單入門後便在馬神的指導下寫了一些簡單的爬蟲。 因為以前web,html,css,xpath都沒有接觸過

112. 路徑總和(樹遞迴中的一點收穫_什麼是子節點)

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。 說明: 葉子節點是指沒有子節點的節點。 示例: 給定如下二叉樹,以及目標和 sum = 22, 5.

[Termux] 有意思的軟體包

因式分解小軟體 pkg install coreutils 然後使用 factor number命令呼叫 如:factor 200 反饋:200: 2 2 2 5 5 Figlet 簡單方便地用符號畫出英文字母 | 不支援中文 ap

精妙的問題(By Divide and Conquer)

所謂分治法就是分而治之的意思,即將一個不易解決的大問題分解為幾個易於解決的小問題,然後再將各個小問題的解合併起來就是大問題的解。這種方法雖然簡單,但是對於很多問題都很有效,下面試舉幾例: 問題一:有效字串個數問題能在通道上傳遞的字串滿足如下兩條性質:該字串中只包含'a'、

有意思的程式,當無限迴圈遇到sleep會發生什麼 !!!∑(゚Д゚ノ)ノ

今天翻文件的時候找到了幾個剛學Java的時候的小程式,跟大家共享一個,其他點 這裡 下載(下載連線包含所有展示的程式,如果實在沒幣可以私聊我,我給你發一份,有幣就支援一下把,謝謝) 這是倒數第二個的程式碼,非常簡單,矩陣的那個複雜一些,但是更有意思,點 這裡下載(下載連線包