Libgcrypt是著名的開源加密軟件GnuPG的底層庫,是一個很是成熟的加密算法庫,支持多種對稱和非對稱加密算法。如今本身隨便造輪子地寫一個加密算法程序顯然是很是不安全的,雖然OpenSSL出現了Heartbleed漏洞,可是用已經成熟的加密算法庫仍是會比不成熟的東西好不少的。最近看了看它最基本的一些功能,嘗試寫了一個AES的demo,中間仍是學到蠻多東西的,因此寫這篇手記。本文是以我寫的一個gcrypt_demo爲例,也能夠看成這個demo的解釋。html
寫個加密程序的第一步是開始加密,第二步是解密?你太天真了!第一步其實是傳入密鑰。Libgcrypt的對稱加密須要兩個重要的參數,一個是密鑰Key,一個是初始化向量Initialization Vector。後者通常由加密程序決定,而前者則須要用戶來提供。若是咱們直接拿用戶輸入的明文做爲加密和解密的密鑰,實在太不安全了,必需要通過一個搞亂的過程。而Libgcrypt提供的用來搞亂密鑰的函數是gcry_kdf_derive。git
gpg_error_t gcry_kdf_derive ( const void *passphrase, size_t passphraselen, int algo, int subalgo, const void *salt, size_t saltlen, unsigned long iterations, size_t keysize, void *keybuffer )
別看它很長很複雜,其實參數只有四部分:1.傳入的密鑰明文和長度;2.打亂用的算法;3.鹽串和鹽串的長度、迭代次數等打亂用的參數;4.生成的打亂後的密鑰串。利用這個函數就能獲得一個很好的密鑰串了。舉個例子,我把明文密鑰放在buf裏面,而輸出的密鑰放在buf裏面,選用PBKDF2和SHA512算法進行散列,鹽串存在SALT裏面,迭代次數爲ITER。github
gpg_error_t err = gcry_kdf_derive(buf, strlen(buf), GCRY_KDF_PBKDF2, GCRY_MD_SHA512,SALT, sizeof(SALT), ITER, LEN_OF_RETKEY, outbuf);
基本上全部Libgcrypt的函數返回值都是錯誤句柄,接收它用來判錯就好。算法
在得到密鑰以後,咱們就須要對加密的句柄進行設置。咱們須要選定加密算法,順便開好用來加密的緩存區間。咱們採用比較簡單的塊模式進行加密,因此首先咱們須要知道咱們選定的加密算法所接受的密鑰長度和塊長度。在Libgcrypt中,加密算法用宏來標識,你須要傳遞指定的宏,來告知它你想用哪一種加密算法。個人demo程序用的是AES256算法,可是爲了通用起見,咱們仍是用一個CIPHER_ALGO來指代咱們用的加密算法的具體名字。緩存
首先咱們須要獲得基本的數據:安全
size_t key_size = gcry_cipher_get_algo_keylen(CIPHER_ALGO); size_t block_size = gcry_cipher_get_algo_blklen(CIPHER_ALGO); size_t block_required=file_size/block_size; if (file_size % block_size != 0){ block_required++; }
file_size是要被加密的數據文件的總大小。利用get_algo_keylen()和get_algo_blklen()兩個函數咱們能夠獲得選定的算法的密鑰長度和塊長度,而後計算總共有多少個塊。
有了這些信息,咱們就能夠建一個句柄:函數
cipher_err=gcry_cipher_open(&cipher_hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS); cipher_err=gcry_cipher_setkey(cipher_hd,key,key_size); cipher_err=gcry_cipher_setiv(cipher_hd, iv, block_size);
第一句創建加密句柄。第一個參數cipher_hd是加密用的句柄,類型是gcry_cipher_hd_t;第二個參數是加密算法,第三個參數和第四個參數指定加密的模式是塊模式,詳細的內容能夠看libgcrypt的文檔。第二句設置密鑰,第三句設置初始化向量。這裏接收的初始化向量是隨便的一個字符串,只要足夠長,超過block_size通常就沒什麼問題了,固然,能和block_size同樣長就最好好。最後創建好讀入緩存和輸出緩存區就好。ui
char *input_buf = (char*)malloc(file_size); char *cipher_buffer = malloc(block_size*block_required); memset(cipher_buffer, 0, block_size*block_required);
作了這麼多準備工做,終於開始加密了呢!假設fin是源數據流,而fout是輸出流,都是文件,那麼咱們能夠這樣寫加密的程序:加密
fread(input_buf,1,file_size,fin); //將數據讀入輸入緩存 memcpy(cipher_buffer,input_buf,block_required*block_size); //將數據複製到輸出緩存中 cipher_err=gcry_cipher_encrypt(cipher_hd,cipher_buffer, //在輸出緩存上進行加密 block_required*block_size,NULL,0); fwrite(cipher_buffer,1,block_required*block_size,fout); //講輸出緩存輸出到文件 gcry_cipher_close(cipher_hd); fclose(fin); fclose(fout); //關閉加密句柄和文件流
要注意的是加密用的函數gcry_cipher_encrypt():指針
gcry_error_t gcry_cipher_encrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
它接收的參數分爲輸出緩存和源數據。可是當源數據的指針爲NULL時,它會直接以輸出緩存中的數據爲源數據,加密後放回去。加密到此結束,戛然而止。準備工做佔了大頭,真正的最核心的工做其實就這麼一點。
對稱加密完天然要解密,否則就沒有意義了。解密的初始化工做和加密基本一致,只要用相同的密鑰和初始化向量,就能夠解密了。只要用gcry_cipher_decrypt()替代加密用的函數就能夠了。我這裏簡單化處理了,若是最後全是0,那麼就截取尾端。其實這麼作是有問題的,應該加一個魔數標誌結束啥的,不過demo嘛,就偷懶一下吧。加密純文本文件的時候也沒有這個問題。
fread(decry_buf,1,file_size,fin); cipher_err=gcry_cipher_decrypt(cipher_hd,decry_buf,file_size,NULL,0); while (decry_buf[file_size-1]==0){ file_size--; } fwrite(decry_buf,1,file_size,fout); gcry_cipher_close(cipher_hd); fclose(fin); fclose(fout);
利用AES對稱加密能夠建造一個最基本的安全的數據交換體系。到目前爲止,AES256算法尚未有效的破解手段,應該說仍是很安全的。這個demo很簡陋,有不少問題,不過做爲demo已經足夠了。不造輪子,而是使用成熟的庫,應該算是優秀工程師的基本素養吧。最後但願你們喜歡(*^^)v