使用LibTomCrypt做AES加解密
我正打算写一篇关于使用OpenSSL的文章,但不久前有个朋友问我关于LibTomCrypt的一些小问题,联系到自己一知半解的,于是我想先把LibTomCrypt的加解密弄熟悉了,再去弄做了复杂抽象的OpenSSL,用代码实践总比我去看一堆加解密理论的文档有效多了,而且很直观。这篇文章就写AES对称加密吧。
AES,Advanced Encryption Standard ,又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。在加解密库上使用LibTomCrypt的原因一般有两个:1,API接口简单,且是C实现,移植方便;2,协议宽松,使用public domain协议,开源,商业,闭源等都不会引起问题;参考LibTomCrypt的源代码,其实现对称加密在接口上形式一样,使用起来很方便,而且支持很多种模式的对称加密,有ECB,CBC, CTR,CFB,OFB等,用数学公式来表达很直观:
k——密匙 ;P —— 明文块 ;E —— 加密函数;C —— 密文块;其中C-1代表初始化向量; ECB —— Ci = Ek(Pi) ; CBC —— Ci = Ek(Pi ^Ci-1) ;
CTR —— C-1 = C-1+1 (mod 2w) , Ci = Pi ^ Ek(C-1) ; w为块大小,以字节为单位;第一个公式表示计数不会超过2w大小;
CFB —— Ci = Pi^C-1 , C-1 = Ek(Ci) ; OFB —— C-1 = Ek(C-1) , Ci = Pi^C-1 ;
在LibTomCrypt的pdf文档中,作者偏爱CTR模式,原因如下:
1. 没有OFB或CFB模式下可能出现的短循环; 2. 在选择明文攻击下被证明是安全的; 3. 技术上不需要解密程序; 4. 允许随机访问明文;
5. 允许加密时的块大小不一定要等于加解密算法的区块大小;
第1,3点我不是很明白,第5点CFB和OFB也可以实现。我在例子中选择CBC模式,一方面是因为它用到了对称加密中的所有元素,密匙长度,初始化向量这些,便于学习对称加密;另一方面是因为它比其他用到初始化向量的加密模式简单。
先要编译LibTomCrypt ,因为LibTomCrypt 1.06版本起不再提供自己的数学库接口,也不依赖于任何第三方库(比如LibTomMath,LibTomFastMath),所以要先编译一个数学库,这里用LibTomMath,得到静态Library,然后再编译得到LibTomCrypt的静态库。最后从LibTomCrypt的源代码中把头文件和静态库一起加到要开发的项目中即可;我写的加密解密代码如下:
#include
int _tmain(int argc, _TCHAR* argv[]) {
int error = 0;//返回值 int index = 0;//加解密器索引
unsigned char IV[16] = {0};//初始化向量
unsigned char key[16] = {“12345678″};//对称密匙 symmetric_CBC cbcAES = {0};//对称加解密内部结构
FILE *filePlainText = 0,*fileCipherText = 0,*decryptFile = 0;//文件 unsigned char ct[16] = {0},pt[16] = {0};//加解密数据块 size_t len = 0;//长度 //注册加解密器
if(-1 == register_cipher(&aes_desc)){
printf(_T(“register_cipher(&aes_desc) failed”)); return (-1); }
//随机初始化IV向量
sprintf((char*)IV,”%ld”,((long)rand())%((long)1<<15)); //查找对应索引
index = find_cipher(“aes”); if(index == -1){
printf(_T(“find_cipher(\\”aes\\”) failed”)); goto EndOfCrypt; }
//////////////////////////////////////////////////////////////////////// //加密流程
//开始CBC模式加解密
error = cbc_start(index,IV,key,sizeof(key),0,&cbcAES); if(error != CRYPT_OK){
printf(_T(“cbc_start error:%s”),error_to_string(error)); goto EndOfCrypt; }
//打开要加密的文件
filePlainText = fopen(“c:\\\\plaintext.txt”,”r”); if(filePlainText == 0){
printf(_T(“fopen(\\”c:\\\\plaintext.txt\\”,\\”r\\”) failed”)); cbc_done(&cbcAES); goto EndOfCrypt; }
//创建加密后的文件
fileCipherText = fopen(“c:\\\\ciphertext.txt”,”w”); if(filePlainText == 0){
printf(_T(“fopen(\\”c:\\\\ciphertext.txt\\”,\\”w\\”) failed”)); fclose(filePlainText); cbc_done(&cbcAES);
goto EndOfCrypt; }
//CBC模式要求是块大小的倍数,末尾可以pad上自己的消息,我这里简单置零了 while(!feof(filePlainText)){ memset(pt,0,sizeof(pt)); memset(ct,0,sizeof(ct));
len = fread(pt,sizeof(pt[0]),16,filePlainText); if(len < 1)//没有读成功 break; //加密
error = cbc_encrypt(pt,ct,16,&cbcAES); if(error != CRYPT_OK){
printf(_T(“cbc_encrypt error:%s”),error_to_string(error)); break; }
fwrite(ct,sizeof(ct[0]),16,fileCipherText); }
//关闭文件
fclose(filePlainText); fclose(fileCipherText); //完成流加解密 cbc_done(&cbcAES); //end 加密流程
////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////// //解密流程
//开始CBC模式加解密
error = cbc_start(index,IV,key,sizeof(key),0,&cbcAES); if(error != CRYPT_OK){
printf(_T(“cbc_start error:%s”),error_to_string(error)); goto EndOfCrypt; }
//打开要解密的文件
fileCipherText = fopen(“c:\\\\ciphertext.txt”,”r”); if(fileCipherText == 0){
printf(_T(“fopen(\\”c:\\\\ciphertext.txt\\”,\\”r\\”) failed”)); cbc_done(&cbcAES); goto EndOfCrypt; }
//创建解密后的文件
decryptFile = fopen(“c:\\\\decrypttext.txt”,”w”); if(decryptFile == 0){
printf(_T(“fopen(\\”c:\\\\decrypttext.txt\\”,\\”w\\”) failed”)); fclose(fileCipherText); cbc_done(&cbcAES); goto EndOfCrypt; }