openssl之aes加密(源碼分析 AES_encrypt 與 AES_cbc_encrypt ,加密模式)


首先要了解AES加密是什麼,以及幾種加密模式的區別。以後纔是編程。具體的編程案例,在下面的連接。html

openssl之aes加密(AES_cbc_encrypt 與 AES_encrypt 的編程案例)算法

下面這個連接有詳細圖解。
http://www.cnblogs.com/adylee/archive/2007/09/14/893438.html
 
     

AES加密算法 - 加密模式編程

ECB模式 
  優勢: 
  1.簡單; 
  2.有利於並行計算; 
  3.偏差不會被傳送; 
  缺點: 
  1.不能隱藏明文的模式; 
  2.可能對明文進行主動攻擊; 
CBC模式: 
  優勢: 
  1.不容易主動攻擊,安全性好於ECB,適合傳輸長度長的報文,是SSL、IPSec的標準。 
  缺點: 
  1.不利於並行計算; 
  2.偏差傳遞; 
  3. 須要初始化向量IV 
CFB模式: 
  優勢: 
  1.隱藏了明文模式; 
  2.分組密碼轉化爲流模式; 
  3.能夠及時加密傳送小於分組的數據; 
  缺點: 
  1.不利於並行計算; 
  2.偏差傳送:一個明文單元損壞影響多個單元; 
  3.惟一的IV; 
ofb模式: 
  優勢: 
  1.隱藏了明文模式; 
  2.分組密碼轉化爲流模式; 
  3.能夠及時加密傳送小於分組的數據; 
  缺點: 
  1.不利於並行計算; 
  2.對明文的主動攻擊是可能的; 
  3.偏差傳送:一個明文單元損壞影響多個單元; 


瞭解這些加密模式以後,再看openssl提供的接口就好理解了。
 
    

openssl提供的aes加密接口安全

如下接口來自「crypto/aes/aes.h」,有openssl源碼。
//設置加密和解密器
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key);

//默認的加密解密方式,參數好理解
void AES_encrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);
void AES_decrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);

//下面這些也是經常使用的加密方式,可是參數不少,而源碼對於參數使用介紹很少,只能摸索
void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key, const int enc);
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, const int enc); //參數相對複雜
void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc);
void AES_cfb1_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc);
void AES_cfb8_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc);
void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num);
void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char ivec[AES_BLOCK_SIZE],
unsigned char ecount_buf[AES_BLOCK_SIZE],
unsigned int *num);

從下面這個文件能夠看出, AES_encrypt 就是ecb加密的方式。而AES_set_encrypt_key和 AES_encrypt,它們的實如今"crypto/aes/aes_x86core.c"和 "crypto/aes/aes_core.c",也就是有兩個版本,根據平臺選擇。看源碼。
 
    

" crypto/aes/ aes_ecb.c"
void AES_ecb_encrypt ( const unsigned char * in , unsigned char * out ,
    const AES_KEY * key , const int enc ) {
        assert ( in && out && key );
assert (( AES_ENCRYPT == enc )||( AES_DECRYPT == enc ));
if ( AES_ENCRYPT == enc )
AES_encrypt ( in , out , key );
else
AES_decrypt ( in , out , key );
}
從這裏能夠看出,ecb方式的加密,是由AES_encrypt接口實現的。


而cbc的加密方式在另外的地方實現了,下面給出目錄以及源代碼。
"crypto/aes/aes_cbc.c"

void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t len, const AES_KEY *key, unsigned char *ivec, const int enc) { if (enc) CRYPTO_cbc128_encrypt(in,out,len,key,ivec,(block128_f)AES_encrypt); else CRYPTO_cbc128_decrypt(in,out,len,key,ivec,(block128_f)AES_decrypt); }函數

從這裏看出,cbc加密方式,調用接口CRYPTO_cbc128_decrypt,而它又將AES_encrypt做爲參數傳入ui

 
    

"crypto/modes/ cbc128.c"
void CRYPTO_cbc128_encrypt ( const unsigned char * in , unsigned char * out ,
size_t len , const void * key ,
unsigned char ivec [ 16 ], block128_f block )
{ //這裏的block就是AES_encrypt
size_t n;
const unsigned char *iv = ivec;
assert(in && out && key && ivec);
#if !defined(OPENSSL_SMALL_FOOTPRINT)
if (STRICT_ALIGNMENT &&
   ((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0) {
while (len>=16) {
for(n=0; n<16; ++n)
out[n] = in[n] ^ iv[n]; //輸入與初始化向量進行異或,保存在out
(*block)(out, out, key); //調用AES_encrypt進行加密,異或結果out做爲加密輸入
//加密輸出結果也保存在out裏面,
iv = out; //將前一次密文,做爲後一次的初始化向量,從而完成加密
len -= 16;
in  += 16;
out += 16;
}
} else {
while (len>=16) {
for(n=0; n<16; n+=sizeof(size_t))
*(size_t*)(out+n) =
*(size_t*)(in+n) ^ *(size_t*)(iv+n);
(*block)(out, out, key);
iv = out;
len -= 16;
in  += 16;
out += 16;
}
}
#endif
while (len) {
for(n=0; n<16 && n<len; ++n)
out[n] = in[n] ^ iv[n]; //in和iv異或
for(; n<16; ++n) //若是in長度不是16的整數倍
out[n] = iv[n]; //最後的out直接用iv初始化,其實也就至關於out與0進行異或
(*block)(out, out, key);
iv = out;
if (len<=16) break; //加密結束
len -= 16;
in  += 16;
out += 16;
}
memcpy(ivec,iv,16);
}
//從上面的源碼能夠看出,cbc本質上和ecb差異不大,惟一區別是將前一次加密結果,與要加密的內容異或。所以,cbc的並行性較差,由於每次都要等待前一次的結果,而ecb則不用,速度較快。其主要區別仍然看文章開頭,原理圖看參考連接。


調用實例:
int aes_encrypt(char* in, char* key, char* out)//, int olen)
{
    if(!in || !key || !out) return 0;
    AES_KEY aes;
    if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    int len=strlen(in)/<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE*</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">, en_len=0;</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">
</span>    while(en_len<len)//輸入輸出字符串夠長,而且是AES_BLOCK_SIZE的整數倍,須要嚴格限制
    {
    	AES_encrypt((unsigned char*)in, (unsigned char*)out, &aes);
    	in+=AES_BLOCK_SIZE;
    	out+=AES_BLOCK_SIZE;
    	en_len+=AES_BLOCK_SIZE;
    }
    return 1;
}
int aes_decrypt(char* in, char* key, char* out)
{
    if(!in || !key || !out) return 0;
    AES_KEY aes;
    if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    int len=strlen(in), en_len=0;
    while(en_len<len)
    {
    	AES_decrypt((unsigned char*)in, (unsigned char*)out, &aes);
    	in+=AES_BLOCK_SIZE;
    	out+=AES_BLOCK_SIZE;
    	en_len+=AES_BLOCK_SIZE;
    }
    return 1;
}
最近遇到一個坑,
AES_encrypt <pre code_snippet_id="232583" snippet_file_name="blog_20140312_1_226454" name="code" class="cpp" style="color: rgb(55, 42, 24); font-size: 16px; line-height: 28px;">AES_decrypt
 
    就是這兩個函數讓我折騰了四五天,心力憔悴,幾近崩潰,也許是走火入魔了,由於遇到了挑戰,非要幹掉它,而不是繞過! 
   
int aes_encrypt(char* in, char* key, char* out)//, int olen)
{
    if(!in || !key || !out) return 0;
    AES_KEY aes;
    if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    <pre code_snippet_id="232583" snippet_file_name="blog_20140312_1_226454" name="code" class="cpp" style="color: rgb(55, 42, 24); font-size: 16px; line-height: 28px;">    int len=strlen(in)/<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE*</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">, en_len=0;</span>
 
    
char *lptmp = new char[len+1];
memset(lptmp, '\0', len+1);
    while(en_len<len)//輸入輸出字符串夠長,而且是AES_BLOCK_SIZE的整數倍,須要嚴格限制
    {
    	AES_encrypt((unsigned char*)in, (unsigned char*)lptmp, &aes);
    	in+=AES_BLOCK_SIZE;
    	<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">lptmp</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">+=AES_BLOCK_SIZE;</span>
    	en_len+=AES_BLOCK_SIZE;
<span style="color:#372a18;">        //hex(lptmp)  </span><strong><span style="color:#ff0000;">那麼就發現第一個數據塊加密的結果是對的,後面就錯誤了,加密後的數據不少是0,查了openssl的</span></strong>
<strong><span style="color:#ff0000;">        //源代碼這個aes這塊的加解密是線程安全的,有誰知道緣由?</span></strong>
}
if(lptmp)
{
delete []lptmp;
lptmp = NULL;
}
    return 1;
}


最後給出一個連接,利用openssl的AES接口進行編程。
參考資料:

分組對稱加密模式:ECB/CBC/CFB/OFB缺CTR

http://fossies.org/dox/openssl-1.0.1f/index.html  (詳細源碼)
相關文章
相關標籤/搜索