1. 程式人生 > >RSA演算法原理及其Openssl庫函式

RSA演算法原理及其Openssl庫函式

原理見:

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

Openssl庫函式:http://blog.chinaunix.net/uid-23069658-id-4282969.html

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

    因為我主要對非對稱加密的RSA演算法比較感興趣,網上最多的就是這麼用的:
   生成私鑰檔案(其中已經包含了公鑰):

[[email protected]st 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有很多值得學習的地方,空了再慢慢研究。

相關推薦

RSA演算法原理及其Openssl函式

原理見: http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.ht

RSA演算法加解密---crypto++OpenSSL

1. OpenSSL庫 a、 方式一: #include<string.h>#include <openssl/rsa.h>#pragma comment(lib,"libeay32.lib")#pragma comment(lib,"ssleay3

RSA演算法原理——(3)RSA加解密過程及公式論證

個人分類: 演算法 上期(RSA簡介及基礎數論知識)為大家介紹了:互質、尤拉函式、尤拉定理、模反元素 這四個數論的知識點,而這四個知識點是理解RSA加密演算法的基石,忘了的同學可以快速的回顧一遍。 三、RSA加解密過程及公式論證 今天的內容主要分為三

RSA演算法原理(一)

(注意: 博文中部分公式圖片來自維基百科和 google , 需要翻牆檢視) 作者: 阮一峰 日期: 2013年6月27日 如果你問我,哪一種演算法最重 因為它是計算機通訊安全的基石,保證了加密資料不會被破解。你可以想象一下,信用卡交易被破解的後果。 進

KNN分類演算法原理及其Matlab實現

KNN演算法原理 K近鄰演算法是一種簡單的監督學習演算法。對於給定測試樣本,直接計算該樣本和訓練集的距離,將距離最近的k個“鄰居”點的類別作為參考,作為預測結果返回。 測試資料 程式碼 matlab版

02.區塊鏈共識演算法(2)PoW挖礦演算法原理及其在比特幣、以太坊中的實現

PoW挖礦演算法原理及其在比特幣、以太坊中的實現 PoW,全稱Proof of Work,即工作量證明,又稱挖礦。 大部分公有鏈或虛擬貨幣,如比特幣、以太坊,均基於PoW演算法,來實現其共識機制。 即根據挖礦貢獻的有效工作,來決定貨幣的分配。 比特幣區塊 比特幣

一致性雜湊演算法原理及其在分散式系統中的應用

分散式快取問題 假設我們有一個網站,最近發現隨著流量增加,伺服器壓力越來越大,之前直接讀寫資料庫的方式不太給力了,於是我們想引入Memcached作為快取機制。現在我們一共有三臺機器可以作為Memcached伺服器,如下圖所示。 很顯然,最簡單的策略是將每一次Memcached請求隨機發送到一臺Memca

RF(隨機森林)演算法原理及其派生演算法

1.前言 整合學習有兩個流派,一個是boosting派系,它的特點是各個弱學習器之間有依賴關係。另一種是bagging流派,它的特點是各個弱學習器之間沒有依賴關係,可以並行擬合。本文就對整合學習中

PoW挖礦演算法原理及其在比特幣、以太坊中的實現

原文地址:http://blog.51cto.com/11821908/2059711PoW,全稱Proof of Work,即工作量證明,又稱挖礦。大部分公有鏈或虛擬貨幣,如比特幣、以太坊,均基於PoW演算法,來實現其共識機制。即根據挖礦貢獻的有效工作,來決定貨幣的分配。 

K-means和K-means++的演算法原理及sklearn中引數解釋、選擇

前言:   這篇博文主要介紹k-means聚類演算法的基本原理以及它的改進演算法k-means的原理及實現步驟,同時文章給出了sklearn機器學習庫中對k-means函式的使用解釋和引數選擇。 K-means介紹:   K-means演算法是很典型的

深入學習主成分分析(PCA)演算法原理及其Python實現

一:引入問題   首先看一個表格,下表是某些學生的語文,數學,物理,化學成績統計:   首先,假設這些科目成績不相關,也就是說某一科目考多少分與其他科目沒有關係,那麼如何判斷三個學生的優秀程度呢?首先我們一眼就能看出來,數學,物理,化學這三門課的成績構成了這組資料的主成分(很顯然,數學作為第一主成分,

密碼學 RSA演算法原理詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

帶你徹底理解RSA演算法原理

1. 什麼是RSARSA演算法是現今使用最廣泛的公鑰密碼演算法,也是號稱地球上最安全的加密演算法。在瞭解RSA演算法之前,先熟悉下幾個術語 根據金鑰的使用方法,可以將密碼分為對稱密碼和公鑰密碼 對稱密碼:加密和解密使用同一種金鑰的方式 公鑰密碼:加密和解密使用不同的密碼的方式,因此公鑰密碼通常也稱為非對稱密碼

用NDK呼叫Android手機自帶的openssl函式

目標: 目前很多Android手機已經自帶了openssl庫, 即libssl.so, libcrypto.so, 以下示例實現了APK通過NDK呼叫openssl庫函式PKCS5_PBKDF2_HMAC(金鑰生成演算法) 1) 在android工程中建立 jni目錄 2)

RSA演算法原理(二)

  33478071698956898786044169   84821269081770479498371376   85689124313889828837938780   02287614711652531743087737   814467999489     ×   3674604366679959

區塊鏈共識演算法(2)PoW挖礦演算法原理及其在比特幣、以太坊中的實現

# PoW挖礦演算法原理及其在比特幣、以太坊中的實現PoW,全稱Proof of Work,即工作量證明,又稱挖礦。大部分公有鏈或虛擬貨幣,如比特幣、以太坊,均基於PoW演算法,來實現其共識機制。即根據挖礦貢獻的有效工作,來決定貨幣的分配。### 比特幣區塊比特幣區塊由區塊頭和該區塊所包含的交易列表組成。區塊

Crypto知識相關——RSA演算法原理與python實現

RSA加密與解密 RSA加密過程 1. 隨機選擇兩個不相等的質數 p 和 q e.g 選擇兩個不想等的質數 p 和 q # python 實現 import gmpy2 import random p=gmpy2.mpz(ran

理解RSA演算法原理,將有助於理解計算機

計算機通訊安全的基石 - 公鑰加密演算法 公開金鑰加密(英語:public-key cryptography,又譯為公開金鑰加密),也稱為非對稱加密(asymmetric cryptography),一種密碼學演算法型別,在這種密碼學方法中,需要一對金鑰,一個是私人金鑰,另

MD5加密演算法原理及其Go語言實現

MD5訊息摘要演演算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼雜湊函式,可以產生出一個128位元(16位元組)的雜湊值(hash value),用於確保資訊傳輸完整一致。 go 呼叫 md5 方法 新建 m

區塊鏈背後的資訊保安(1)AES加密演算法原理及其GO語言實現

# AES加密演算法原理及其GO語言實現AES是作為DES的替代標準出現的,全稱Advanced Encryption Standard,即:高階加密標準。AES加密演算法,經歷了公開的選拔,最終2000年,由比利時密碼學家Joan Daemen和Vincent Rijmen設計的Rijndael演算法被選中