C++使用openssl的EVP对文件进行AES-256-CBC加解密

1、背景

    有项目需求,有对文件进行加密的功能,经过评估,最终决定使用AES-256-CBC加密。在C++中要实现这种加密有很多中方式和第三方库,由于运行环境的限制,可选择的库不多,最终决定使用openssl来进行。

    关于AES加密的相关知识直接百度就可以百度到了,这里就不赘述了。

2、加密

XuFile.h

//
// Created by zhengqiuxu on 2021/10/15.
//#ifndef VIS_ADOS_I7_XUFILE_H
#define VIS_ADOS_I7_XUFILE_H#include <vector>
#include <string>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <iostream>
#include <openssl/err.h>class XuFile {
public:/*** 用AES-256-CBC的方式加密一个文件* @param inputFile : 输入文件(源文件)地址* @param outputFile : 输出文件(加密后的文件)地址* @param key : 密钥* @param iv : 初始向量* @return  0:成功  其他:失败*/int encryptFile_AES_256_CBC(const std::string &inputFile, const std::string &outputFile, const uint8_t *key, const uint8_t *iv);private:};#endif //VIS_ADOS_I7_XUFILE_H

XuFile.cpp

/*** 用AES-256-CBC的方式加密一个文件* @param inputFile : 输入文件(源文件)地址* @param outputFile : 输出文件(加密后的文件)地址* @param key : 密钥* @param iv : 初始向量* @return  0:成功  其他:失败*/
int XuFile::encryptFile_AES_256_CBC(const std::string &inputFile, const std::string &outputFile, const uint8_t *key,const uint8_t *iv) {int ret = -1;try {/* 源文件的输入流 */std::ifstream srcIn(inputFile.c_str(), std::ios_base::in | std::ios_base::binary | std::ios_base::ate);/* 加密后的文件的输出流 */std::ofstream decOut(outputFile.c_str(), std::ios_base::out | std::ios_base::binary);/* 如果都打开了才能进行解密 */if (srcIn.is_open() && decOut.is_open()) {/* 初始化OpenSSL库 */OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \| OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);/* 初始化下加密工具 */EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();EVP_CIPHER_CTX_init(ctx);EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);EVP_CIPHER_CTX_set_padding(ctx, EVP_PADDING_PKCS7);/* 确定文件的大小 */uint64_t inputFileLen = srcIn.tellg();/* 重新将文件流指针置于文件开始的位置 */srcIn.seekg(0, std::ios_base::beg);/* 用来读数据的缓冲区 */char readBuf[8192] = {0x00};/* 用来进行加密然后写数据的缓冲区(比读取大32个字节是为了放填充数据) */uint8_t writeBuf[8192 + 32] = {0x00};/* 记录读了多少长度的变量 */uint64_t allReadLen = 0;/* 循环从流里读出文件 */while (allReadLen < inputFileLen) {/* 当前应该读多长的数据 */int curRead = sizeof(readBuf);if (inputFileLen - allReadLen < sizeof(readBuf)) {curRead = inputFileLen - allReadLen;}/* 读出原文 */srcIn.read(readBuf, curRead);/* 记录一下长度 */allReadLen = allReadLen + curRead;/* 对原文一包包读出来进行加密 */int toEnBufLen = 0;int curEnLen = 0;if (!EVP_EncryptUpdate(ctx, writeBuf, &toEnBufLen, reinterpret_cast<const unsigned char *>(readBuf),curRead)) {printf("EVP_EncryptUpdate failed!  err:%s  \n", ERR_error_string(ERR_get_error(), NULL));break;}/* 如果是最后一包了,那么就调用一下结束,同时拿到填充出来的数据,只有调用了结束,才会加填充,这样加密一整个文件的时候就只是尾部有填充字节 */if (curRead < sizeof(readBuf)) {if (!EVP_EncryptFinal_ex(ctx, writeBuf + toEnBufLen, &curEnLen)) {printf("EVP_EncryptFinal_ex failed!  err:%s  \n", ERR_error_string(ERR_get_error(), NULL));break;}toEnBufLen += curEnLen;}
//                    /* 将密文写入文件 */decOut.write(reinterpret_cast<const char *>(writeBuf), toEnBufLen);//                    printf("allReadLen=%llu   inputFileLen=%llu  toEnBufLen=%d curEnLen=%d curRead=%d\n",allReadLen,inputFileLen,toEnBufLen,curEnLen,curRead);}/* 关闭流 */srcIn.close();decOut.close();/* 关闭加密工具 */EVP_CIPHER_CTX_reset(ctx);EVP_CIPHER_CTX_free(ctx);ret = 0;} else {printf("%s or %s can not open! \n", inputFile.c_str(), outputFile.c_str());}} catch (...) {printf("encryptFile_AES_256_CBC failed! err:%s  \n", strerror(errno));}return ret;}

3、解密

XuFile.h

//
// Created by zhengqiuxu on 2021/10/15.
//#ifndef VIS_ADOS_I7_XUFILE_H
#define VIS_ADOS_I7_XUFILE_H#include <vector>
#include <string>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <iostream>
#include <openssl/err.h>class XuFile {
public:/*** 用AES-256-CBC的方式解密一个文件* @param inputFile : 输入文件(源文件)地址* @param outputFile : 输出文件(解密后的文件)地址* @param key : 密钥* @param iv : 初始向量* @return  0:成功  其他:失败*/int decryptFile_AES_256_CBC(const std::string& inputFile, const std::string& outputFile, const uint8_t *key, const uint8_t *iv);private:};#endif //VIS_ADOS_I7_XUFILE_H

XuFile.cpp

/*** 用AES-256-CBC的方式解密一个文件* @param inputFile : 输入文件(源文件)地址* @param outputFile : 输出文件(解密后的文件)地址* @param key : 密钥* @param iv : 初始向量* @return  0:成功  其他:失败*/
int XuFile::decryptFile_AES_256_CBC(const std::string &inputFile, const std::string &outputFile, const uint8_t *key, const uint8_t *iv) {int ret = -1;try {/* 源文件的输入流 */std::ifstream srcIn(inputFile.c_str(),std::ios_base::in | std::ios_base::binary | std::ios_base::ate);/* 解密后的文件的输出流 */std::ofstream decOut(outputFile.c_str(),std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);/* 如果都打开了才能进行解密 */if(srcIn.is_open() && decOut.is_open()){/* 初始化OpenSSL库 */OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \| OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);/* 初始化下解密工具 */EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();EVP_CIPHER_CTX_init(ctx);int rr = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);EVP_CIPHER_CTX_set_padding(ctx,EVP_PADDING_PKCS7);/* 确定文件的大小 */uint64_t inputFileLen = srcIn.tellg();/* 重新将文件流指针置于文件开始的位置 */srcIn.seekg(0, std::ios_base::beg);/* 用来读数据的缓冲区 */char readBuf[8192] = {0x00};/* 用来进行解密然后写数据的缓冲区(比读取大32个字节是为了放填充数据) */uint8_t writeBuf[8192+32] = {0x00};/* 记录读了多少长度的变量 */uint64_t allReadLen = 0;/* 循环从流里读出文件 */while (allReadLen < inputFileLen){/* 当前应该读多长的数据 */int curRead = sizeof(readBuf);if(inputFileLen - allReadLen < sizeof(readBuf)){curRead = inputFileLen - allReadLen;}/* 读出密文 */srcIn.read(readBuf, curRead);/* 记录一下长度 */allReadLen = allReadLen + curRead;/* 对密文一包包读取数据的数据进行解密 */int toEnBufLen = 0;int curEnLen = 0;if(!EVP_DecryptUpdate(ctx, writeBuf, &toEnBufLen,reinterpret_cast<const unsigned char *>(readBuf), curRead)){printf("EVP_DecryptUpdate failed!  err:%s  \n", ERR_error_string(ERR_get_error(),NULL));break;}/* 如果是最后一包了,那么就调用一下结束 */if(curRead < sizeof(readBuf)){if(!EVP_DecryptFinal(ctx, writeBuf + toEnBufLen, &curEnLen)){printf("EVP_DecryptFinal failed!  err:%s  \n", ERR_error_string(ERR_get_error(),NULL));break;}toEnBufLen += curEnLen;}//                    /* 将解密后的数据写入文件 */decOut.write(reinterpret_cast<const char *>(writeBuf), toEnBufLen);printf("decryptFile_AES_256_CBC allReadLen=%llu   inputFileLen=%llu  toEnBufLen=%d curEnLen=%d curRead=%d\n",allReadLen,inputFileLen,toEnBufLen,curEnLen,curRead);}/* 关闭流 */srcIn.close();decOut.close();/* 关闭解密工具 */EVP_CIPHER_CTX_reset(ctx);EVP_CIPHER_CTX_free(ctx);ret = 0;}else{printf("%s or %s can not open! \n", inputFile.c_str(),outputFile.c_str());}} catch (...) {printf("decryptFile_AES_256_CBC failed! errstr:%s  \n",  ERR_error_string(ERR_get_error(),NULL));}return ret;}

4、其他

开发过程中遇到过几个问题,由于一开始不熟悉耽误了时间,现在记录下。

1、加密的时候由于是从流里面一段段数据读出来的,所以每次调用完EVP_EncryptUpdate都会去调用一下EVP_EncryptFinal_ex,导致每一段数据后面都被添加了填充数据,所以后面改成所有的数据加密完后再调用EVP_EncryptFinal_ex获取填充数据。

2、使用C++加密后再使用Java解密,用的是javax.crypto.Cipher这个类。在key或者IV错误的情况,它可能有各种提示,比如说:填充数据错误、块错误等,但是原因都是key或者IV错误,所以解密失败第一时间还是要检查key跟IV对不对。

3、加密的时候可以选择PKCS7填充或者PKCS5填充,但是解密的时候要使用PKCS7,因为PKCS7兼容PKCS5。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/597658.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于多反应堆的高并发服务器【C/C++/Reactor】(中)Buffer的创建和销毁、扩容、写入数据

TcpConnection:封装的就是建立连接之后得到的用于通信的文件描述符&#xff0c;然后基于这个文件描述符&#xff0c;在发送数据的时候&#xff0c;需要把数据先写入到一块内存里边&#xff0c;然后再把这块内存里边的数据发送给客户端&#xff0c;除了发送数据&#xff0c;剩下…

Jvm垃圾收集器系列之CMS收集器(个人见解仅供参考)

问&#xff1a;什么是CMS收集器&#xff1f; 答&#xff1a;CMS&#xff08;Concurrent Mark Sweep&#xff09;收集器是Java HotSpot虚拟机中的一种垃圾收集器&#xff0c;主要用于实现低延迟的垃圾回收。 问&#xff1a;CMS收集器的主要目标是什么&#xff1f; 答&#xff1a…

基于综合特征的细菌噬菌体宿主预测工具iPHoP (Integrated Phage HOst Prediction)的介绍以及使用方法详细流程

介绍 iPHoP&#xff08;Integrated Phage HOst Prediction&#xff09;是一种基于综合特征的细菌噬菌体宿主预测方法。它是通过整合基因组序列、蛋白质序列和宿主基因组信息来预测细菌噬菌体的宿主范围。 iPHoP的预测过程分为三个步骤&#xff1a;特征提取、特征选择和宿主预…

Ubuntu22.04无法启动EasyConnect的问题

转自&#xff1a;https://juejin.cn/post/7121236352032571422 无法启动主要是因为依赖包版本高&#xff0c;不兼容&#xff0c;要降级 解决方法&#xff1a; 1、安装easyconnect&#xff08;已安装就跳过&#xff09; 2、EasyConnect的安装位置在 /usr/share/sangfor/EasyCon…

【Spring实战】21 Spring Data REST 常用功能详细介绍

文章目录 1. 资源导出&#xff08;Resource Exporting&#xff09;2. 查询方法&#xff08;Query Methods&#xff09;3. 分页和排序&#xff08;Pagination and Sorting&#xff09;4. 关联关系&#xff08;Associations&#xff09;5. 事件&#xff08;Events&#xff09;6. …

“华为杯”杭州电子科技大学2023新生编程大赛---树

题目链接 Problem Description 给定一棵包含 n 个节点的带边权的树&#xff0c;树是一个无环的无向联通图。定义 xordist(u,v) 为节点 u 到 v 的简单路径上所有边权值的异或和。 有 q 次询问&#xff0c;每次给出 l r x&#xff0c;求 ∑rilxordist(i,x) 的值。 Input 测试…

一篇文章学会如何在 NestJS 中使用 Redis 并基于 Redis 实现接口访问限频率

前言 在处理高频数据操作和大规模并发请求的场合&#xff0c;我们需要一种机制能够快速读取和缓存数据&#xff0c;这时 Redis 就闪亮登场了。Redis 是一个开源的内存中数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。 NestJS 是一个灵活且模块化的Node.j…

frp透传软件最新toml格式的配置文件的使用

frp软件在0.52.0版本开始支持 toml格式的配置文件了&#xff0c;并将在后继某个版本开始取消对 ini配置格式的支持。这里做一下新旧配置文件的比较。 一、frps 服务端配置文件的变化 frps.ini cat /etc/frp/frps.ini[common] bind_port7000 vhost_http_port8080 vhost_https…

JVM之内存模型带参数

Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里)&#xff1a; java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize256M ‐XX:MaxMetaspaceSize256M ‐jar xxxxxx.jar-Xss&#xff1a;每个线程的栈大小 -Xms&#xff1a;设置…

关于“Python”的核心知识点整理大全61

目录 注意 20.1.4 使用 jumbotron 设置主页的样式 index.html 20.1.5 设置登录页面的样式 login.html 20.1.6 设置 new_topic 页面的样式 new_topic.html 20.1.7 设置 topics 页面的样式 topics.html 元素&#xff0c;让它们在页面上显得大些&#xff08;见2&#xf…

imgaug库指南(三):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

prometheus与zabbix监控的对比介绍

一、普米与zabbix基本介绍 1、prometheus介绍 Prometheus的基本原理是Prometheus Server通过HTTP周期性抓取被监控组件的监控数据&#xff0c;任意组件只要提供对应的HTTP接口并且符合Prometheus定义的数据格式&#xff0c;就可以接入Prometheus监控。 工作流程大致分为收集数…

嵌入式Linux之MX6ULL裸机开发学习笔记(汇编LED灯点亮)

汇编LED驱动实验 1.驱动编写 首先创建在vscode上创建工作区&#xff0c;创建led.s汇编文件&#xff0c;然后编写以下程序 .global _start 全局标号 _start: /* 使能所有外设时钟 */ ldr r0,0x020c4068 CCGR0 ldr r1,0xffffffff 要向CCGR0写入的数据 str r1,[r0] 将0xff…

优化企业运营,深入探索SAP库存管理解决方案

SAP库存管理是SAP提供的一款领先的企业库存管理解决方案。它致力于帮助企业实现对库存的全面掌控&#xff0c;优化供应链管理&#xff0c;降低库存成本&#xff0c;提高客户满意度。这个功能强大的系统为企业提供了丰富的仓储管理功能&#xff0c;如库存盘点、物料追踪、供应商…

JAVA批量新增、批量修改

JAVA批量新增、批量修改 若数据量非常大&#xff0c;可以把List拆成多份&#xff0c;每份1000条数据。NetPointDTO批量新增SQL.xml批量修改SQL.xml 若数据量非常大&#xff0c;可以把List拆成多份&#xff0c;每份1000条数据。 import cn.hutool.core.collection.ListUtil; im…

Unity Enum位掩码(BitMask)的运用

Unity Enum位掩码&#xff08;BitMask&#xff09;的运用 前言项目使用场景代码编写定义技能枚举角色类学习技能检查技能 添加并设置脚本运行效果总结 感谢 前言 在Unity游戏开发中&#xff0c;我们经常会面临需要对一组相关的状态进行管理的情况。Enum位掩码是一种有效的方法…

Spring 与 SpringBoot:一窥两者的奥秘与差异

随着 Java 开发领域的不断演进&#xff0c;Spring 框架已经成为了许多企业级应用的首选。然而&#xff0c;近年来&#xff0c;随着 SpringBoot 的兴起&#xff0c;许多开发者开始对其产生了浓厚的兴趣。尽管 SpringBoot 和 Spring 都来自于同一个家族&#xff0c;并且都是为了简…

【LeetCode】150. 逆波兰表达式求值(ASCII码)

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;150. 逆波兰表达式求值 代码随想录题解地址&#xff1a;代码随想录 题目简介 即将后缀表达式转换成中缀表达式并计算。 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 …

【编译原理】期末预习PPT前四章笔记II

看了看学校的ppt&#xff0c;记的比较随意O.o 因为我的考试范围里边没有简答所以概念什么的没怎么记 没有简答只有选择真是太好了嘿嘿嘿 目录 I. 概述&#xff08;好多字。。&#xff09; 一、高级语言的分类 1、体裁 2、执行方式 二、各种语言的执行方式 三、编译程序…

读算法霸权笔记11_微目标

1. 脸书 1.1. 一份请愿书属于脸书了&#xff0c;而社交网络的算法会对如何最大限度地利用这份请愿书做出判断 1.1.1. 脸书的算法在决定谁能看到我的请愿书时会把所有因素都考虑在内 1.2. 通过改变信息推送的方式&#xff0c;脸书研究了我们…