openssl-AES-128-CTR加解密char型数组分析
本文章通过对一个unsigned char*类型的数据做简单的加解密操作来学习如何使用openssl库函数。
openssl为3.0.0,对此前版本的很多函数都不兼容。

加解密源码
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>
#include <openssl/rand.h>
#include <openssl/aes.h>
#include <stdlib.h>void handleErrors(void) {ERR_print_errors_fp(stderr);abort();
}int main() {ERR_load_crypto_strings();OpenSSL_add_all_algorithms();// 密钥和初始化向量unsigned char key[AES_BLOCK_SIZE];unsigned char iv[AES_BLOCK_SIZE];// 随机生成密钥和初始化向量RAND_bytes(key, sizeof(key));RAND_bytes(iv, sizeof(iv));// 要加密的数据unsigned char plaintext[] = "The quick brown fox jumps over the lazy dog";// 加密后的数据unsigned char ciphertext[sizeof(plaintext)];// 解密后的数据unsigned char decryptedtext[sizeof(plaintext)];// 创建并初始化加解密上下文EVP_CIPHER_CTX *ctx;int len;// 初始化加密上下文if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();// 初始化加密操作if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv)) handleErrors();// 执行加密操作if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, sizeof(plaintext))) handleErrors();EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);// 清理加密上下文EVP_CIPHER_CTX_free(ctx);// 初始化解密上下文if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();// 初始化解密操作if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv)) handleErrors();// 执行解密操作if (1 != EVP_DecryptUpdate(ctx, decryptedtext, &len, ciphertext, sizeof(ciphertext))) handleErrors();EVP_DecryptFinal_ex(ctx, decryptedtext + len, &len);// 清理解密上下文EVP_CIPHER_CTX_free(ctx);// 打印解密后的数据printf("Decrypted text: %s\n", decryptedtext);// 清理 OpenSSLERR_free_strings();return 0;
}
运行脚本
#!/bin/bash
gcc aesctr.c -o aesctr -lcrypto
./aesctr
输出的数据与加密前的数据一样,当然这只是加密字符串可以如此简单,但是如果数据是结构体呢?

代码整体分析
这段代码是一个使用 OpenSSL 库进行 AES-128-CTR 模式加密和解密的 C 程序。下面是代码的详细解析:
-
包含头文件:
<openssl/evp.h>:提供加密算法的封装。<openssl/err.h>:提供错误处理功能。<openssl/rand.h>:提供随机数生成功能。<openssl/aes.h>:提供 AES 加密算法的函数。<stdlib.h>:提供标准库函数,如abort()。
-
错误处理函数:
handleErrors:当遇到错误时,打印错误信息并终止程序。
-
主函数
main:ERR_load_crypto_strings:加载错误字符串,以便ERR_print_errors_fp函数可以打印出可读的错误信息。OpenSSL_add_all_algorithms:注册所有 OpenSSL 加密算法。
-
密钥和初始化向量:
unsigned char key[AES_BLOCK_SIZE]:定义一个 128 位(16 字节)的密钥数组。unsigned char iv[AES_BLOCK_SIZE]:定义一个 128 位(16 字节)的初始化向量(IV)数组。
-
生成密钥和 IV:
RAND_bytes(key, sizeof(key)):生成随机密钥。RAND_bytes(iv, sizeof(iv)):生成随机 IV。
-
定义数据:
unsigned char plaintext[]:定义要加密的明文数据。unsigned char ciphertext[sizeof(plaintext)]:定义存储加密后数据的数组。unsigned char decryptedtext[sizeof(plaintext)]:定义存储解密后数据的数组。
-
加密和解密上下文:
EVP_CIPHER_CTX *ctx:定义一个加密上下文指针,用于存储加密和解密过程中的状态信息。
-
加密过程:
EVP_CIPHER_CTX_new:创建一个新的加密上下文。EVP_EncryptInit_ex:初始化加密操作,设置加密算法为 AES-128-CTR,使用密钥和 IV。EVP_EncryptUpdate:执行加密操作,将明文数据加密到ciphertext数组中。EVP_EncryptFinal_ex:完成加密操作,处理任何剩余的数据。
-
解密过程:
- 与加密过程类似,但使用
EVP_DecryptInit_ex、EVP_DecryptUpdate和EVP_DecryptFinal_ex函数进行解密。
- 与加密过程类似,但使用
-
打印解密后的数据:
printf:打印解密后的文本。
-
清理 OpenSSL:
ERR_free_strings:释放加载的错误字符串。
-
返回:
return 0:程序正常退出。
这个程序演示了如何使用 OpenSSL 库进行 AES-128-CTR 模式的加密和解密。它首先生成随机的密钥和 IV,然后使用这些密钥和 IV 对明文数据进行加密,最后使用相同的密钥和 IV 对密文数据进行解密,以验证加密和解密过程的正确性。
EVP_CIPHER_CTX_new()初始化加密上下文
EVP_CIPHER_CTX_new 是 OpenSSL 库中的一个函数,用于创建一个新的加密上下文(EVP_CIPHER_CTX)。这个上下文用于存储加密或解密操作的状态,包括算法特定的信息、密钥、初始化向量(IV)、加密模式等。
函数原型如下:
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
这个函数不接受任何参数,并返回一个指向新创建的 EVP_CIPHER_CTX 结构的指针。如果内存分配失败,将返回 NULL。
EVP_EncryptInit_ex加密初始化
EVP_EncryptInit_ex 是 OpenSSL 库中的一个函数,用于初始化加密操作。这个函数是加密过程中的第一步,它准备加密上下文(EVP_CIPHER_CTX)以便后续的加密操作。这个函数非常灵活,可以用于多种不同的加密算法和模式。
函数原型如下:
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv);
参数说明:
ctx:指向EVP_CIPHER_CTX结构的指针,这个结构用于存储加密过程中的状态信息。cipher:指向EVP_CIPHER结构的指针,这个结构指定了要使用的加密算法和模式。impl:指向ENGINE结构的指针,用于指定加密操作可能使用的硬件加速引擎。如果为NULL,则使用默认的软件实现。key:指向密钥的指针。密钥的长度和格式取决于所选的加密算法。iv:指向初始化向量的指针。初始化向量用于某些加密模式(如 CBC、CTR 等),以确保即使相同的明文也不会产生相同的密文。如果加密模式不需要 IV,这个参数可以为NULL。
返回值:
- 成功时返回
1。 - 失败时返回
0,并可通过ERR_get_error函数获取错误信息。
EVP_EncryptUpdate执行加密操作
EVP_EncryptUpdate 是 OpenSSL 库中的一个函数,用于执行加密操作的中间步骤。这个函数用于处理要加密的数据块,通常在初始化加密操作(使用 EVP_EncryptInit_ex)之后和完成加密操作(使用 EVP_EncryptFinal_ex)之前调用。
函数原型如下:
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
参数说明:
ctx:指向之前通过EVP_EncryptInit_ex初始化的EVP_CIPHER_CTX结构的指针。out:指向用于存储加密数据的缓冲区的指针。加密的数据将被写入这个缓冲区。outl:一个整数指针,用于在输入时指定out缓冲区的大小,并在输出时存储实际写入out缓冲区的数据大小。in:指向要加密的数据的指针。inl:指定in指向的数据的大小(以字节为单位)。
返回值:
- 成功时返回
1。 - 失败时返回
0,并可通过ERR_get_error函数获取错误信息。
EVP_EncryptFinal_ex完成剩余加密操作
EVP_EncryptFinal_ex 是 OpenSSL 库中的一个函数,它用于完成加密操作。在调用 EVP_EncryptUpdate 处理完所有要加密的数据后,你会调用这个函数来完成加密过程,它会处理任何剩余的数据块并确保加密过程正确结束。
函数原型如下:
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
参数说明:
ctx:指向之前通过EVP_EncryptInit_ex和EVP_EncryptUpdate进行初始化和更新的EVP_CIPHER_CTX结构的指针。out:指向用于存储最后一部分加密数据的缓冲区的指针。如果加密算法在最后需要填充(如块加密模式),填充的数据也会被写入这个缓冲区。outl:一个整数指针,用于在输入时指定out缓冲区的大小,并在输出时存储实际写入out缓冲区的数据大小。
返回值:
- 成功时返回
1。 - 失败时返回
0,并可通过ERR_get_error函数获取错误信息。
EVP_CIPHER_CTX_free释放初始化上下文
EVP_CIPHER_CTX_free 是 OpenSSL 库中的一个函数,用于释放之前通过 EVP_CIPHER_CTX_new 创建的加密上下文(EVP_CIPHER_CTX)所占用的内存资源。这个函数是管理 OpenSSL 加密操作中内存使用的重要部分,确保在完成加密或解密操作后,及时释放分配的内存,避免内存泄漏。
函数原型如下:
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx);
参数说明:
ctx:指向EVP_CIPHER_CTX结构的指针。这个结构是在之前通过EVP_CIPHER_CTX_new创建的,用于存储加密或解密操作的状态和参数。
解密
解密与加密差不多
// 初始化解密上下文if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();// 初始化解密操作if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv)) handleErrors();// 执行解密操作if (1 != EVP_DecryptUpdate(ctx, decryptedtext, &len, ciphertext, sizeof(ciphertext))) handleErrors();EVP_DecryptFinal_ex(ctx, decryptedtext + len, &len);// 清理解密上下文EVP_CIPHER_CTX_free(ctx);
错误处理函数
您提供的代码是一个简单的错误处理函数,通常用于 OpenSSL 或其他需要处理错误堆栈的库。这个函数的作用是打印错误信息到标准错误输出(stderr),然后终止程序。下面是对这段代码的详细解释:
-
ERR_print_errors_fp(stderr);- 这行代码调用
ERR_print_errors_fp函数,该函数是 OpenSSL 库中的一个函数,用于打印错误信息。 stderr是标准错误输出流,它用于输出错误信息和调试信息。- 这个函数会打印出所有未被处理的错误到
stderr。
- 这行代码调用
-
abort();- 这行代码调用
abort函数,该函数是 C 标准库中的一个函数,用于立即终止程序。 - 当
abort被调用时,它会生成一个SIGABRT信号,如果没有捕获该信号的信号处理函数,程序将被终止。
- 这行代码调用
这个错误处理函数通常在 OpenSSL 函数调用失败时调用,以确保程序在遇到错误时能够优雅地终止,并提供足够的错误信息供调试使用。
相关文章:
openssl-AES-128-CTR加解密char型数组分析
本文章通过对一个unsigned char*类型的数据做简单的加解密操作来学习如何使用openssl库函数。 openssl为3.0.0,对此前版本的很多函数都不兼容。 加解密源码 #include <openssl/evp.h> #include <openssl/err.h> #include <string.h> #include …...
自动化生成与更新 Changelog 文件
在软件开发中,保持 Changelog 文件的更新是一项至关重要的任务。 Changelog 文件记录了项目的每一个重要变更,包括新功能、修复的问题以及任何可能破坏现有功能的变更。对于维护者、贡献者和最终用户来说,这都是一个宝贵的资源。然而&#x…...
(六)WebAPI方法的调用
1.WebAPI中定义的GET、POST方法 [HttpGet(Name "GetWeatherForecast")]public IEnumerable<WeatherForecast> Get(){return Enumerable.Range(1, 5).Select(index > new WeatherForecast{Date DateTime.Now.AddDays(index),TemperatureC Random.Shared.N…...
运维工程师面试整理-故障排查常见故障的排查步骤及方法
故障排查是运维工程师的重要技能之一。在面试中,面试官通常会通过故障排查相关的问题来评估你解决问题的能力和系统思维。以下是关于常见故障的排查步骤及方法的详细内容,帮助你更好地准备面试。 1. 故障排查的基本步骤 1. 问题识别 a. 描述问题:明确问题的具体表现...
OpenAI o1解决了「Quiet-STaR」的挑战吗?
随着OpenAI o1近期的发布,业界讨论o1关联论文最多之一可能是早前这篇斯坦福大学和Notbad AI Inc的研究人员开发的Quiet-STaR,即让AI学会先安静的“思考”再“说话” ,回想自己一年前对于这一领域的思考和探索,当初也将这篇论文进行…...
PDF产品册营销推广利器FLBOOK
在互联网高速发展的时代,营销推广已成为企业拓展市场的重要手段。而一款优秀的营销工具,可以为企业带来事半功倍的推广效果。今天,就为大家介绍一款集创意与实用于一体的PDF产品册营销推广利器——FLBOOK,帮助企业轻松提升品牌影响…...
华为OD机试 - 字符串划分(Python/JS/C/C++ 2024 E卷 100分)
华为OD机试 2024E卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试真题(Python/JS/C/C)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,…...
nginx和php-fpm连接超时的相关配置以及Nginx中的try_files以及root、alias的使用
一、nginx和php-fpm连接超时的相关配置 线上的PHP服务器架构大都是nginx proxy->nginx web->php-fpm。在服务器运行正常,服务器之间的连接正常,未被防火墙阻止的情况下,对这种架构排查504报错时需要注意以下几个地方的参数。 1是nginx…...
在MAC中Ollama开放其他电脑访问
ollama安装完毕后默认只能在本地访问,之前我都是安装其他的软件之后可以结合开放其他端口访问,其实是可以新增或修改下电脑的系统配置,就可以打开端口允许除本机IP或localhost访问。 步骤如下: 1、查看端口(默认是&…...
NE555芯片制作的节拍器
NE555芯片的节拍器,以一定的频率发出声音和闪烁灯光,起到节拍指示的作用。...
如何使用 Next.js 进行服务端渲染(Server-Side Rendering, SSR)
文章目录 前言步骤 1: 创建 Next.js 应用步骤 2: 创建页面组件示例页面组件 步骤 3: 自定义 _app.js 文件步骤 4: 自定义 _document.js 文件步骤 5: 运行应用步骤 6: 构建和部署总结 前言 Next.js 本身就支持 SSR 并提供了一系列内置的方法来简化这个过程。下面将详细介绍如何使…...
【machine learning-八-可视化loss funciton】
可视化lossfunction loss funciton可视化损失函数等高图 loss funciton 上一节讲过损失函数,也就是代价函数,它是衡量模型训练好坏的指标,对于线性回归来说,模型、参数、损失函数以及目标如下:、 损失函数的目标当然…...
Android 将EasyPermissions进一步封装,使得动态权限申请更加简明
1.引入依赖: implementation pub.devrel:easypermissions:3.0.0 2.在BaseActivity处理统一的结果回调和请求Code 核心内容: (1)处理Activity本身继承的方法onRequestPermissionsResult (2)实现接口EasyPermissions.PermissionCallbacks来接收请求结果 (3)定义申请权…...
我的AI工具箱Tauri版-VideoReapeat视频解说复述克隆
本教程基于自研的AI工具箱Tauri版进行VideoReapeat视频解说复述克隆。 VideoReapeat视频解说复述克隆 是自研的AI工具箱Tauri版中的一款专用模块,旨在通过AI技术对视频解说内容进行复述和克隆。该工具可自动洗稿并重新生成视频解说,通过简单配置即可对大…...
MySQL5.7.42高可用MHA搭建及故障切换演示
系列文章目录 rpmbuild构建mysql5.7RPM安装包 MySQL基于GTID同步模式搭建主从复制 文章目录 系列文章目录前言一、MHA架构介绍1.MHA的功能2.MHA组成3.MHA故障转移过程4.MHA架构优缺点 二、环境准备1.服务器免密2.基于GTID主从复制搭建3.下载mha组件 三、MHA组件安装1.安装依赖…...
快速搭建最简单的前端项目vue+View UI Plus
1 引言 Vue是一套用于构建Web前端界面的渐进式JavaScript框架。它以其易学易用、性能出色、灵活多变而深受开发者喜爱,并且与其他前端框架(如React和Angular)相比,在国内市场上受到了广泛的认可和使用。点击进入官方…...
倍增练习(1)
A - ST 表 && RMQ 问题 题目思路:st表的板子题用于静态区间求最值,通过倍增的思想,先通过预处理将各个区间的最大值通过转移式求出f[i][j] max(f[i][j - 1], f[i (1 << (j - 1))][j - 1]);然后再进行重叠查询查询,k log2(r - l 1);,max(f[l][k], f[r - (1 &l…...
MATLAB 在数学建模中的深入应用:从基础到高级实践
目录 前言 一、MATLAB基础知识 1.1 MATLAB工作环境简介 1.1.1 命令窗口(Command Window) 1.1.2 工作区(Workspace) 1.1.3 命令历史(Command History) 1.1.4 编辑器(Editor) 1…...
Unity 设计模式 之 【什么是设计模式】/ 【为什么要使用设计模式】/ 【架构和设计模式的区别】
Unity 设计模式 之 【什么是设计模式】/ 【为什么要使用设计模式】/ 【架构和设计模式的区别】 目录 Unity 设计模式 之 【什么是设计模式】/ 【为什么要使用设计模式】/ 【架构和设计模式的区别】 一、简单介绍 二、 Unity 设计模式 1、Unity 开发中使用设计模式的特点 2…...
[数据集][目标检测]智慧交通铁路异物入侵检测数据集VOC+YOLO格式802张7类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):802 标注数量(xml文件个数):802 标注数量(txt文件个数):802 标注类别…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
