#include <iostream>
#include <cassert>
#include <cmath>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
const char *key = "wandoer.com_wandoer.com_wandoer.";
char iv[16] = { '_','i','v','_','m','u','s','t','_','b','e','_','1','6','B','_' };
const char *plain_text = "The EVP interface supports the ability to perform authenticated encryption and decryption, as well as the option to attach unencrypted,"
" associated data to the message. Such Authenticated-Encryption with Associated-Data (AEAD) schemes provide confidentiality by encrypting the data, and also"
" provide authenticity assurances by creating a MAC tag over the encrypted data. The MAC tag will ensure the data is not accidentally altered or maliciously"
" tampered during transmission and storage.";
template<typename CIPHER>
bool encrypt(CIPHER cipher,const char* key, const char* iv, const unsigned char *in, int in_len, unsigned char *out, int *out_len) {
bool ret = false;
//创建加密上下文
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int tmp = 0;
//初始化加密上下文
int rst = EVP_EncryptInit(ctx, cipher, reinterpret_cast<const unsigned char *>(key),
reinterpret_cast<const unsigned char *>(iv));
if (rst != 1) goto RET;
//加密操作
rst = EVP_EncryptUpdate(ctx, out, &tmp, in, in_len);
if (rst != 1) goto RET;
*out_len = tmp;
//获取加密上下文中遗留的信息
rst = EVP_EncryptFinal(ctx, out + tmp, &tmp);
if (rst != 1) goto RET;
if (tmp > 0) {
*out_len += tmp;
}
ret = true;
RET:
//释放加密上下文
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
return ret;
}
template<typename CIPHER>
bool decrypt(CIPHER cipher, const char* key, const char* iv, const unsigned char *in, int in_len, unsigned char *out, int *out_len) {
bool ret = false;
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int tmp = 0;
int rst = EVP_DecryptInit(ctx, cipher, reinterpret_cast<const unsigned char *>(key),
reinterpret_cast<const unsigned char *>(iv));
if (rst != 1) goto RET;
rst = EVP_DecryptUpdate(ctx, out, &tmp, in, in_len);
if (rst != 1) goto RET;
*out_len = tmp;
rst = EVP_DecryptFinal(ctx, out + tmp, &tmp);
if (rst != 1) goto RET;
if (tmp > 0) {
*out_len += tmp;
}
ret = true;
RET:
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
return ret;
}
template<typename CIPHER>
bool do_crypto(CIPHER cipher, const char* key, const char* iv, const unsigned char* in, int in_len, unsigned char* out, int* out_len, bool encrypt) {
bool ret = false;
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
int tmp = 0;
int rst = EVP_CipherInit(ctx, cipher, reinterpret_cast<const unsigned char*>(key), reinterpret_cast<const unsigned char*>(iv), encrypt ? 1 : 0);
if (rst != 1) goto RET;
rst = EVP_CipherUpdate(ctx, out, &tmp, in, in_len);
if (rst != 1) goto RET;
*out_len = tmp;
rst = EVP_CipherFinal(ctx, out + tmp, &tmp);
if (rst != 1) goto RET;
if (tmp > 0) {
*out_len += tmp;
}
ret = true;
RET:
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
return ret;
}
int main(void) {
//TODO init OPENSSL
// EVP_CIPHER 是 OPENSSL 内置的一系列对称加密算法。可以使用 EVP_xxx_() 来获取,使用后无需释放。
// OPENSSL 具体提供哪些内置算法可以在 evp.h 里查看
const EVP_CIPHER *cipher = EVP_aes_256_cfb();
// 计算加解密结果 buffer 的大小。AES 对称加密后数据的大小与加密前相同。
// 考虑到可能有 padding 存在,大小为大于输入的,最小的 AES_BLOCK_SIZE 倍整数。
int out_len = ceil((float)strlen(plain_text) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
unsigned char *cipher_text = (unsigned char *)malloc(out_len);
unsigned char *re_plain_text = (unsigned char *)malloc(out_len);
//实际加解密输出的 buffer 大小
int enc_len = 0, dec_len = 0;
/** Method 1
* 使用 EVP_Encrypt / EVP_Decrypt 系列函数
*/
memset(cipher_text, 0, out_len);
memset(re_plain_text, 0, out_len);
bool encryped = encrypt(cipher,key,iv, reinterpret_cast<const unsigned char *>(plain_text),
strlen(plain_text), cipher_text, &enc_len);
assert(encryped);
bool decrypted = decrypt(cipher,key, iv, cipher_text, enc_len, re_plain_text, &dec_len);
assert(decrypted);
if (dec_len < out_len)
re_plain_text[dec_len] = '\0';
int cmp = strcmp(plain_text, reinterpret_cast<char *>(re_plain_text));
assert(cmp == 0);
/** Method 2
* 使用 EVP_Cipher 系列函数
*/
memset(cipher_text, 0, out_len);
memset(re_plain_text, 0, out_len);
encryped = do_crypto(cipher, key, iv, reinterpret_cast<const unsigned char*>(plain_text), strlen(plain_text), cipher_text, &enc_len, true);
assert(encryped);
decrypted = do_crypto(cipher, key, iv, cipher_text, enc_len, re_plain_text, &dec_len, false);
assert(decrypted);
cmp = strcmp(plain_text, reinterpret_cast<char*>(re_plain_text));
assert(cmp == 0);
free(cipher_text);
free(re_plain_text);
//TODO free OPENSSL
return 0;
}