首先,我这里使用OpenSSL库来实现的加密,使用时需要导入这些库和头文件,已经上传到GitHub,先从这里拉下来。
一、介绍AES加密:
AES是一种对称加密的算法,可以自己指定密钥key参与明文的加密,加解密使用同一个密钥key。共有五种加密模式,
- 电码本模式(Electronic Codebook Book (ECB)):这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。
2.密码分组链接模式(Cipher Block Chaining (CBC)):这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
3.计算器模式(Counter (CTR)):计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是 在计算器不能维持很长的情况下,密钥只能使用一次 。
4.密码反馈模式(Cipher FeedBack (CFB)):
5.输出反馈模式(Output FeedBack (OFB))
由于第4和第5种很复杂,用示意图来展示
4.密码反馈模式(Cipher FeedBack (CFB)):
5、输出反馈模式(Output FeedBack (OFB))
1.1下面用一个图片来整体说明下AES对称加密的流程:
1.2分组密码的填充
这种是PKCS#5填充方式,如果明文刚好是16字节不需要填充的时候,就再加16字节的16,后边看代码可以一目了然。这里我是使用cbc的加密模式,我这里是使用c++来实现的,Java已经基础了api直接调用即可,c++代码也可以写成jni方式去调用,跨平台是一个优势,c++代码的性能优越也是一个优势,下面看代码实现:
//测试入口写在了main函数中
int main(int argc, char** argv)
{
//记录开始加密时间,用来评估加密耗时
struct timeval tv;
gettimeofday(&tv, NULL);
int64_t start = tv.tv_sec * 1000 + tv.tv_usec / 1000;
//手动写一串明文用来加密,读者可以将这段明文替换成字节需要加密的明文字节
std::string data = "abcdefghijklmnopqrstovwxyz";
int16_t dateLen = data.length();
/**一般AES是用128bit作为加密块长度,也就是16字节,因此密钥长度一般也用16个字节的长度去做加密*/
int AES_block_num = dateLen / AES_BLOCK_SIZE + 1;
int AES_encrypt_data_len = AES_block_num * AES_BLOCK_SIZE;
unsigned char *encryptBuffer = (unsigned char *)malloc(AES_encrypt_data_len);
unsigned char *ciphertext= (unsigned char *)malloc(AES_encrypt_data_len);
if( encryptBuffer == NULL || out == NULL)
{
ALOGE("Error,malloc fail:encryptBuffer or out");
return;
}
memset(encryptBuffer,0,AES_encrypt_data_len);
memset(ciphertext,0,AES_encrypt_data_len);
/**填充的原则是:
1、如果源字节长度不是16的整数倍,需要补满16个字节的整数倍,在不够的地方补(16-len)个16-len(值),
例如:原始字节是0x10 11 01 02 05 04,总共6个字节,就需要补16-6个字节,每个字节填充16-6 = 10(十六进制是0x0a)
填充后就变成:0x10 11 01 02 05 04 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a
2、如果源字节长度正好是16的整数倍,则需要再补16个字节的十进制的16*/
int fillDataAndNum = 0;
if( dateLen % AES_BLOCK_SIZE >0 )
{
fillDataAndNum = AES_encrypt_data_len - dateLen;
}
else
{
fillDataAndNum = AES_BLOCK_SIZE;
}
memset(encryptBuffer,fillDataAndNum,AES_encrypt_data_len);
memcpy(encryptBuffer,date, dateLen);
AES_encryptOrEncrypt(encryptBuffer,AES_encrypt_data_len,TRUE,ciphertext);
if(ciphertext)
{
//这里可以把ciphertext中的值打印出来,加密后的密文就在ciphertext里
}
else
{
ALOGE("[AES_encrypt]:faild!\n");
break;
}
struct timeval tv1;
gettimeofday(&tv1, NULL);
int64_t end = tv1.tv_sec * 1000 + tv1.tv_usec / 1000;
ALOGE("11111ddddd:AES_encryptDataTime: %d",end - start);
ALOGE("11111aaa:encryptData len = %d",AES_encrypt_data_len );
//decrypt
int AES_block_num1 = AES_encrypt_data_len / AES_BLOCK_SIZE + 1;
int AES_decrypt_data_len = AES_block_num1 * AES_BLOCK_SIZE;
unsigned char *decryptBuffer = (unsigned char *)malloc(AES_decrypt_data_len );
unsigned char *out1 = (unsigned char *)malloc(AES_decrypt_data_len );
if( decryptBuffer == NULL || out1 == NULL)
{
ALOGE("Error,malloc fail:encryptBuffer or out");
return;
}
memset(decryptBuffer ,AES_decrypt_data_len );
memset(out1,0,AES_decrypt_data_len );
/**填充的原则是:
1、如果源字节长度不是16的整数倍,需要补满16个字节的整数倍,在不够的地方补(16-len)个16-len(值),
例如:原始字节是0x10 11 01 02 05 04,总共6个字节,就需要补16-6个字节,每个字节填充16-6 = 10(十六进制是0x0a)
填充后就变成:0x10 11 01 02 05 04 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a
2、如果源字节长度正好是16的整数倍,则需要再补16个字节的十进制的16*/
int fillDataAndNum1 = 0;
if(AES_decrypt_data_len % AES_BLOCK_SIZE >0 )
{
fillDataAndNum1 = AES_decrypt_data_len - AES_encrypt_data_len ;
}
else
{
fillDataAndNum1 = AES_BLOCK_SIZE;
}
memset(decryptBuffer,fillDataAndNum1,AES_decrypt_data_len );
memcpy(decryptBuffer,msg_info.msg, AES_encrypt_data_len );
AES_encryptOrEncrypt(decryptBuffer,AES_decrypt_data_len,FALSE,out1);
ALOGE("11111bbb:decryptData");
if(out1)
{
//这里可以把out1中的值打印出来,解密后的明文就在out1里
}
else
{
ALOGE("[AES_encrypt]:faild!\n");
break;
}
return 0;
}
void GBDataHandle::AES_encryptOrEncrypt(uint8_t *data,uint16_t len,bool isEncrypt,unsigned char* out)
{
AES_KEY aes;
char ivValue[] = "0000000000000000";
unsigned char key[17] = {0};
memset(key, '0', 16);//赋值默认的加密key,只用于测试
if(isEncrypt)
{
//设置加密密钥,16字节
int keyLen = strlen((char*)(key)) * 8;
ALOGD(" key = %s, keyLen = %d", key, keyLen);
if (AES_set_encrypt_key((unsigned char*)key, keyLen, &aes) < 0)
{
ALOGD("Unable to set encryption key in AES");
return;
}
}
else
{
//设置解密密钥,16字节
int keyLen = strlen((char*)(key)) * 8;
ALOGD(" key = %s, keyLen = %d", key, keyLen);
if (AES_set_decrypt_key((unsigned char*)key, keyLen, &aes) < 0)
{
ALOGD("Unable to set encryption key in AES");
return;
}
}
//std::string data_bak = (char*)pcInput;
AES_cbc_encrypt((const unsigned char*)pcInput, (unsigned char*)out, nLen,
&aes, (unsigned char*)ivValue, isEncrypt ? AES_ENCRYPT : AES_DECRYPT);
}
这样使用,加解密即可完成,因为是demo使用时可以注意封装与优化,总体思路是没错的