今天心血來潮忽然想搞搞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格式的公鑰文件。因爲本人密碼學基礎較薄弱,如今還不能說出二者的區別,請各位見諒,還望密碼方面的大牛們予以點撥。演示代碼以下:
- /* filename: tmp.c
- */
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<openssl/rsa.h>
- #include<openssl/pem.h>
- #include<openssl/err.h>
-
- void hexprint(char *str,int len)
- {
- int i=0;
- for(i=0;i<len;i++){
- printf("%s%02x%s",((i%16==0?"|":"")),*((unsigned char*)str+i),(((i+1)%16==0)?"|\n":" "));
- }
- if(i%16!=0)
- printf("|\n");
- }
-
- static int do_operation(RSA* rsa_ctx,char *instr,char* path_key,int inlen,char** outstr,int type)
- {
- if(rsa_ctx == NULL || instr == NULL || path_key == NULL)
- {
- perror("input elems error,please check them!");
- return -1;
- }
- int rsa_len,num;
- rsa_len=RSA_size(rsa_ctx);
- *outstr=(unsigned char *)malloc(rsa_len+1);
- memset(*outstr,0,rsa_len+1);
- switch(type){
- case 1: //pub enc
- if(inlen == 0){
- perror("input str len is zero!");
- goto err;
- }
- num = RSA_public_encrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);
- break;
- case 2: //prv dec
- num = RSA_private_decrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);
- default:
- break;
- }
-
- if(num == -1)
- {
- printf("Got error on enc/dec!\n");
- err:
- free(*outstr);
- *outstr = NULL;
- num = -1;
- }
- return num;
- }
-
- int rsa_pub_encrypt(char *str,char *path_key,char** outstr){
- RSA *p_rsa;
- FILE *file;
- int flen,rsa_len,num;
- if((file=fopen(path_key,"r"))==NULL){
- perror("open key file error");
- return -1;
- }
- #ifdef RSAPUBKEY
- if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
- #else
- if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){
- #endif
- ERR_print_errors_fp(stdout);
- return -1;
- }
-
- num = do_operation(p_rsa,str,path_key,strlen(str),outstr,1);
- RSA_free(p_rsa);
- fclose(file);
- return num;
- }
-
- int rsa_prv_decrypt(char *str,char *path_key,int inlen,char** outstr){
- RSA *p_rsa;
- FILE *file;
- int rsa_len,num;
-
- if((file=fopen(path_key,"r"))==NULL){
- perror("open key file error");
- return -1;
- }
- if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
- ERR_print_errors_fp(stdout);
- return -1;
- }
- num = do_operation(p_rsa,str,path_key,inlen,outstr,2);
- RSA_free(p_rsa);
- fclose(file);
- return num;
- }
-
- int main(int argc,char** argv){
- char *ptr_en,*ptr_de;
- int len;
-
- printf("source is :%s\n",argv[1]);
- len=rsa_pub_encrypt(argv[1],argv[2],&ptr_en);
- printf("pubkey encrypt:\n");
- hexprint(ptr_en,len);
-
- rsa_prv_decrypt(ptr_en,argv[3],len,&ptr_de);
- printf("prvkey decrypt:%s\n",ptr_de==NULL?"NULL":ptr_de);
-
- if(ptr_en!=NULL){
- free(ptr_en);
- }
- if(ptr_de!=NULL){
- free(ptr_de);
- }
-
- return 0;
- }
若是開啓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加密處理後的私鑰文件:
- RSA* getPRV(char *path_key_fullname,char* pwd)
- {
- RSA *rsaK=RSA_new();
- OpenSSL_add_all_algorithms();
- BIO *BP = BIO_new_file(path_key_fullname,"rb");
- if(NULL == BP)
- return NULL;
-
- rsaK=PEM_read_bio_RSAPrivateKey(BP,NULL,NULL,pwd);
- return rsaK;
- }
而後將tmp.c中第85行從:
if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
替換成:
if((p_rsa=getPRV(path_key,"123456"))==NULL){
從新編譯,運行後結果以下:
關於openssl有不少值得學習的地方,空了再慢慢研究。