openssl之EVP系列之6---EVP_Encrypt系列函數編程架構及樣例
---依據openssl doc/crypto/EVP_EncryptInit.pod和doc/ssleay.txt cipher.doc部分翻譯和自己的理解寫成
作者:DragonKing, Mail: wzhah@263.net ,
版本號:openssl-0.9.7
在前面的兩篇文章,已經對EVP_Encrypt*...*系列函數做了具體的介紹,本章將說明該系列函數通用的應用架構,並舉幾個函數應用樣例。
【應用架構】
一般來說。EVP_Encrypt*...*系列函數的應用架構例如以下所描寫敘述(如果加密算法為3DES):
1.定義一些必須的變量
char key[EVP_MAX_KEY_LENGTH];
char iv[EVP_MAX_IV_LENGTH];
EVP_CIPHER_CTX ctx;
unsigned char out[512+8];
int outl;
2.給變量key和iv賦值,這里使用了函數EVP_BytesToKey。該函數從輸入password產生了密鑰key和初始化向量iv,該函數將在后面做介紹。假設能夠有別的辦法設定key和iv。該函數的調用不是必須的
EVP_BytesToKey(EVP_des_ede3_cbc,EVP_md5,NULL,passwd,strlen(passwd),key,iv);
3.初始加密算法結構EVP_CIPHER_CTX
EVP_EncryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, key, iv);
4.進行數據的加密操作
while (....)
{
EVP_EncryptUpdate(ctx,out,&outl,in,512);
}
一般來說採用了循環的結構進行處理,每次循環加密數據為512字節,密文輸出到out,out和int應該是指向不同樣的內存的。
5.結束加密,輸出最后的一段512字節的數據
EVP_EncryptFinal_ex(&ctx, out, &outl)
該函數會進行加密的檢測,假設加密過程有誤,通常會檢查出來。
說明:加密跟上述過程是一樣的,僅僅只是要使用EVP_Decrypt*...*系列函數。
【樣例】
1.取得算法RC5使用的循環次數(round)
int nrounds;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GET_RC5_ROUNDS, 0, &nrounds);
2.取得算法RC2的有效密鑰長度
int key_bits;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GET_RC2_KEY_BITS, 0, &key_bits);
3.設置算法RC5使用的循環次數(round)
int nrounds;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC5_ROUNDS, nrounds, NULL);
4.設置算法RC2的有效密鑰長度
int key_bits;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, key_bits, NULL);
5.使用Blowfish算法加密一個字符串
int do_crypt(char *outfile)
{
unsigned char outbuf[1024];
int outlen, tmplen;
//事實上key和iv一般應該從別的地方得到,這里固定了至少作為演示使用的
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
unsigned char iv[] = {1,2,3,4,5,6,7,8};
char intext[] = "Some Crypto Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
{
/* 出錯處理*/
return 0;
}
//注意,傳入給以下函數的輸出緩存參數必須注意不能覆蓋了原來的加密輸出的數據
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* 出錯處理*/
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
//注意,保存到文件的時候要使用二進制模式打開文件,由於密文數據是二進制的,並且。不能採用strlen函數,由於密文字符串不是以NULL(0)為結束的字符串
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
上面舉的樣例加密的密文能夠使用openssl提供的應用程序cipher.exe來解密,命令例如以下:
openssl bf -in cipher.bin -K 000102030405060708090A0B0C0D0E0F -iv 0102030405060708 -d
6.使用文件I/O和80位密鑰RC2算法的通用加密解密函數實例。該函數能夠進行加密,也能夠進行解密,由參數do_encrypt決定。該參數為1時則運行加密,為0時則運行解密。
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/*為輸出緩沖設置足夠的附加空間*/
inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
//事實上key和iv一般應該從別的地方得到。這里固定了至少作為演示使用的
unsigned char key[] = "0123456789";
unsigned char iv[] = "12345678";
//這時候不進行key和IV的設置,由於我們還要改動參數
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
EVP_CIPHER_CTX_set_key_length(&ctx, 10);
//完畢參數設置。進行key和IV的設置
EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
for(;;)
{
inlen = fread(inbuf, 1, 1024, in);
if(inlen <= 0) break;
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/*出錯處理 */
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* 出錯處理*/
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}