標籤:國密 sm2 sm3 openssl 摘要演算法
openssl簡介
openssl是一個功能豐富且自包含的開源安全工具箱。它提供的主要功能有:SSL協議實現(包括SSLv2、SSLv3和TLSv1)、大量軟演算法(對稱/非對稱/摘要)、大數運算、非對稱演算法密鑰產生、ASN.1編解碼庫、認證請求(PKCS10)編解碼、數位憑證編解碼、CRL編解碼、OCSP協議、數位憑證驗證、PKCS7標準實現和PKCS12個人數位憑證格式實現等功能。
openssl採用C語言作為開發語言,這使得它具有優秀的跨平台效能。openssl支援Linux、UNIX、windows、Mac等平台。openssl目前最新的版本是openssl-1.0.1i。
更多的相關知識請參閱鋪天蓋地的各種官方非官方文檔。今天來實現怎樣將一個新演算法添加進openssl中。
問題描述
openssl中雖然整合了很多經典演算法,對稱演算法、非對稱演算法、摘要演算法等等,但有時候我們需要用到自訂的演算法,比如要實現一個自訂的摘要演算法,需要藉助openssl帶的大數庫來實現,同時又希望將自己實現的演算法封裝到openssl裡面,以便統一調用或者方便項目改造和替換。最具現實意義的案例就是國密SM2等的改造。本例將用國密中的摘要演算法SM3作為添加對象,在windows下面進行編譯調試,僅描述添加步驟,所以本文的前提是已經完成了基於openssl大數庫的SM3的c語言實現。本文僅供依葫蘆畫瓢,要理解原理請自行查閱別的資料或者自己解讀源碼,或者等我的下一篇文章。
具體步驟
一、準備工作
1、 下載openssl源碼,openssl目前最新的版本是openssl-1.0.1i。:
2、 sm3的c語言實現。
3、 windows下perl、vc的安裝配置。
4、 熟悉openssl的編譯過程。
二、SM3代碼改造【這個步驟涉及到的檔案全部建立,存放目錄關係】
1、 涉及到的檔案7個:
1、 【建立檔案】sm3.h
這是最重要的一個標頭檔,裡面定義了演算法可以匯出的演算法,也就是裡面聲明的每一個函數最後都會產生對應的libeay.num(將在第五章中具體講),能夠匯出到dll中供外部調用。幾個主要的聲明如下:
定義sm3的摘要長度:
#define SM3_DIGEST_LENGTH 32 /* sm3 摘要長度為256位 32位元組,md5的摘要長度128位 not sure*/
定義SM3_CTX,這個結構其實就是對應evp封裝中EVP_MD_CTX的md_data:
typedef struct SM3state_st{unsigned long long total_length;unsigned char message_buffer[64];size_t message_buffer_position;size_t V_i[8];size_t V_i_1[8];size_t T_j[64];} SM3_CTX;
聲明函數,包括init、update、final,這幾個函數會在封裝的時候被內部調用,當然在dll中匯出了也可以外部調用:
int SM3_Init(SM3_CTX*c);intSM3_Update(SM3_CTX *c, const void *data, size_t len);intSM3_Final(unsigned char *md, SM3_CTX *c);unsignedchar *SM3(unsigned char *d, size_t n, unsigned char *md);
2、 【建立檔案】sm3_locl.h
這個標頭檔中包含一些要用到的宏定義。主要定義了本演算法實現中需要用到的內建函式,不能被匯出到dll中供外部調用。
宏定義:
#ifndefROTATE#defineROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))#endif #define EE(b,c,d) ((b & c) | ((~ b) & d))#define FF(b,c,d) ((((c)^ (d)) & (b)) ^ (d))#define GG(b,c,d) (((b)& (c)) | ((b) & (d)) | ((c) & (d)))#define HH(b,c,d) ((b)^ (c) ^ (d))#define HH1(a) (a^ (ROTATE(a, 9)) ^ (ROTATE(a, 17)))#define HH2(a) (a^ (ROTATE(a, 15)) ^ (ROTATE(a, 23)))內建函式:voidinit_T_j(size_t *T_j);voidinit_V_i(size_t *V_i);voidCF(size_t *T_j,size_t *V_i, unsigned char *B_i, size_t *V_i_1);
3、 【建立檔案】sm3_dgst.c
實現sm3.h和sm3_locl中聲明的函數,除了函數SM3(將在sm3_one.c中實現)。
4、 【建立檔案】sm3_one.c
實現sm3.h中聲明的函數SM3。
5、 【建立檔案】sm3.c
實現對檔案的摘要,如用在openssl作為命令列工具,檔案名稱和摘要演算法名作為選項參數實現使用演算法對檔案進行摘要。
6、 【建立檔案】sm3test.c
演算法的test檔案。
7、 【建立檔案】Makefile
編譯的Makefile檔案。裡面描述在編譯的時候以上的幾個檔案的角色、編譯過程、依賴關係等。
三、EVP封裝相關代碼
1、 涉及到的檔案4個:
2、 【手動修改】evp.h
evp在openssl裡面的主要做的是封裝的事情,因此首先在evp.h標頭檔中添加新演算法sm3:
#ifndef OPENSSL_NO_SM3const EVP_MD *EVP_sm3(void);#endif
說明:EVP_sm3()函數將返回返回一個sm3的EVP_MD的結構,這個結構以及EVP_sm3()函數都在m_sm3.c中定義。
3、 【建立檔案】m_sm3.c
這是放在EVP目錄中關於新演算法的一個重要檔案,定義sm3的EVP_MD結構,EVP_MD結構原型在evp.h中:
sm3的EVP_MD結構:
static const EVP_MD sm3_md={NID_sm3,//這個需要定義了OID之後產生,這個步驟在第四章。NID_sm3WithRSAEncryption,//這個地方sm3WithRSA只做樣本SM3_DIGEST_LENGTH,//SM3的摘要長度,在sm3.h中定義0, //flaginit, //結構中的init函數,指向自己聲明在sm3.h中的SM3_Initupdate,//同上final,//同上NULL,NULL,EVP_PKEY_RSA_method,//同上,只做樣本SM3_CBLOCK, //在sm3.h中聲明sizeof(EVP_MD *)+sizeof(SM3_CTX), };
4、 【手動修改】c_alld.c
openssl的演算法封裝之後使用時需要載入演算法,實現SM3的添加:
#ifndefOPENSSL_NO_SM3EVP_add_digest(EVP_sm3());#endif
5、 【手動修改】Makefile
將編譯時間候的LIBSRC、LIBOBJ中加入SM3新演算法,並且添加sm3各種標頭檔的依賴關係。
四、OID產生
1、 涉及到的檔案7個:
2、 【手動修改】objects.txt
添加sm3的OID,特別說明:此處僅作樣本,sm3的實際OID是:
rsadsi 2 12 :SM3 : sm3
添加sm3WithRSAEncryption,這個是對應PKCS1的,只是為了讓sm3的EVP_MD結構有值填充,實際添加sm3的本意不用於RSA,這裡僅作樣本。
pkcs1 15 : RSA-SM3 : sm3WithRSAEncryption
3、 【執行命令自動更新】obj_mac.h和obj_mac.num
開啟cmd,切換到目錄下源碼的\crypto\objects\所在的目錄下,
執行命令:perl objects.pl objects.txt obj_mac.num obj_mac.h
obj_mac.h和obj_mac.num這兩個檔案都會因為objects.txt的修改而更新。增加的內容如:
4、 【手動修改】objects.h
將obj_mac.h中新增的內容同步到objects.h中。(其實這一步不做也沒有影響,因為之後不用這個檔案來產生obj_dat.h。objects.h的內容沒有obj_mac.h的內容全面,千萬不能用objects.h來obj_dat.h,一定要用obj_mac.h來產生才是正確的。切記,血的教訓!)
5、 【執行命令自動更新】obj_dat.h
執行命令:perl obj_dat.pl obj_mac.h obj_dat.h
將會自動更新obj_dat.h這個檔案,新增的內容如下:
注意看圖上最後兩排
Line 4672: 921,/* OBJ_sm3 1 2 840 113549 2 12 */Line 4843: 920,/* OBJ_sm3WithRSAEncryption 1 2 840 113549 1 1 15 */
最後帶的一串數字本來是演算法的OID,因為上面第2步的objects.txt中是亂填的,所以這個不是真實的OID。僅作樣本。
6、 【手動修改】obj_xref.txt
添加:
sm3WithRSAEncryptionsm3rsaEncryption
7、 【執行命令自動更新】obj_xref.h
執行命令:perl objxref.pl obj_xref.txt obj_xref.h
obj_xref.h更新後的添加的內容如:
五、make相關設定
1、 涉及到的檔案\util\下面5個:
2、 【手動修改】mkfiles.pl
添加:
"crypto/sm3",
添加上包含sm3的演算法源碼的目錄,這個目錄中包含sm3的Makefile檔案。之後就會按照Makefile進行編譯。
3、 【手動修改】mkdef.pl
新增內容
加上新演算法的標頭檔等,可以在dll中匯出演算法函數。
4、 【執行命令自動更新】libeay.num
執行命令:perl util/mkdef.pl crypto update
Libeay.num更新後增加的內容如:
5、 【手動修改】mk1mf.pl
新增內容如:
6、 【手動修改】sp-diff.pl
新增內容如:
7、 涉及到的檔案\crypto\下面2個:
8、 【手動修改】crypto-lib.com
新增內容如:
9、 【手動修改】install-crypto.com
新增內容如:
10、 涉及到的檔案根目錄\下面5個:
11、 【手動修改】
新增內容Makefile、Makefile.bak、Makefile.org、makevms.com、INSTALL.VMS
到這裡,源碼需要添加和修改的就結束了,下面將介紹編譯和測試。
六、編譯
1、 在cmd下,首先設定VC環境,執行VC目錄下的vcvars32.BAT檔案,如:
2、 cmd下進入openssl源碼所在的目錄,依次執行命令【編32位,如果64位可能缺ml64.exe】:
1)執行命令:perl Configure VC-WIN32 no-asm
2) 執行命令:ms\do_ms
3)執行命令:nmake-f ms\ntdll.mak:這個是動態庫:如果編譯成功,最後的輸出都在out32dll目錄下: 包括可執行檔 、兩個dll和兩個lib檔案【nmake -fms\nt.mak
這是靜態庫的編譯命令,輸出在out32目錄下】。編譯成功
4) 執行命令:nmake -fms\ntdll.mak test
5)執行命令:nmake -f ms\ntdll.mak install
七、測試
1、 API編程測試,在vs建工程,將openssl目錄下產生的out32dll檔案夾下的libeay32.dll和libeay32.lib到工程。
2、 設定工程的附加庫和附加元件封裝含目錄:
右鍵工程->屬性-> C/C++ ->常規->附加元件封裝含目錄
右鍵工程->屬性-> 連結器 ->常規->附加庫目錄
3、 sm3test.c源碼如:
sm3test.c
#include <stdio.h>#include <openssl/evp.h>#include <openssl/sm3.h>#pragma comment(lib, "libeay32.lib")static size_t hash[8] = {0};void out_hex(size_t *list1){size_t i = 0;for (i = 0; i < 8; i++){printf("%08x ", list1[i]);}printf("\r\n");}main(int argc, char *argv[]){EVP_MD_CTX mdctx;const EVP_MD *md;char mess1[] = "abc";char mess2[] = "abc";unsigned char md_value[EVP_MAX_MD_SIZE];int md_len, i;//使EVP_Digest系列函數支援所有有效資訊摘要演算法OpenSSL_add_all_digests();argv[1] = "sm3";if(!argv[1]) {printf("Usage: mdtest digestname\n");exit(1);}//根據輸入的資訊摘要函數的名字得到相應的EVP_MD演算法結構md = EVP_get_digestbyname(argv[1]);//md = EVP_sm3();if(!md) {printf("Unknown message digest %s\n", argv[1]);exit(1);}//初始化資訊摘要結構mdctx,這在調用EVP_DigestInit_ex函數的時候是必須的。EVP_MD_CTX_init(&mdctx);//使用md的演算法結構設定mdctx結構,impl為NULL,即使用預設實現的演算法(openssl本身提供的資訊摘要演算法)EVP_DigestInit_ex(&mdctx, md, NULL);//開始真正進行資訊摘要運算,可以多次調用該函數,處理更多的資料,這裡只調用了兩次EVP_DigestUpdate(&mdctx, mess1, strlen(mess1));//EVP_DigestUpdate(&mdctx, mess2, strlen(mess2));//完成資訊摘要計算過程,將完成的摘要資訊儲存在md_value裡面,長度資訊儲存在md_len裡面EVP_DigestFinal_ex(&mdctx, md_value, &md_len);//使用該函數釋放mdctx佔用的資源,如果使用_ex系列函數,這是必須調用的。EVP_MD_CTX_cleanup(&mdctx);printf("Digest is: ");for(i = 0; i < md_len; i++) printf("%02x", md_value[i]);printf("\n");//SM3("abc",3,hash);//out_hex(hash);system("pause");}int main1(int argc, char* argv[]){SM3_CTX *c = (SM3_CTX*)malloc(sizeof(SM3_CTX));SM3_Init(c);//SM3_Final_dword(hash, c);SM3_Update(c, "abc", 3);SM3_Final(hash, c);out_hex(hash);//66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0SM3_Init(c);SM3_Update(c, "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", 64);/*for (i = 0; i<16; i++){SM3_Update(c, "abcd", 4);}*/SM3_Final(hash,c);out_hex(hash);//debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732//SM3("abc",3,hash);//out_hex(hash);system("pause");return 0;}
4、 sm3test.c運行使用sm3演算法對“abc”進行摘要後的結果
和國密標準的附錄樣本一致:
八、總結
本例以摘要演算法為例,具體介紹了一下通過直接添加源碼的方式怎樣在openssl裡面添加新演算法,而非engine方式。當然如果認為engine方式更方便,則請忽略本文。本文只是在描述怎麼做,但是沒有講為什麼。因為例如openssl本身的封裝機制是需要解讀源碼才能更好地理解上述文中為什麼需要做那些步驟,因為這些步驟已經夠複雜了,所以解讀源碼將另起一篇文章。本文僅供依葫蘆畫瓢,要理解原理請自行查閱別的資料或者自己解讀源碼,或者等我的下一篇文章。
任何疑問或者指正請聯絡我,謝謝!
小夥伴們加油!GOOD LUCK!
源碼方式向openssl中添加新演算法完整詳細步驟(樣本:摘要演算法SM3)【非engine方式】