openssl AES 加密/解密

以 aes-cbc-128, PKCS7 填充方式为例。
以下为使用两种不同api的实现相同加解密的代码:

int AesDecrypt(const std::string& sessionkey, const std::string& encrypted_data, const std::string& iv, std::string &plaintext)
{
    // set decrypt key
    AES_KEY aes_key;
    //设置秘钥位数,只能为 128,192,256; 若key_bits为256,也可以设置key_bits为128,此时秘钥只使用了前 (128/8 = 16)位
    const int key_bits = 8 * sessionkey.size();
    ret = AES_set_decrypt_key((const unsigned char*)sessionkey.c_str(), key_bits, &aes_key);
    if (ret != 0)
    {
        ERROR("AES_set_decrypt_key failed, ret: %d, key: %s, key_bits: %d", ret, sessionkey.c_str(), key_bits);
        return -2;
    }
    // decrypt
    plaintext.resize(encrypted_data.size() + AES_BLOCK_SIZE);// assign decrypt block
    AES_cbc_encrypt((const unsigned char*)encrypted_data.c_str(), (unsigned char*)plaintext.c_str(), encrypted_data.size(), &aes_key, (unsigned char*)iv.c_str(), AES_DECRYPT);// 加密:AES_ENCRYPT,解密:AES_DECRYPT

    return 0;
}
/***
* @brief aes-128-cbc加解密 pkcs#7填充; 
* do_encrypt = 1 加密; do_encrypt = 0 解密
*/
int AesDecrypt(const string &iv, const string &key, const string& cipherText, string& plainText, int do_encrypt)
{
    /* Allow enough space in output buffer for additional block */
     unsigned char outbuf[1024 + EVP_MAX_BLOCK_LENGTH] = {0};
     int outlen;
     EVP_CIPHER_CTX *ctx;

     /* Don't set key or IV right away; we want to check lengths */
     ctx = EVP_CIPHER_CTX_new();
     EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,
                       do_encrypt);
     OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
     OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);

     /* Now we can set key and IV */
    EVP_CipherInit_ex(ctx, NULL, NULL, reinterpret_cast<unsigned const char*>(key.c_str()), reinterpret_cast<unsigned const char*>(iv.c_str()), do_encrypt);



    EVP_CipherUpdate(ctx, outbuf, &outlen, reinterpret_cast<unsigned const char*>(cipherText.c_str()), cipherText.length());
     
     // Update cipher_text with the final remaining bytes.
    int lastBlockLen = 0;
    EVP_CipherFinal_ex(ctx, reinterpret_cast<unsigned char*>(&outbuf[outlen]), &lastBlockLen);
    
    if ((outlen + lastBlockLen) == 0)
    {
        EVP_CIPHER_CTX_free(ctx);
		cout << "EVP_CipherUpdate and EVP_CipherFinal_ex failed, cipherText(" + cipherText + ")." << endl;
        return -1;
    }

    plainText.assign(reinterpret_cast<char*>(outbuf));
    EVP_CIPHER_CTX_free(ctx);
    return 0;
}

第一个代码段是专门针对 AES-CBC的,AES_set_decrypt_key和AES_cbc_encrypt是对通用加解密函数的封装而已,相应的更多封装api可以参考 OpenSSL中AES加密的用法

第二个代码段是更为通用的加解密函数,通过 EVP_CipherInit_ex 进行相应的特殊配置

AES介绍

aes加解密算法是一种将明文块分为一个个 128bit 块进行加密的算法, 所以对于无法恰好整分128bit的明文数据,就需要填充,填充方式的不同也是造成各种加密数据无法成功解密的原因之一。

aes的各种不同区分:
1、秘钥长度
aes算法分为 128,192,256 三种秘钥长度的加密方式 ,也就是常见的 aes-128 , aes-192 , aes-256。

2、加密模式
ECB模式
按照块密码的块大小被分为数个块,并对每个块进行独立加密。
  优点:
  1.简单;
  2.有利于并行计算;
  3.误差不会被传送;
  缺点:
  1.不能隐藏明文的模式;
  2.可能对明文进行主动攻击;
  
CBC模式:
每个平文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有平文块。
同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。
  优点:
  1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
  缺点:
  1.不利于并行计算;
  2.误差传递;
  3.需要初始化向量IV

CFB模式:
模式类似于CBC,可以将块密码变为自同步的流密码。
  优点:
  1.隐藏了明文模式;
  2.分组密码转化为流模式;
  3.可以及时加密传送小于分组的数据;
  缺点:
  1.不利于并行计算;
  2.误差传送:一个明文单元损坏影响多个单元;
  3.唯一的IV;

OFB模式:
可以将块密码变成同步的流密码。它产生密钥流的块,然后将其与平文块进行异或,得到密文。
  优点:
  1.隐藏了明文模式;
  2.分组密码转化为流模式;
  3.可以及时加密传送小于分组的数据;
  缺点:
  1.不利于并行计算;
  2.对明文的主动攻击是可能的;
  3.误差传送:一个明文单元损坏影响多个单元;

PCBC模式

CTR模式

关于cbc模式的一张图:
openssl AES 加密/解密_第1张图片
3、填充方式
None //不填充。
PKCS7 //填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
Zeros //填充字符串由设置为零的字节组成。
ANSIX923 //ANSIX923 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节均填充数字零。
ISO10126 //ISO10126 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节填充随机数据。

在调用EVP_EncryptFinal_ex函数时,padding是默认启用的。
是否使用填充,可以通过EVP_CIPHER_CTX_set_padding函数设置,第二个参数为0则禁用padding,为1则启用padding。
默认的填充方式为: PKCS#7。
PKCS#7填充时将明文长度扩充为16的整数倍,每一个填充的字节值为填充的长度。

假设块长度为128bit,也就是16个字节。
那么当需要padding的字符串长度为11时(假设字符源串16进制为 : DD DD DD DD DD DD DD DD | DD DD DD DD ),则padding结果为:

| DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

参考:
1、 solohac —— openssl AES加密以及padding
2、 游蓝海 —— OpenSSL中AES加密的用法

你可能感兴趣的:(随便记点)