1. 程式人生 > >openssl之aes加密(原始碼分析 AES_encrypt 與 AES_cbc_encrypt ,加密模式)

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

首先要了解AES加密是什麼,以及幾種加密模式的區別。之後才是程式設計。具體的程式設計案例,在下面的連結。

下面這個連結有詳細圖解。
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"voidAES_ecb_encrypt(constunsignedchar*in,unsignedchar*out,const AES_KEY *key,constint enc){assert(in&&out&& key);assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));if(AES_ENCRYPT == enc)AES_encrypt(in,out, key);elseAES_decrypt(in,out, key);}從這裡可以看出,ecb方式的加密,是由AES_encrypt介面實現的。 而cbc的加密方式在另外的地方實現了,下面給出目錄以及原始碼。
"crypto/aes/aes_cbc.c"

void AES_cbc_encrypt(constunsignedchar*in,unsignedchar*out, size_t len,const AES_KEY *key, unsignedchar*ivec,constint 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作為引數傳入

"crypto/modes/cbc128.c"voidCRYPTO_cbc128_encrypt(constunsignedchar*in,unsignedchar*out,size_t len,constvoid*key,unsignedchar ivec[16], block128_f block){//這裡的block就是AES_encryptsize_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;}}#endifwhile (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介面進行程式設計。 參考資料: http://fossies.org/dox/openssl-1.0.1f/index.html (詳細原始碼)