openssl為用戶提供了豐富的指令,同時也提供了供編程調用的API,本文以使用128位aes算法的ecb模式進行加密和解密驗證,如下所示
第一種方法,直接使用aes算法提供的api進行調用,代碼如下
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <openssl/aes.h> int main(void) { char userkey[AES_BLOCK_SIZE]; unsigned char *date = malloc(AES_BLOCK_SIZE*3); unsigned char *encrypt = malloc(AES_BLOCK_SIZE*3 + 4); unsigned char *plain = malloc(AES_BLOCK_SIZE*3); AES_KEY key; memset((void*)userkey, 'k', AES_BLOCK_SIZE); memset((void*)date, 'p', AES_BLOCK_SIZE*3); memset((void*)encrypt, 0, AES_BLOCK_SIZE*6); memset((void*)plain, 0, AES_BLOCK_SIZE*3); /*設置加密key及密鑰長度*/
AES_set_encrypt_key(userkey, AES_BLOCK_SIZE*8, &key); int len = 0; /*循環加密,每次只能加密AES_BLOCK_SIZE長度的數據*/
while(len < AES_BLOCK_SIZE*3) { AES_encrypt(date+len, encrypt+len, &key); len += AES_BLOCK_SIZE; }
/*設置解密key及密鑰長度*/ AES_set_decrypt_key(userkey, AES_BLOCK_SIZE*8, &key); len = 0; /*循環解密*/
while(len < AES_BLOCK_SIZE*3) { AES_decrypt(encrypt+len, plain+len, &key); len += AES_BLOCK_SIZE; }
/*解密后與原數據是否一致*/ if(!memcmp(plain, date, AES_BLOCK_SIZE*3)){ printf("test success\n"); }else{ printf("test failed\n"); } printf("encrypt: "); int i = 0; for(i = 0; i < AES_BLOCK_SIZE*3 + 4; i++){ printf("%.2x ", encrypt[i]); if((i+1) % 32 == 0){ printf("\n"); } } printf("\n"); return 0; }
編譯執行結果如下
xlzh@cmos:~/cmos/openssl-code/aes$ gcc aes.c -o aes.out -lssl -lcrypto xlzh@cmos:~/cmos/openssl-code/aes$ ./aes.out test success encrypt: 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 00 00 00 00 xlzh@cmos:~/cmos/openssl-code/aes$
第二種方法,使用EVP框架,示例如下
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <openssl/evp.h> #include <openssl/aes.h> int main(void) { char userkey[EVP_MAX_KEY_LENGTH]; char iv[EVP_MAX_IV_LENGTH]; unsigned char *date = malloc(AES_BLOCK_SIZE*3); unsigned char *encrypt = malloc(AES_BLOCK_SIZE*6); unsigned char *plain = malloc(AES_BLOCK_SIZE*6); EVP_CIPHER_CTX ctx; int ret; int tlen = 0; int mlen = 0; int flen = 0; memset((void*)userkey, 'k', EVP_MAX_KEY_LENGTH); memset((void*)iv, 'i', EVP_MAX_IV_LENGTH); memset((void*)date, 'p', AES_BLOCK_SIZE*3); memset((void*)encrypt, 0, AES_BLOCK_SIZE*6); memset((void*)plain, 0, AES_BLOCK_SIZE*6); /*初始化ctx*/
EVP_CIPHER_CTX_init(&ctx); /*指定加密算法及key和iv(此處IV沒有用)*/
ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, userkey, iv); if(ret != 1) { printf("EVP_EncryptInit_ex failed\n"); exit(-1); } /*禁用padding功能*/ EVP_CIPHER_CTX_set_padding(&ctx, 0); /*進行加密操作*/
ret = EVP_EncryptUpdate(&ctx, encrypt, &mlen, date, AES_BLOCK_SIZE*3); if(ret != 1) { printf("EVP_EncryptUpdate failed\n"); exit(-1); } /*結束加密操作*/ ret = EVP_EncryptFinal_ex(&ctx, encrypt+mlen, &flen); if(ret != 1) { printf("EVP_EncryptFinal_ex failed\n"); exit(-1); } tlen = mlen + flen; tlen = 0; mlen = 0; flen = 0; EVP_CIPHER_CTX_cleanup(&ctx); EVP_CIPHER_CTX_init(&ctx);
ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, userkey, iv); if(ret != 1) { printf("EVP_DecryptInit_ex failed\n"); exit(-1); } EVP_CIPHER_CTX_set_padding(&ctx, 0); ret = EVP_DecryptUpdate(&ctx, plain, &mlen, encrypt, AES_BLOCK_SIZE*3); if(ret != 1) { printf("EVP_DecryptUpdate failed\n"); exit(-1); } ret = EVP_DecryptFinal_ex(&ctx, plain+mlen, &flen); if(ret != 1) { printf("EVP_DecryptFinal_ex failed\n"); exit(-1); }
/*對比解密后與原數據是否一致*/ if(!memcmp(plain, date, AES_BLOCK_SIZE*3)) { printf("test success\n"); } else { printf("test failed\n"); } printf("encrypt: "); int i; for(i = 0; i < AES_BLOCK_SIZE*3+4; i ++){ printf("%.2x ", encrypt[i]); if((i+1)%32 == 0){ printf("\n"); } } printf("\n"); return 0; }
編譯執行結果如下:
xlzh@cmos:~/cmos/openssl-code/aes$ gcc evp.c -o evp.out -lssl -lcrypto xlzh@cmos:~/cmos/openssl-code/aes$ ./evp.out test success encrypt: 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 08 a9 74 4d b0 66 57 1b 57 fe 60 3d 91 e4 ed 53 00 00 00 00 xlzh@cmos:~/cmos/openssl-code/aes$
EVP框架是對openssl提供的所有算法進行了封裝,在使用工程中只需要修改少量的代碼就可以選擇不同的加密算法,在工作中通常采用這種方式。
在上述兩個示例中,直接使用API提供的接口,沒有使用padding,在EVP中同樣需要聲明不可以使用padding方式,否則即使要加密的數據長度是AES_BLOCK_SIZE的整數倍,EVP默認也會對原始數據進行追加,導致結果不同,所以在試驗中通過EVP_CIPHER_CTX_set_padding(&ctx, 0)函數關閉的EVP的padding功能,同樣在解密的時候也需要進行關閉。