AES对称加密使用方式与浅析

首先,AES的加解密算法来自于OpenSSL库,Android系统已经集成了这个算法,Java中可以直接导入类,在Android系统的native层中c和c++的使用时也可以直接从系统库中链接到OpenSSL库(NDK使用和c++是一样的,都是在Android.mk或者CMakeLists.txt中链接),关于头文件 因为使用的是系统库,因此可以直接include,当然自己编的库那就一定要有头文件了,可以从OpenSSL官网获取,还有一个问题:如果加解密时需要用到base64去编解码,那就得自己编库,系统库里好像不包含,编译会报错
1、Android.mk中链接OpenSSL库:

LOCAL_SHARED_LIBRARIES := \
    libcrypto\
    libssl

2、CMakeLists.txt中链接OpenSSL库:

include_directories(include)
# import openssl library files BEGIN
set(SSL_LIB_NAME ssl)
add_library(${SSL_LIB_NAME} SHARED IMPORTED)
set_target_properties(${SSL_LIB_NAME} PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/lib${SSL_LIB_NAME}.so)

set(CRYPTO_LIB_NAME crypto)
add_library(${CRYPTO_LIB_NAME} SHARED IMPORTED)
set_target_properties(${CRYPTO_LIB_NAME} PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/lib${CRYPTO_LIB_NAME}.so)
# import openssl library files END

target_link_libraries(
        native-lib
        ssl
        crypto
        ${log-lib})

一、介绍AES加密:

AES是一种对称加密的算法,可以自己指定密钥key参与明文的加密,加解密使用同一个密钥key。共有五种加密模式,

  1. 电码本模式(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++代码的性能优越也是一个优势,下面看代码实现:
先看下使用到的头文件:

#include <openssl/obj_mac.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
#include <openssl/crypto.h>
#include <openssl/buffer.h> 
#include <openssl/bio.h>
#include <openssl/opensslconf.h>
#include <openssl/bn.h>
#include <sys/time.h>
#include <ctime>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <utils/Log.h>
#include <cutils/log.h>
//测试入口写在了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
    //解密的时候直接把密文buffer传过去就行,无需再16字节对齐了
        unsigned char *out1 = (unsigned char *)malloc(AES_decrypt_data_len);
        if( out1 == NULL)
        {
           ALOGE("Error,malloc fail:decryptBuffer or out");
           return;
        }
        memset(out1,0,AES_decrypt_data_len);
		AES_encryptOrEncrypt(ciphertext,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使用时可以注意封装与优化,总体思路是没错的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值