1. 程式人生 > >openssl原始碼方式安裝以及簡單的實驗

openssl原始碼方式安裝以及簡單的實驗

一、引言

這仍然是資訊保安課程的一次作業,老師的要求包括以原始碼方式來安裝openssl,瞭解AES和RSA加密演算法並去嘗試呼叫openssl庫中AES和RSA演算法的API,其實總體上安裝以及實驗還是比較容易的,不過還是踩了一些坑,這裡還是記錄一下,順便試試CSDN的Markdown編輯器。

本文演示使用的作業系統為Ubuntu 16.04

二、安裝

1.下載原始碼包

從官網上可以找到原始碼包的下載,這裡我直接給出下載連結:
openssl原始碼包下載
根據官網的描述,1.1.1版本將是目前他們長期支援的版本,一直到2023年,建議使用1.0.x版本的使用者也去安裝這個最新版本。

2.編譯安裝

解壓下載的原始碼包,可以看到如下的目錄結構

目錄結構可以點開INSTALL檔案來檢視安裝說明,這裡我們按照它說的最簡說明方式來進行安裝:

on Unix (again, this includes Mac OS/X):

$ ./config
$ make
$ make test
$ sudo make install

其中最後一條命令如果不是root使用者執行,需要加上sudo,否則無法訪問系統根目錄的一些資料夾,至少我第一次沒加sudo執行時報了錯。

這四條命令執行完之後都沒有報錯的話,說明它的安裝指令碼中的所有任務都完成了,這時可以使用

openssl version

命令來檢視當前openssl的版本,不過我在這時遇到了問題,報錯是這樣的:

openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory

通過查詢資料,這個問題也很容易就解決了,原因大概是因為libssl.so.1.1被安裝指令碼放置到了/usr/local/的lib下,而命令列呼叫的時候找的是/usr/的lib下的libssl.so.1.1,所以我們只需要用如下兩條命令在/usr/的lib下建立連結檔案即可:

ln -s /usr/local/lib/libssl.so.1.1 /usr/lib/libssl.so.1.1
ln -s /usr/local/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1

值得一提的是,我百度到的命令的lib資料夾命名為lib64,這裡一定要結合自己的情況去檢視,不要直接複製命令去執行。

在解決這個問題之後,再執行之前的openssl version命令就會出現如下的顯示,也說明安裝成功了:

OpenSSL 1.1.1  11 Sep 2018

三、執行

1.MD5演算法的實驗

首先來實驗一下最簡單,也是最常用的MD5摘要演算法,說它簡單是因為它的API只有三個,用來試執行再好不過了,這裡我也是參照了一位博主的一個例子來實驗的,更詳細的講解可以看這篇部落格:

Linux下C語言使用openssl庫進行加密
用到的程式碼也是直接搬過來使用的,這裡貼一下吧:

#include <openssl/md5.h>
#include <stdio.h>
#include <string.h>
int main()
{
	MD5_CTX ctx;
	unsigned char outmd[16];
	int i=0;

	memset(outmd,0,sizeof(outmd));
	MD5_Init(&ctx);
	MD5_Update(&ctx,"hel",3);
	MD5_Update(&ctx,"lo\n",3);
	MD5_Final(outmd,&ctx);
	for(int i=0;i<16;i++)
	{
		printf("%02X",outmd[i]);
	}
	printf("\n");
	return 0;
}

這裡是我遇到的另外一個坑,如果用gcc編譯的話,會出現如下的情況:

$ gcc md5test.c 
/tmp/ccP3aNqM.o:在函式‘main’中:
md5test.c:(.text+0x42):對‘MD5_Init’未定義的引用
md5test.c:(.text+0x58):對‘MD5_Update’未定義的引用
md5test.c:(.text+0x6e):對‘MD5_Update’未定義的引用
md5test.c:(.text+0x81):對‘MD5_Final’未定義的引用
collect2: error: ld returned 1 exit status

通過百度,這個問題也很好解決,只需要引用連結庫crypto即可,如下所示:

$ gcc md5test.c -lcrypto && ./a.out
B1946AC92492D2347C6235B4D2611184

2. AES對稱加密演算法API呼叫實驗

使用API之前,最好先簡單瞭解一下AES演算法的原理(雖然呼叫API的話並不需要搞清楚演算法實現),這裡我貼出一個博主的部落格,感覺講的還是挺好的
AES加密演算法的詳細介紹與實現
而實驗部分的API我也參考了另一位博主的部落格:
OPENSSL庫的使用-AES篇

#include <openssl/aes.h>
#include <stdio.h>
int main()
{
	AES_KEY key;	//新建一個AES_KEY
	unsigned char userkey[]="test";	//金鑰字串
	unsigned char in[]="this is data";	//要加密的資訊
	unsigned char out[13];	//密文
	int res=AES_set_encrypt_key(userkey,128,&key);	//設定加密祕鑰

	AES_ecb_encrypt(in,out,&key,AES_ENCRYPT);	//設定解密祕鑰
	puts(out);			//列印密文
	res=AES_set_decrypt_key(userkey,128,&key);	//設定解密祕鑰
	
	unsigned char out2[13];	//儲存解密後的字串
	AES_ecb_encrypt(out,out2,&key,AES_DECRYPT);	//解密
	puts(out2);	//列印解密後的資訊
	return 0;
}

這裡的執行結果太鬼畜了,我貼一下圖吧:
aes程式碼執行結果

3.RSA演算法API呼叫實驗

RSA演算法還是感覺挺神奇的,這裡也是貼出一篇學習的時候參考的部落格:
對稱加密與非對稱加密,以及RSA的原理
博主文尾舉的例子好像有錯誤,我也在評論中寫出來了,不過這篇文章依然極具參考價值。
實現部分,我參考了兩位博主的程式碼:
OPENSSL庫的使用-RSA篇
如何利用OpenSSL庫進行RSA加密和解密

程式碼如下:

#include <openssl/rsa.h>
#include <stdio.h>
#include <string.h>
RSA * get_rsa(long e,int bit)
{
	RSA * rsa=RSA_new();	//新建RSA結構體指標
	if(rsa==NULL)
		return NULL;
	BIGNUM *eNum=BN_new();	//新建一個大數結構體物件
	if(!BN_set_word(eNum,e))	//設定演算法中的e
		return NULL;
	if(!RSA_generate_key_ex(rsa,bit,eNum,NULL))	//呼叫API生成RSA結構體
		return NULL;
	return rsa;
}
int main()
{
	RSA *rsa=get_rsa(0x10001,1024);	//利用e為0x10001(65537)生成一個模數n為1024的rsa指標
	//RSA_print_fp(stdout,rsa,0);		//執行這句話可以打印出生成的十六進位制格式的模數n、兩個質數p、q等
	unsigned char in[]="Hello World";	//要加密的明文
	unsigned char out[2048];	//儲存加密後的資訊
	unsigned char res[120];	//儲存解密後的資訊,長度要小於RSA_size(rsa)

	//用於加解密傳輸:
	puts(in);	//列印明文
	printf("%d\n",RSA_public_encrypt(strlen(in)+1,in,out,rsa,RSA_PKCS1_PADDING));	//公鑰加密明文生成密文
	puts(out);	//列印密文
	printf("%d\n",RSA_private_decrypt(128,out,res,rsa,RSA_PKCS1_PADDING));	//私鑰解密密文
	puts(res);	//列印解密後的密文

	//用於簽名和驗籤:
	printf("%d\n",RSA_private_encrypt(strlen(in)+1,in,out,rsa,RSA_PKCS1_PADDING));	//私鑰加密明文生成密文
	puts(out);//列印密文
	printf("%d\n",RSA_public_decrypt(128,out,res,rsa,RSA_PKCS1_PADDING));	//公鑰解密
	puts(res);	//列印解密後的密文

	RSA_free(rsa);	//釋放rsa結構體記憶體
	return 0;
}

這裡我封裝了生成RSA結構體指標的過程,RSA指標中儲存著之前原理中提過的n、p、q、e等BIGNUM型別的資料,似乎在1.1.1版本之前還可以檢視值,但是我嘗試去獲取這些資料時會報錯:

rsatest.c:20:32: error: dereferencing pointer to incomplete type ‘RSA {aka struct rsa_st}’
printf("%d\n",BN_num_bytes(rsa->n));

所以如果想檢視rsa生成的東西的話,可以使用RSA_print_fp(stdout,rsa,0)函式,效果如下:

圖片

這裡呼叫RSA_generate_key_ex(rsa,bit,eNum,NULL)傳入的bit引數即為生成的rsa指標中的模數的位數,小於512的話會報段錯誤,其實我本來還想試試能不能生成原理中舉的那個n為143的例子來著,看來也是根本不可能。

此外注意,儲存解密後的資訊的陣列,長度要小於RSA_size(rsa)函式的返回值(違反這個規則的話是無法完成解密的,解密結果是亂碼)我這裡由於modulus(之前提到的bit引數)是1024,1024/8=128,所以長度設為了120,如果改變了modulus的大小,這裡也要對應的改變。

最後來測試效果,也是比較鬼畜,所以直接截圖了:

RSA演算法結果
可見演算法執行正確。

最後通過查閱相關資料得知,RSA加密演算法有兩種用途:

第一種用法:公鑰加密,私鑰解密。---用於加解密
第二種用法:私鑰簽名,公鑰驗籤。---用於簽名