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