一、AES加密算法
1.1 AES算法結構
AES(Advanced Encryption Standard)算法是一種常見的對稱加密算法,其具體的加密傳輸流程如圖1所示:
圖1 AES數據加密流程
對於對稱加密算法而言發送端和接收端使用相同的密鑰K,而加密函數E(P,K)和解密函數D(C,K)是一組逆運算。
對稱加密算法的優勢是算法公開、計算量小、加密速度快、加密效率高。其安全性主要由密鑰保證。對稱加密的缺點主要體現在多用戶通信場景中,密鑰分發和管理比較困難。
AES為分組密碼,分組密碼也就是把明文分成長度相同的組,每次加密一組數據,直到加密完整個明文。在AES標准規范中,分組長度為128位,即每個分組為16個字節,推薦的加密輪數為10輪。即在加密公式C = E(P,K)中,會將一個輪函數執行10次。在這10次執行中,前9次的操作是完全一致的,只有第10次有所不同。AES的核心就是實現一輪中的所有操作。
AES的整體結構如下圖所示,加密的輪函數包括4個操作:字節代換、行位移、列混合和輪密鑰加。最后一輪迭代不執行列混合。另外,在第一輪迭代之前,先將明文和原始密鑰進行一次異或加密操作。
圖2 加解密函數的構成
1.2 算法的性能與應用
AES是美國國家標准與技術研究院(National Institute of Standards and Technology,NIST)於2001年發布的高級加密標准。旨在取代DES成為新一代的分組加密標准,現已廣泛應用於各個領域。目前現有方法無法對AES算法的解密機制進行破壞,只能針對密鑰的安全性進行攻擊,也就是對密鑰進行窮舉攻擊。
以本模塊中使用的AES-128算法為例,平均需要嘗試2127≈1.7×1038個128bit的隨機數作為密鑰進行加解密運算,方能找到正確的密鑰。以比特幣運算網絡為例,比特幣網絡在全球范圍內調用了非常龐大的硬件資源以達到極高的運算效率,每秒鍾操作的Hash運算(SHA-256)可高達2.5644×1019次。則使用全球范圍的運算資源需要6.6345×1018秒,即2104億年方能破解,大約是宇宙大爆炸時間的15倍。同樣以比特幣運算網絡為例,破解密鑰消耗的電量約為1.1201×1022kWh,費用則是達到了1.368×1013億美元,約是全球GDP總和的1400萬倍。
AES算法在信息安全相關領域中已得到廣泛應用。WLAN的IEEE803.11協議使用AES加密算法保障無線網絡的通信信道安全性;https的協議棧中也包含使用AES算法加密的SSL安全套接層協議;支付寶開放平台和微信小程序也將AES算法作為通用的加密算法。
二、openssl庫
SSL是Secure Sockets Layer(安全套接層協議)的縮寫,可以在Internet上提供秘密性傳輸。Netscape公司在推出第一個Web瀏覽器的同時,提出了SSL協議標准。其目標是保證兩個應用間通信的保密性和可靠性,可在服務器端和用戶端同時實現支持。已經成為Internet上保密通訊的工業標准。
openssl是一個安全套接字層密碼庫,囊括主要的密碼算法、常用密鑰、證書封裝管理功能及實現ssl協議。OpenSSL整個軟件包大概可以分成三個主要的功能部分:SSL協議庫libssl、應用程序命令工具以及密碼算法庫libcrypto。
三、AES加解密API
在openssl/aes.h中定義了多組加解密相關的API,其中常用的有以下幾個:
Int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)
功能:用於生成加密密鑰。
參數:
const unsigned char *userKey:密鑰字符串
const int bits:密鑰長度,以bit為單位,如果密鑰數字是16個字節,則此參數值應為128
AES_KEY *key:AES_KEY對象指針,用於接收生成的加密密鑰
返回值:
0:成功; -1:userkey,key為空;-2:密鑰長度不是128,192,256
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key)
功能:用於生成解密密鑰。
參數:
const unsigned char *userKey:密鑰字符串
const int bits:密鑰長度,以bit為單位,如果密鑰數字是16個字節,則此參數值應為128
AES_KEY *key:AES_KEY對象指針,用於接收生成的解密密鑰
返回值:
0:成功; -1:userkey,key為空;-2:密鑰長度不是128,192,256
void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
功能:加密數據塊。
參數:
const unsigned char *in:明文數據
unsigned char *out:密文數據(可以與in指向同一塊內存區域,則密文會覆蓋明文)
const AES_KEY *key:AES_KEY對象指針,加密密鑰
void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
功能:解密數據塊。
參數:
const unsigned char *in:密文數據
unsigned char *out:明文數據(可以與in指向同一塊內存區域,則明文會覆蓋密文)
const AES_KEY *key:AES_KEY對象指針,解密密鑰
void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc)
功能:以ECB模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc)
功能:以CBC模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
size_t length:數據塊長度(單位為字節)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
unsigned char *ivec:初始向量
const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)
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)
功能:以CFB128位模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
size_t length:數據塊長度(單位為字節)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
unsigned char *ivec:初始向量
int *num:輸出參數,計數加密的CFB數據塊個數
const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)
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)
功能:以CFB1位模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
size_t length:數據塊長度(單位為字節)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
unsigned char *ivec:初始向量
int *num:輸出參數,計數加密的CFB數據塊個數
const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)
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)
功能:以CFB8位模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
size_t length:數據塊長度(單位為字節)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
unsigned char *ivec:初始向量
int *num:輸出參數,計數加密的CFB數據塊個數
const int enc:加解密模式(AES_ENCRYPT 代表加密, AES_DECRYPT代表解密)
void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, int *num)
功能:以OFB128位模式加密/解密數據塊。
參數:
const unsigned char *in:輸入數據(加密時為明文,解密時為密文)
unsigned char *out:輸出數據(加密時為密文,解密時為明文)
size_t length:數據塊長度(單位為字節)
const AES_KEY *key:AES_KEY對象指針,加密/解密密鑰
unsigned char *ivec:初始向量
int *num:輸出參數,計數加密的OFB數據塊個數
(注:ofb模式的API不需要加解密模式參數,加解密使用相同的函數)
四、代碼實現(以CBC模式為例)
以下代碼在linux環境下編譯運行,其他環境可能部分頭文件有不同。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#define AESKEY "df98b715d5c6ed2b25817b6f255411a1" //HEX密鑰
#define AESIV "2841ae97419c2973296a0d4bdfe19a4f" //HEX初始向量
//將文本形式的HEX串進行轉換
unsigned char* str2hex(char *str)
{
unsigned char *ret = NULL;
int str_len = strlen(str);
int i = 0;
assert((str_len%2) == 0);
ret = (char *)malloc(str_len/2);
for (i =0;i < str_len; i = i+2 )
{
sscanf(str+i,"%2hhx",&ret[i/2]);
}
return ret;
}
int main()
{
AES_KEY encryptkey;
AES_KEY decryptkey;
unsigned char *key;
unsigned char *stdiv;
key = str2hex(AESKEY);
stdiv = str2hex(AESIV);
AES_set_encrypt_key(key,128,&encryptkey);
AES_set_decrypt_key(key,128,&decryptkey);
unsigned char plain_text [32];
memcpy(plain_text, "AES encrypt in openssl demo", 27);
memset(plain_text + 27, 0, 5);
//需要將加密區塊長度填充為16字節整數倍,此處使用zero-padding,即末尾全用0填充
printf(“plain_text: ” );
for(int i = 0; i < 32; i++)
{
printf(“%02X ”, plain_text[i]);
}
printf(“\n” );
unsigned char encrypted_text [32];
memset(encrypted_text, 0, 32);
unsigned char tmpiv[16];
memcpy(tmpiv, stdiv, 16);
AES_cbc_encrypt(plain_text, encrypted_text, 32, &encryptkey, tmpiv, AES_ENCRYPT);
//初始向量這個參數每次使用都會將其改變,有興趣的話可以把調用前后這個地址下的內容打印出來。所以如果要多次加密且每次使用固定的初始向量,可以先用tmpiv接收
printf(“encrypted_text: ” );
for(int i = 0; i < 32; i++)
{
printf(“%02X ”, encrypted_text[i]);
}
printf(“\n” );
unsigned char decrypted_text [32];
memset(decrypted_text, 0, 32);
memcpy(tmpiv, stdiv, 16);
AES_cbc_encrypt(encrypted_text, decrypted_text, 32, &decryptkey, tmpiv, AES_DECRYPT);
printf(“decrypted_text: ” );
for(int i = 0; i < 32; i++)
{
printf(“%02X ”, decrypted_text[i]);
}
printf(“\n” );
return 0;
}
編譯運行之后,輸出情況如下圖所示:
圖3 加密demo程序的運行效果