初次使用openssl中aes(含代码)

环境:vs2019 Win32OpenSSL-3_0_7

前言

最近在做毕业设计,做一个测试工具,整体功能都已实现完毕,目前在不断进行扩展。因为里面包含网络传输功能,所有就想着加入各种常见的加密算法,虽然工具支持python和java脚本,可以通过依赖第三方或者自己实现,但是为了方便还是在程序中实现。因为测试工具中https的请求依赖于openssl,所以就使用openssl来实现aes

先提供两个加解密网址:

https://www.mklab.cn/utils/aes

https://the-x.cn/cryptography/Aes.aspx

最初代码不清楚,网上随便找了一份下来修改,再慢慢学习,里面使用的函数是AES_ecb_encrypt和AES_cbc_encrypt,这种函数还要自己去实现PCKS5-7来对加密的数据进行填充,原理看代码,其实很简单

//假设str = 0xff
//那么填充结果 =  0xff 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f
//假设str = 0xff
//那么填充结果 =  0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 
int Irg3_Aes::PKCS5Padding(unsigned char* str, int len)
{
    int remain, i;
    remain = 16 - len % 16;
    for (i = 0; i < remain; i++){
        str[len + i] = remain;
    }
    str[len + i] = '\0';
    return len + remain;
}

int Irg3_Aes::DePKCS5Padding(unsigned char* str, int str_len)
{
    int remain, i;
    remain = *(str + str_len - 1);//读取填充的个数
    for (i = 0; i < remain; i++) { str--; }
    str++;
    *str = '\0';//截断
    return remain;
}

全部代码

irg3_aes.h

#pragma once


#define IRG3_AES_BLOCK_SIZE 16
#define IRG3_AES_MAX_SEND_BUFF_SIZE 4096
#define IRG3_AES_MAX_PKT_BUFF 4096
/*
* 依赖openssl库
* AES	CBC	PKCS5-7	128	192	256	
*/
class Irg3_Aes
{
private:
	Irg3_Aes() {};
public:
	struct aes_node {
		char* in_ = nullptr;	//需要输入的字符串
		int in_len_;	//输入字符串长度
		char key_[33] = "11";	//密钥
		int key_len_ = 16;	//密钥长度	16	24	32
		char iv_[16] = "11";	//向量固定16位
	};

	virtual ~Irg3_Aes() {}
	static Irg3_Aes* getInstance() {
		static Irg3_Aes aes_;
		return &aes_;
	}
public:

	int aes_cbc_base64_enc_data(aes_node& node_, char* out);
	int aes_cbc_base64_dec_data(aes_node& node_, char* out);

	int aes_ecb_base64_enc_data(aes_node& node_, char* out);
	int aes_ecb_base64_dec_data(aes_node& node_, char* out);
	//AES	CBC	加密	return	1 成功	0 失败
	int aes_cbc_encrypt(aes_node& node_, char* out);
	//AES	CBC	解密	return	1 成功	0 失败
	int aes_cbc_decrypt(aes_node& node_, char* out);
	//AES	ECB	加密	return	1 成功	0 失败
	int aes_ecb_encrypt(aes_node& node_, char* out);
	//AES	ECB	解密	return	1 成功	0 失败
	int aes_ecb_decrypt(aes_node& node_, char* out);
private:

	
	/*
	* 对初始数据进行PKCS5Padding填充
	* return	正向测试填充后的字符串长度
	*/
	int PKCS5Padding(unsigned char* str, int len);
	/*
	* 对明文进行PKCS5Padding填充反填充(去除后面的填充乱码)
	* return	反填充个数
	*/
	int DePKCS5Padding(unsigned char* str, int str_len);
	/*
	* 对in_str进行base64编码 输出到out_str
	* return	-1失败 成功返回编号的字节数
	*/
	int base64_encode(const char* in_str, int in_len, char* out_str);
	/*
	* 对str_in进行base64编码 输出到out_str
	* return	-1失败 成功返回编号的字节数
	*/
	int base64_decode(char* in_str, int in_len, char* out_str);
	/*
	* 获取base64 decode后的长度
	*/
	int get_base64_decode_len(char* data_, int len_);
};

irg3_aes.cpp

#include "irg3_aes.h"


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/sha.h>
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <time.h>

#pragma comment(lib,"libcrypto.lib")

int Irg3_Aes::aes_cbc_base64_enc_data(aes_node& node_, char* out)
{
    int i = 0;
    int len_b64 = 0;
    int enc_len = ((node_.in_len_ / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
    if (!aes_cbc_encrypt(node_, out)) {
        printf("encrypt error\n");
        return -1;
    }

    base64_encode(out, enc_len, out);
    len_b64 = strlen(out);
    /* base64 加密后会存在回车换行符*/
    if (out[len_b64 - 1] == '\n') {
        out[len_b64 - 1] = '\0';
        len_b64--;
    }
    return len_b64;
}
int Irg3_Aes::aes_cbc_base64_dec_data(aes_node& node_, char* out)
{
    int dec_len = 0;
    node_.in_[node_.in_len_++] = '\n'; /* 单独解码需要增加\n */
    node_.in_[node_.in_len_] = '\0';
    node_.in_len_ = base64_decode(node_.in_, node_.in_len_,node_.in_);
    dec_len = aes_cbc_decrypt(node_,out);
    out[dec_len] = '\0';
    return dec_len;
}

int Irg3_Aes::aes_ecb_base64_enc_data(aes_node& node_, char* out)
{
    int i = 0;
    int len_b64 = 0;
    int enc_len = ((node_.in_len_ / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
    if (!aes_ecb_encrypt(node_, out)) {
        printf("encrypt error\n");
        return -1;
    }
    base64_encode(out, enc_len, out);
    len_b64 = strlen(out);
    /* base64 加密后会存在回车换行符*/
    if (out[len_b64 - 1] == '\n') {
        out[len_b64 - 1] = '\0';
        len_b64--;
    }
    return len_b64;
}

int Irg3_Aes::aes_ecb_base64_dec_data(aes_node& node_, char* out)
{
    int dec_len = 0;
    node_.in_[node_.in_len_++] = '\n'; /* 单独解码需要增加\n */
    node_.in_[node_.in_len_] = '\0';
    node_.in_len_ = base64_decode(node_.in_, node_.in_len_, node_.in_);
    dec_len = aes_ecb_decrypt(node_, out);
    out[dec_len] = '\0';
    return dec_len;
}


int Irg3_Aes::aes_cbc_encrypt(aes_node& node_, char* out)
{
    int ret_val_ = 0;
    AES_KEY aes;
    if (!node_.in_ || !node_.key_||!out) {
        return ret_val_;
    }
    unsigned char*aes_encode_temp = new unsigned char[IRG3_AES_MAX_PKT_BUFF];
    memset(aes_encode_temp, 0, IRG3_AES_MAX_PKT_BUFF);
    //抽取数据
    memcpy(aes_encode_temp, node_.in_, node_.in_len_);

    do {
        //进行PCK5填充 获取填充后长度
        int len = PKCS5Padding(aes_encode_temp, node_.in_len_);
        //第二个参数位密钥长度*8,代表几位加密
        //16位密钥对应128位加密
        //24位密钥对应192位加密
        //32位密钥对应256位加密
        if (AES_set_encrypt_key((unsigned char*)node_.key_, node_.key_len_*8, &aes) < 0) {
            printf("fail to set encrypt key");
            break;
        }
        //AES_cbc_encrypt 会破坏掉iv_
        unsigned char enc_iv[17] = "";
        memcpy(enc_iv, node_.iv_, 16);
        AES_cbc_encrypt(aes_encode_temp, (unsigned char*)out, len, &aes, enc_iv, AES_ENCRYPT);
        ret_val_ = 1;
    } while (false);
    delete[]aes_encode_temp;
    return ret_val_;
}


int Irg3_Aes::aes_cbc_decrypt(aes_node& node_, char* out)
{
    int ret_val_ = 0;
    AES_KEY aes;
    if (!node_.in_ || !node_.key_ || !out) {
        return ret_val_;
    }

    if (AES_set_decrypt_key((unsigned char*)node_.key_, node_.key_len_ * 8, &aes) < 0) {
        printf("fail to set encrypt key");
        return 0;
    }
    //AES_cbc_encrypt 会破坏掉iv
    unsigned char enc_iv[17] = "";
    memcpy(enc_iv, node_.iv_, 16);
    AES_cbc_encrypt((unsigned char*)node_.in_, (unsigned char*)out, node_.in_len_, &aes,enc_iv, AES_DECRYPT);
    return (node_.in_len_ - DePKCS5Padding((unsigned char*)out, node_.in_len_));
}

int Irg3_Aes::aes_ecb_encrypt(aes_node& node_, char* out)
{
    int ret_val_ = 0;
    AES_KEY aes;
    if (!node_.in_ || !node_.key_ || !out) {
        return ret_val_;
    }
    unsigned char* aes_encode_temp = new unsigned char[IRG3_AES_MAX_PKT_BUFF];
    memset(aes_encode_temp, 0, IRG3_AES_MAX_PKT_BUFF);
    //抽取数据
    memcpy(aes_encode_temp, node_.in_, node_.in_len_);

    do {
        //进行PCK5填充 获取填充后长度
        int len = PKCS5Padding(aes_encode_temp, node_.in_len_);
        //第二个参数位密钥长度*8,代表几位加密
        //16位密钥对应128位加密
        //24位密钥对应192位加密
        //32位密钥对应256位加密
        if (AES_set_encrypt_key((unsigned char*)node_.key_, node_.key_len_ * 8, &aes) < 0) {
            printf("fail to set encrypt key");
            break;
        }
        AES_ecb_encrypt(aes_encode_temp, (unsigned char*)out, &aes, AES_ENCRYPT);
        ret_val_ = 1;
    } while (false);
    delete[]aes_encode_temp;
    return ret_val_;
}

int Irg3_Aes::aes_ecb_decrypt(aes_node& node_, char* out)
{
    int ret_val_ = 0;
    AES_KEY aes;
    if (!node_.in_ || !node_.key_ || !out) {
        return ret_val_;
    }

    if (AES_set_decrypt_key((unsigned char*)node_.key_, node_.key_len_ * 8, &aes) < 0) {
        printf("fail to set encrypt key");
        return 0;
    }
    AES_ecb_encrypt((unsigned char*)node_.in_, (unsigned char*)out, &aes,AES_DECRYPT);
    return (node_.in_len_ - DePKCS5Padding((unsigned char*)out, node_.in_len_));
}

int Irg3_Aes::PKCS5Padding(unsigned char* str, int len)
{
    int remain, i;
    remain = 16 - len % 16;
    //printf("remain = %d\n",remain);
    for (i = 0; i < remain; i++){
        str[len + i] = remain;
        //printf("str[len+i]= %d\n",str[len+i]);
    }
    str[len + i] = '\0';
    return len + remain;
}



int Irg3_Aes::DePKCS5Padding(unsigned char* str, int str_len)
{
    int remain, i;

    remain = *(str + str_len - 1);//读取填充的个数
    //printf("remain = %d\n",remain);
    for (i = 0; i < remain; i++) { str--; }
    str++;
    *str = '\0';//截断

    return remain;
}



int Irg3_Aes::base64_encode(const char* in_str, int in_len, char* out_str)
{
    BIO* b64 = nullptr, * bio = nullptr;
    BUF_MEM* bptr = nullptr;
    size_t size = -1;

    if (in_str == nullptr || out_str == nullptr)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);
    BIO_write(bio, in_str, in_len);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bptr);
    memcpy(out_str, bptr->data, bptr->length);
    out_str[bptr->length] = '\0';
    size = bptr->length;
    BIO_free_all(bio);

    return size;
}


int Irg3_Aes::base64_decode(char* in_str,int in_len, char* out_str)
{
    int len_ = -1;
    BIO* b64 = NULL;
    BIO* bmem = NULL;
    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new_mem_buf(in_str, in_len);
    bmem = BIO_push(b64, bmem);
    len_ = BIO_read(bmem, out_str, in_len);
    BIO_free_all(bmem);
    return len_;
}

int Irg3_Aes::get_base64_decode_len(char* data_,int len_)
{
    int j = 0;
    int len_b64 = 0;
    /* base64加密过程,每76个字符会添加\r\n,计算解码后长度时需去除,但解码数据不需要去除 */
    for (int i = 0; i < len_; i++) {
        if (data_[i] == '\n' || data_[i] == '\r') {
            continue;
        }
        else if (data_[i] == '=') {
            len_b64--;
        }
        j++;
    }
    // 计算解码后的数据长度
    len_b64 += j / 4 * 3;
    return len_b64;
}

后面找国密sm2、sm4时,发现有另一种方法EVP_CIPHER_CTX_new,EVP_EncryptInit_ex,EVP_EncryptUpdate,EVP_EncryptFinal_ex,EVP_CIPHER_CTX_free,而且用这种好像更完善,至少它包含AES CTR

代码从网上找的,来源忘记了,只是自己稍做修改,还没封装

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#pragma comment(lib,"libcrypto.lib")

//这个一定要加,不然会出现OPENSSL_Uplink(7A3C5380,07): no OPENSSL_Applink
extern "C"
{
#include <openssl/applink.c>
};


int encrypt(const char* data_, int data_len_, const char* key,
    const char* iv, unsigned char* ret_)
{
    EVP_CIPHER_CTX* ctx;
    int len;
    //初始化密码上下文ctx
    if (!(ctx = EVP_CIPHER_CTX_new()))
        return -1;

    //设置什么类型加密
    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, (unsigned char*)key, (unsigned char*)iv))
        return -1;

    //开始加密
    if (1 != EVP_EncryptUpdate(ctx, ret_, &len,(unsigned char*)data_, data_len_))
        return -1;
    int ret_len_ = len;

    //调用EVP_EncryptFinal_ex情况:
    //当加密的数据长度不是cipher_block_size整数倍,又没有disable padding功能
    //当加密的数据长度为cipher_block_size的整数倍,但是没有disable padding功能
    //不调用情况:
    //当加密的数据长度为cipher_block_size的整数倍,又disable padding功能
    if (1 != EVP_EncryptFinal_ex(ctx, ret_ + len, &len))
        return -1;
    ret_len_ += len;

    EVP_CIPHER_CTX_free(ctx);
    return ret_len_;
}

int decrypt(const char* data, int data_len, const char* key,
    const char* iv,unsigned char* ret_)
{
    EVP_CIPHER_CTX* ctx;

    int len;

    if (!(ctx = EVP_CIPHER_CTX_new()))
        return -1;


    if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, (unsigned char*)key, (unsigned char*)iv))
        return -1;


    if (1 != EVP_DecryptUpdate(ctx, ret_, &len, (unsigned char*)data, data_len))
        return -1;
    int ret_len_ = len;

    if (1 != EVP_DecryptFinal_ex(ctx, ret_ + len, &len))
        return -1;
    ret_len_ += len;

    EVP_CIPHER_CTX_free(ctx);

    return ret_len_;
}

int main(int argc, char const* argv[])
{
    //EVP_aes_128_ctr
    //调用的是128,所以key长度为16位,不足代码内部用0x00补齐,超过自动截断
    const char* key = "1";
    //固定16位
    const char* iv = "0000000000000000";
    //数据默认使用PKCS5-7进行补齐,以16为一块,不足自动补齐
    //大致原理    remain = 16 - len % 16; for (i = 0; i < remain; i++) { str[len + i] = remain; }
    const char* data = "hello";

    unsigned char ciphertext[128] = { 0 };
    unsigned char decryptedtext[128];
    int decryptedtext_len, ciphertext_len;

    printf("data is: %s\n", data);

    ciphertext_len = encrypt(data, strlen(data), key, iv,
        ciphertext);

    printf("Ciphertext is %d bytes long:\n", ciphertext_len);
    //打印16进制数据
    BIO_dump_fp(stdout, ciphertext, ciphertext_len);
    decryptedtext_len = decrypt((char*)ciphertext, ciphertext_len, key, iv,
        decryptedtext);

    decryptedtext[decryptedtext_len] = '\0';

    printf("Decrypted text is: %s\n", decryptedtext);

    return 0;
}

里面具体的使用我还没来得及了解,最后是我存在的一些疑问,也是我自己的一个误区,就是为什么不同语言实现的aes结果会不一样,其实归根结底是因为在设置参数的时候,你以为他们一致,其实他们不一致

工具地址是https://www.mklab.cn/utils/aes

同一个算法不同的组件结果不同,按道理说如果参数都一样,那么结果应该一样,所以我在key中设置16位0,结果一样

代表CryptoJS 和 JDK Cipher这两者当key不足,或者说不够时,默认填充的值不一致,其实如果去看源码的话会发现JDK Cipher中key不够默认填充是0x00,而CryptoJS好像是按照一定的规则进行生成,详细的我还没深入去了解

而且当key大于16位时(因为我们选的是128位),它的值又不一样了

 说这个就是不要陷入一个误区:为什么不同语言实现的aes结果会不一样,其实归根结底是因为在设置参数的时候,你以为他们一致,其实他们不一致

 顺便一提,openssl中默认key不足会用0x00补齐,超出会自动截断

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值