openssl-AES-128-CTR加解密char型数组分析

news/2024/9/20 8:45:23/ 标签: linux, c语言, ssl

本文章通过对一个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 程序。下面是代码的详细解析:

  1. 包含头文件

    • <openssl/evp.h>:提供加密算法的封装。
    • <openssl/err.h>:提供错误处理功能。
    • <openssl/rand.h>:提供随机数生成功能。
    • <openssl/aes.h>:提供 AES 加密算法的函数。
    • <stdlib.h>:提供标准库函数,如 abort()
  2. 错误处理函数

    • handleErrors:当遇到错误时,打印错误信息并终止程序。
  3. 主函数 main

    • ERR_load_crypto_strings:加载错误字符串,以便 ERR_print_errors_fp 函数可以打印出可读的错误信息。
    • OpenSSL_add_all_algorithms:注册所有 OpenSSL 加密算法。
  4. 密钥和初始化向量

    • unsigned char key[AES_BLOCK_SIZE]:定义一个 128 位(16 字节)的密钥数组。
    • unsigned char iv[AES_BLOCK_SIZE]:定义一个 128 位(16 字节)的初始化向量(IV)数组。
  5. 生成密钥和 IV

    • RAND_bytes(key, sizeof(key)):生成随机密钥。
    • RAND_bytes(iv, sizeof(iv)):生成随机 IV。
  6. 定义数据

    • unsigned char plaintext[]:定义要加密的明文数据。
    • unsigned char ciphertext[sizeof(plaintext)]:定义存储加密后数据的数组。
    • unsigned char decryptedtext[sizeof(plaintext)]:定义存储解密后数据的数组。
  7. 加密和解密上下文

    • EVP_CIPHER_CTX *ctx:定义一个加密上下文指针,用于存储加密和解密过程中的状态信息。
  8. 加密过程

    • EVP_CIPHER_CTX_new:创建一个新的加密上下文。
    • EVP_EncryptInit_ex:初始化加密操作,设置加密算法为 AES-128-CTR,使用密钥和 IV。
    • EVP_EncryptUpdate:执行加密操作,将明文数据加密到 ciphertext 数组中。
    • EVP_EncryptFinal_ex:完成加密操作,处理任何剩余的数据。
  9. 解密过程

    • 与加密过程类似,但使用 EVP_DecryptInit_exEVP_DecryptUpdateEVP_DecryptFinal_ex 函数进行解密。
  10. 打印解密后的数据

    • printf:打印解密后的文本。
  11. 清理 OpenSSL

    • ERR_free_strings:释放加载的错误字符串。
  12. 返回

    • 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_exEVP_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),然后终止程序。下面是对这段代码的详细解释:

  1. ERR_print_errors_fp(stderr);

    • 这行代码调用 ERR_print_errors_fp 函数,该函数是 OpenSSL 库中的一个函数,用于打印错误信息。
    • stderr 是标准错误输出流,它用于输出错误信息和调试信息。
    • 这个函数会打印出所有未被处理的错误到 stderr
  2. abort();

    • 这行代码调用 abort 函数,该函数是 C 标准库中的一个函数,用于立即终止程序。
    • abort 被调用时,它会生成一个 SIGABRT 信号,如果没有捕获该信号的信号处理函数,程序将被终止。

这个错误处理函数通常在 OpenSSL 函数调用失败时调用,以确保程序在遇到错误时能够优雅地终止,并提供足够的错误信息供调试使用。


http://www.ppmy.cn/news/1527942.html

相关文章

职场人生-外企福利待遇

今天来给大家分享一下外企的待遇&#xff0c;虽然外服享受不到这些待遇&#xff0c;但是也可以跟大家分享分享 1.四季福利.外企一般在春夏秋冬都会发印有公司logo的衣服&#xff0c;春天的外套&#xff0c;夏天的短袖&#xff0c;秋天的长袖&#xff0c;冬天的棉袄&#xff0c…

【HTML】入门教程

HTML入门 HTML网页主流浏览器及其内核W3C标准HTML结构简单示例meta HTML标签语法规则标签分类标签之间的关系标签属性通用属性&#xff1a; 常用标签及其属性应用示例 HTML5语义化标签多媒体标签视频标签 video音频标签 audio 新增的表单元素 参考文档 HTML HTML 指的是超文本…

使用llama.cpp 在推理MiniCPM-1.2B模型

llama.cpp 是一个开源项目&#xff0c;它允许用户在C中实现与LLaMA&#xff08;Large Language Model Meta AI&#xff09;模型的交互。LLaMA模型是由Meta Platforms开发的一种大型语言模型&#xff0c;虽然llama.cpp本身并不包含LLaMA模型的训练代码或模型权重&#xff0c;但它…

GPU 云与 GenAI :DigitalOcean 在 AI 平台与应用方向的技术规划

在 DigitalOcean&#xff0c;我们不仅在观察人工智能革命&#xff0c;而且还在积极参与这场技术革命。 去年&#xff0c;我们进行了一项关键的收购以扩展平台的人工智能能力&#xff0c;扩大了对曾经仅限于大型企业的 AI/ML 开发工具的访问。在2024年7月由 DigitalOcean 主办的…

道路裂缝,坑洼,病害数据集-包括无人机视角,摩托车视角,车辆视角覆盖道路

道路裂缝&#xff0c;坑洼&#xff0c;病害数据集 包括无人机视角&#xff0c;摩托车视角&#xff0c;车辆视角 覆盖道路所有问题 一共有八类16000张 1到7依次为: [横向裂缝, 纵向裂缝, 块状裂缝, 龟裂, 坑槽, 修补网状裂缝, 修补裂缝, 修补坑槽] 道路病害&#xff08;如裂缝、…

问:说说notify()和notifyAll()有什么区别?

notify() 和 notifyAll() 是 Java 中用于线程间通信的方法&#xff0c;这两个方法都用于唤醒正在等待 (wait()) 的线程。然而&#xff0c;它们在工作方式和应用场景上有一些重要的区别。 notify() vs notifyAll() notify(): 唤醒一个正在等待 (wait()) 的线程。如果有多个线程…

演示:基于WPF自绘的中国省份、城市、区县矢量地图

一、目的&#xff1a;演示一个基于WPF自绘的中国省份、城市、区县矢量地图 二、效果 国 省 市 三、功能 支持实际经纬度显示 支持平移&#xff0c;缩放等功能 显示中国地图 显示各个省份地图 显示各个省份地图&#xff08;包含在表格中&#xff0c;包含缩率图&#xff09; 显…

基于单片机的远程无线控制系统设计

摘 要 &#xff1a; 主要介绍了一种以单片机 AT89C2051 、 无线模块 APC200A-43 和继电器为核心的远程智能控制系统。 该系统通过对单片机功能的研究 &#xff0c; 使用单片机的输入输出口和中断实现对控制信号的处理&#xff0c; 通过调试无线通讯模块 &#xff0c; 控制…

桥接网络设置多用户lxd容器

文章目录 前言配置宿主机网络固定内核版本安装 lxd、zfs 及 bridge-utils安装宿主机显卡驱动lxd 初始化创建容器模板安装容器显卡驱动复制容器 前言 使用桥接网络配置 lxd 有个好处&#xff0c;就是每个用户都可以在该局域网下有一个自己独立的 IP&#xff0c;该 IP 的端口可以…

玩转RabbitMQ声明队列交换机、消息转换器

♥️作者&#xff1a;小宋1021 &#x1f935;‍♂️个人主页&#xff1a;小宋1021主页 ♥️坚持分析平时学习到的项目以及学习到的软件开发知识&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…

【自动驾驶】控制算法(九)深度解析车辆纵向控制 | 从算法基础到 Carsim 仿真实践

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

Leetcode面试经典150题-79.搜索单词

题目比较简单&#xff0c;回溯最基础的题&#xff0c;记得除非覆盖&#xff0c;否则一定要恢复现场就行 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public boolean exist(char[][] board, String word) {int m board.length; int n board[0].length;i…

JavaScript如何判断输入的是空格

1、JavaScript如何判断输入的是空格 1.1. 使用trim()方法和length属性 trim() 方法可以移除字符串两端的空白字符&#xff08;包括空格、制表符、换行符等&#xff09;&#xff0c;然后检查处理后的字符串长度是否为0。 function isOnlySpaces(str) {return str.trim().lengt…

es由一个集群迁移到另外一个集群es的数据迁移

迁移es的数据 改下index的索引 就可以了。 查询 用curl -u就可以查询了

C# 中Faker

在 C# 中&#xff0c;Faker 类通常用于生成模拟数据&#xff08;也称为虚拟数据、测试数据&#xff09;&#xff0c;这对于开发、测试以及演示应用程序非常有用。一个流行的库叫做 Faker&#xff0c;它提供了一种简单的方式来生成各种随机数据。 安装 Faker 库 要使用 Faker …

VMware安装飞牛私有云fnOS并挂载小雅Alist实现异地远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java后端性能监控:使用JMX与Java Mission Control的深入解析

Java后端性能监控&#xff1a;使用JMX与Java Mission Control的深入解析 大家好&#xff0c;我是微赚淘客返利系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在Java后端开发中&#xff0c;性能监控是确保系统稳定和高效运行的关键。…

【Qt网络编程】Tcp多线程并发服务器和客户端通信

目录 一、编写思路 1、服务器 &#xff08;1&#xff09;总体思路widget.c&#xff08;主线程&#xff09; &#xff08;2&#xff09;详细流程widget.c&#xff08;主线程&#xff09; &#xff08;1&#xff09;总体思路chat_thread.c&#xff08;处理聊天逻辑线程&…

stable diffusion 神经网络插件 controlnet 的安装,很详细

stable diffusion 神经网络插件 controlnet 的安装&#xff0c;很详细 一、前言二、下载1、方式一2、方式二 一、前言 学到 stable diffusion 的 controlnet 插件&#xff0c;安装也略微曲折&#xff0c;这里做个记录。 下载前保证 github 能正常访问。 二、下载 1、方式一…

golang学习笔记30——golang 中代码仓库的 h1 和 go.mod h1 不一致的修正方法

推荐学习文档 golang应用级os框架&#xff0c;欢迎stargolang应用级os框架使用案例&#xff0c;欢迎star案例&#xff1a;基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识&#xff0c;这里有免费的golang学习笔…