Openssl-AES加密

AES加密算法

此次介绍AES两种加密算法,其他的暂不使用

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

AES Padding

None // 不填充。
PKCS7 // 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
Zeros // 填充字符串由设置为零的字节组成。
此处采用PKCS7进行填充
AES块大小为16,即以16字节为单位进行加密或解密,不足16字节是需要进行填充
假如当前有9个字节为[31 32 33 34 35 36 37 38 39],缺少7个字节,那么就需要填充7个字节的07
填充后就是[31 32 33 34 35 36 37 38 39 07 07 07 07 07 07 07]
需注意当为15个字节时,填充为01,但存在可能16个字节,最后一个字节加密为01(未证明过,可以查看openssl源码)
代码如下

std::shared_ptr<ByteBuffer> Aes::PKCS7Padding(const uint8_t *in, uint32_t inLen)
{
    std::shared_ptr<ByteBuffer> ptr(new ByteBuffer(in, inLen));
    uint8_t remainderSize = inLen % AES_BLOCK_SIZE;
    if (remainderSize == 0) {
        return ptr;
    }

    uint8_t buf[AES_BLOCK_SIZE] = {0};
    uint8_t paddingSize = AES_BLOCK_SIZE - remainderSize;

    memset(buf, paddingSize, paddingSize);	// memset是以一个字节为单位格式化
    if (ptr != nullptr) {
        ptr->append(buf, paddingSize);
    }

    return ptr;
}

使用Openssl AES进行加密和解密

// aes.h
#ifndef __CRYPTO_AES_H__
#define __CRYPTO_AES_H__

#include "crypto.h"
#include 
#include 
#include 

#define MAX_USER_KEY_SIZE 32
#define CBC_VECTOR_SIZE 16

namespace eular {
class Aes : public CryptoBase
{
public:
    enum KeyType {
        AES128 = 16,
        AES256 = 32,
    };

    enum EncodeType {
        AESECB = 0,
        AESCBC = 1,
    };

    Aes(const uint8_t *userKey, Aes::KeyType userKeytype, Aes::EncodeType encodeType);
    virtual ~Aes();

    bool reinit(const uint8_t *userKey, Aes::KeyType userKeytype, Aes::EncodeType encodeType);

    virtual int encode(uint8_t *out, const uint8_t *src, const uint32_t &srcLen);
    virtual int decode(uint8_t *out, const uint8_t *src, const uint32_t &srcLen);

protected:
    std::shared_ptr<ByteBuffer> PKCS7Padding(const uint8_t *in, uint32_t inLen);

protected:
    AES_KEY mAesKey;
    uint8_t mUserKey[MAX_USER_KEY_SIZE];
    uint8_t mUserKeyType;
    uint8_t mEncodeType;
    uint8_t vecForCBC[CBC_VECTOR_SIZE];
};

} // namespace eular

#endif // __CRYPTO_AES_H__
// aes.cpp
#include "aes.h"
#include 
#include 
#include 
#include 

#define LOG_TAG "AES"

namespace eular {

Aes::Aes(const uint8_t *userKey, Aes::KeyType userKeytype, Aes::EncodeType encodeType)
{
    memset(mUserKey, 0, MAX_USER_KEY_SIZE);
    memset(vecForCBC, 0, CBC_VECTOR_SIZE);
    reinit(userKey, userKeytype, encodeType);
}

Aes::~Aes()
{

}

bool Aes::reinit(const uint8_t *userKey, Aes::KeyType userKeytype, Aes::EncodeType encodeType)
{
    switch (userKeytype) {
    case Aes::KeyType::AES128:
    case Aes::KeyType::AES256:
        break;
    default:
        throw(Exception(String8::format("Invalid AES Type: %d", userKeytype)));
        break;
    }
    memcpy(mUserKey, userKey, userKeytype);
    mUserKeyType = userKeytype;
    mEncodeType = encodeType;
}

int Aes::encode(uint8_t *out, const uint8_t *src, const uint32_t &srcLen)
{
    LOG_ASSERT(out || src || srcLen, "");
    std::shared_ptr<ByteBuffer> ptr = PKCS7Padding(src, srcLen);
    if (ptr == nullptr) {
        return NO_MEMORY;
    }

    AES_set_encrypt_key(mUserKey, mUserKeyType * 8, &mAesKey);
    if (mEncodeType == AESECB) {
        AES_ecb_encrypt(ptr->const_data(), out, &mAesKey, AES_ENCRYPT);
    } else if (mEncodeType == AESCBC) {
        memset(vecForCBC, 0, AES_BLOCK_SIZE);   // 加密和解密时初始vecForCBC内容须一致,一般设置为全0
        AES_cbc_encrypt(ptr->const_data(), out, ptr->size(), &mAesKey, vecForCBC, AES_ENCRYPT);
    }

    return ptr->size();
}

int Aes::decode(uint8_t *out, const uint8_t *src, const uint32_t &srcLen)
{
    LOG_ASSERT(out || src || srcLen, "");

    AES_set_decrypt_key(mUserKey, mUserKeyType * 8, &mAesKey);
    if (mEncodeType == AESECB) {
        AES_ecb_encrypt(src, out, &mAesKey, AES_DECRYPT);
    } else if (mEncodeType == AESCBC) {
        memset(vecForCBC, 0, AES_BLOCK_SIZE);
        AES_cbc_encrypt(src, out, srcLen, &mAesKey, vecForCBC, AES_DECRYPT);
    }

    uint8_t paddingSize = 0;
    if (out[srcLen - 1] == 0x1) {    // 填充了一个
        paddingSize = 0x1;
    } else if (out[srcLen - 1] == out[srcLen - 2]) {
        paddingSize = out[srcLen - 1];
    }

    out[srcLen - paddingSize] = '\0';
    return srcLen - paddingSize;
}

std::shared_ptr<ByteBuffer> Aes::PKCS7Padding(const uint8_t *in, uint32_t inLen)
{
    std::shared_ptr<ByteBuffer> ptr(new ByteBuffer(in, inLen));
    uint8_t remainderSize = inLen % AES_BLOCK_SIZE;
    if (remainderSize == 0) {
        return ptr;
    }

    uint8_t buf[AES_BLOCK_SIZE] = {0};
    uint8_t paddingSize = AES_BLOCK_SIZE - remainderSize;

    memset(buf, paddingSize, paddingSize);
    if (ptr != nullptr) {
        ptr->append(buf, paddingSize);
    }

    return ptr;
}

} // namespace eular

// testaes.cc
#include "aes.h"
#include 
#include 

using namespace eular;
using namespace std;

int main(int argc, char **argv)
{
    uint8_t userKey[16] = {
        '1', '2', '3', '4', '5', '6',
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
    Aes aes(userKey, Aes::KeyType::AES128, Aes::EncodeType::AESCBC);
    uint8_t in[1024] = {0};
    uint8_t out[1024] = {0};
    uint8_t tmp[1024] = {0};

    printf("input one string:\n");
    scanf("%[^\n]", in);

    int encodeSize = aes.encode(out, in, strlen((char *)in));
    if (encodeSize < 0) {
        return -1;
    }
    printf("after encode: %d\n", encodeSize);
    for (int i = 0; i < encodeSize; ++i) {
        if (i != 0 && i % 16 == 0) {
            printf("\n");
        }
        printf("%02x ", out[i]);
    }
    printf("\n");

    int decodeSize = aes.decode(tmp, out, encodeSize);
    printf("after decrypt: %s\n", tmp);
    for (int i = 0; i < decodeSize; ++i) {
        if (i != 0 && i % 16 == 0) {
            printf("\n");
        }
        printf("%02x ", tmp[i]);
    }
    printf("\n");

    assert(memcmp(in, tmp, decodeSize) == 0);
    return 0;
}

参考:https://www.cnblogs.com/solohac/p/4284424.html

你可能感兴趣的:(c/c++,c++,linux)