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;
}
// 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