openssl-AES-128-CTR加解密结构体

源码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

#define length 1024

typedef struct {
    char id[length];
    char pwd[length];
    int age;
    int number;
} info;

void handleErrors(void) {
    fprintf(stderr, "An error occurred.\n");
    //abort();
    exit(1);
}

// Serialize the structure into a byte array
void serialize(info *data, unsigned char *buffer) {
    size_t id_len = strlen(data->id) + 1;
    size_t pwd_len = strlen(data->pwd) + 1;
    memcpy(buffer, &id_len, sizeof(size_t));
    memcpy(buffer + sizeof(size_t), data->id, id_len);
    memcpy(buffer + sizeof(size_t) + id_len, &pwd_len, sizeof(size_t));
    memcpy(buffer + 2 * sizeof(size_t) + id_len, data->pwd, pwd_len);
    memcpy(buffer + 2 * sizeof(size_t) + id_len + pwd_len, &data->age, sizeof(data->age));
    memcpy(buffer + 2 * sizeof(size_t) + id_len + pwd_len + sizeof(data->age), &data->number, sizeof(data->number));
}

// Deserialize the byte array back into the structure
void deserialize(unsigned char *buffer, info *data) {
    size_t id_len, pwd_len;
    memcpy(&id_len, buffer, sizeof(size_t));
    memcpy(data->id, buffer + sizeof(size_t), id_len);
    data->id[id_len - 1] = '\0'; // Ensure null-termination

    memcpy(&pwd_len, buffer + sizeof(size_t) + id_len, sizeof(size_t));
    memcpy(data->pwd, buffer + 2 * sizeof(size_t) + id_len, pwd_len);
    data->pwd[pwd_len - 1] = '\0'; // Ensure null-termination

    memcpy(&data->age, buffer + 2 * sizeof(size_t) + id_len + pwd_len, sizeof(data->age));
    memcpy(&data->number, buffer + 2 * sizeof(size_t) + id_len + pwd_len + sizeof(data->age), sizeof(data->number));
}

int main() {
    info config = {
        .id = "ags",
        .pwd = "asg",
        .age = 25,
        .number = 12345
    };

    unsigned char key[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];
    unsigned char buffer[sizeof(config)];
    unsigned char enc_out[sizeof(config)];
    unsigned char dec_out[sizeof(config)];
    int len, enc_len;

    if (!RAND_bytes(key, sizeof(key)) || !RAND_bytes(iv, sizeof(iv))) {
        handleErrors();
    }

    // Serialize
    serialize(&config, buffer);

    // Encrypt
    EVP_CIPHER_CTX *ctx;
    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, enc_out, &enc_len, buffer, sizeof(config))) handleErrors();
    len = enc_len;
    if (1 != EVP_EncryptFinal_ex(ctx, enc_out + enc_len, &enc_len)) handleErrors();
    len += enc_len;
    EVP_CIPHER_CTX_free(ctx);

    // Write encrypted data to file
    FILE *fencrypt = fopen("configEncrypt.bin", "wb");
    if (!fencrypt) handleErrors();
    fwrite(enc_out, 1, len, fencrypt);
    fclose(fencrypt);

    // Decrypt
    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, dec_out, &enc_len, enc_out, len)) handleErrors();
    len = enc_len;
    if (1 != EVP_DecryptFinal_ex(ctx, dec_out + enc_len, &enc_len)) handleErrors();
    len += enc_len;
    EVP_CIPHER_CTX_free(ctx);

    // Deserialize
    info decrypted_config;
    deserialize(dec_out, &decrypted_config);

    // Write decrypted data to file
    FILE *fdecrypt = fopen("configDecrypt.txt", "w");
    if (!fdecrypt) handleErrors();
    fprintf(fdecrypt, "ID: %s\n", decrypted_config.id);
    fprintf(fdecrypt, "Password: %s\n", decrypted_config.pwd);
    fprintf(fdecrypt, "Age: %d\n", decrypted_config.age);
    fprintf(fdecrypt, "Number: %d\n", decrypted_config.number);
    fclose(fdecrypt);

    // Read and print decrypted data from file
    // FILE *fread = fopen("configDecrypt.txt", "r");
    // if (!fread) handleErrors();
    // char line[1024];
    // while (fgets(line, sizeof(line), fread)) {
    //     printf("%s", line);
    // }
    // fclose(fread);

    return 0;
}

错误处理函数

c 复制代码
void handleErrors(void) {
    fprintf(stderr, "An error occurred.\n");
    //abort();
    exit(1);
}

abort()和exit()都是stdlib库中的函数,这两个函数的功能都有很多相似之处,都是用于退出程序的函数,然而细细追查其中也是有着不小的差别的,以下是这两种方法的主要区别:

使用 exit(1);

  • exit 函数请求程序终止,并可以指定一个退出状态码,通常用于指示程序是正常结束还是由于错误而结束。
  • exit 被调用时,它会触发几个清理操作:
    • 刷新并关闭所有打开的文件流(stdio流)。
    • 调用所有注册的 atexit() 函数。
    • 释放分配的资源,如内存。
    • 终止进程。
  • 它提供了一种优雅的方式退出程序,允许程序在退出前进行必要的清理工作。

使用 abort();

  • abort 函数立即终止程序,并向操作系统报告一个异常终止信号(通常是 SIGABRT)。
  • abort 被调用时,它不会执行任何清理操作,如关闭文件流或调用 atexit() 注册的函数。
  • 它通常用于指示程序遇到了严重错误,无法正常退出。
  • abort 会导致操作系统生成一个核心转储文件(core dump),如果操作系统配置了核心转储,这可以用于后续的调试和分析。

区别总结

  • 清理操作exit 执行清理操作,而 abort 不会。
  • 退出状态exit 允许你指定一个退出状态码,而 abort 通常不提供这个功能。
  • 调试abort 可能会生成核心转储文件,有助于调试,而 exit 不会。
  • 使用场景exit 用于正常的程序退出或错误退出,而 abort 用于遇到严重错误,需要立即终止程序的情况。

序列化函数

序列化是将程序中的数据结构或对象状态转换成可以存储或传输的格式的过程。在C语言中,序列化通常意味着将结构体中的字段按一定顺序转换成字节流。在提供的代码中,serialize 函数将 info 结构体的内容序列化到一个字节缓冲区。以下是序列化过程的详细分析:

序列化函数

buffer只有config的大小,那么其实在序列化时却将ID的长度和pwd的长度数据也写入到了buffer,那么如果id和pwd刚好用完了自己的内存,没有多的给ID的长度和pwd的长度数据就会出现数据越界。

c 复制代码
#define length 4
typedef struct {
    char id[length];
    char pwd[length];
    int age;
    int number;
} info;
info config = {
        .id = "agsd",
        .pwd = "asgg",
        .age = 25,
        .number = 12345
    };

这种情况我们只能尽可能的多开辟空间。

c 复制代码
unsigned char buffer[sizeof(config)];
c 复制代码
void serialize(info *data, unsigned char *buffer) {
    size_t id_len = strlen(data->id) + 1; // 计算ID的长度,包括空字符'\0'
    size_t pwd_len = strlen(data->pwd) + 1; // 计算密码的长度,包括空字符'\0'

    // 将ID的长度复制到缓冲区
    memcpy(buffer, &id_len, sizeof(size_t));
    // 将ID复制到缓冲区,紧接着ID长度之后
    memcpy(buffer + sizeof(size_t), data->id, id_len);
    // 将密码的长度复制到缓冲区,紧接着ID之后
    memcpy(buffer + sizeof(size_t) + id_len, &pwd_len, sizeof(size_t));
    // 将密码复制到缓冲区,紧接着密码长度之后
    memcpy(buffer + 2 * sizeof(size_t) + id_len, data->pwd, pwd_len);
    // 将年龄复制到缓冲区,紧接着密码之后
    memcpy(buffer + 2 * sizeof(size_t) + id_len + pwd_len, &data->age, sizeof(data->age));
    // 将编号复制到缓冲区,紧接着年龄之后
    memcpy(buffer + 2 * sizeof(size_t) + id_len + pwd_len + sizeof(data->age), &data->number, sizeof(data->number));
}

序列化步骤

  1. 计算长度

    • id_lenpwd_len 分别计算 data->iddata->pwd 的长度,包括字符串末尾的空字符 \0
  2. 复制长度

    • 使用 memcpy 函数将 id_lenpwd_len 的值复制到 buffer 的开始位置。memcpy 函数的第一个参数是目标地址,第二个参数是源地址,第三个参数是要复制的字节数。
  3. 复制字符串

    • 紧接着长度信息之后,使用 memcpydata->iddata->pwd 的内容复制到 buffer 中。字符串的复制包括了字符串的长度和空字符。
  4. 复制数值

    • data->agedata->number 的值复制到 buffer 中,它们紧跟在字符串之后。这些是整数值,所以直接使用 memcpy 复制它们的内存表示。

反序列化函数

这段代码是 deserialize 函数的实现,它的作用是将序列化后的数据(存储在 buffer 中)反序列化回 info 结构体。这个过程是 serialize 函数的逆过程,用于从字节流中恢复原始数据结构。以下是对这段代码的详细解释:

c 复制代码
void deserialize(unsigned char *buffer, info *data) {
    size_t id_len, pwd_len;
    memcpy(&id_len, buffer, sizeof(size_t));
    memcpy(data->id, buffer + sizeof(size_t), id_len);
    data->id[id_len - 1] = '\0'; // Ensure null-termination

    memcpy(&pwd_len, buffer + sizeof(size_t) + id_len, sizeof(size_t));
    memcpy(data->pwd, buffer + 2 * sizeof(size_t) + id_len, pwd_len);
    data->pwd[pwd_len - 1] = '\0'; // Ensure null-termination

    memcpy(&data->age, buffer + 2 * sizeof(size_t) + id_len + pwd_len, sizeof(data->age));
    memcpy(&data->number, buffer + 2 * sizeof(size_t) + id_len + pwd_len + sizeof(data->age), sizeof(data->number));
}

函数定义

c 复制代码
void deserialize(unsigned char *buffer, info *data) {
    size_t id_len, pwd_len;
  • buffer:指向包含序列化数据的字节缓冲区的指针。
  • data:指向 info 结构体的指针,该结构体将存储反序列化的数据。

读取并存储长度信息

c 复制代码
    memcpy(&id_len, buffer, sizeof(size_t));
    memcpy(&pwd_len, buffer + sizeof(size_t) + id_len, sizeof(size_t));
  • 使用 memcpy 函数从 buffer 中复制 size_t 大小的数据到 id_lenpwd_len 变量中。这些变量存储了 idpwd 字段的长度,包括字符串的空字符。

读取并存储字符串数据

c 复制代码
    memcpy(data->id, buffer + sizeof(size_t), id_len);
    data->id[id_len - 1] = '\0'; // Ensure null-termination

    memcpy(data->pwd, buffer + 2 * sizeof(size_t) + id_len, pwd_len);
    data->pwd[pwd_len - 1] = '\0'; // Ensure null-termination
  • 再次使用 memcpyidpwd 的数据从 buffer 复制到 data 结构体中。注意,字符串数据紧跟在它们的长度信息之后。
  • 为了确保字符串正确终止,将最后一个字符设置为 null 字符('\0')。这是必要的,因为 C 字符串以 null 字符结束。

读取并存储数值数据

c 复制代码
    memcpy(&data->age, buffer + 2 * sizeof(size_t) + id_len + pwd_len, sizeof(data->age));
    memcpy(&data->number, buffer + 2 * sizeof(size_t) + id_len + pwd_len + sizeof(data->age), sizeof(data->number));
  • 使用 memcpyagenumber 的数据从 buffer 复制到 data 结构体中。这些数据紧跟在 pwd 字符串之后。

加密函数

c 复制代码
EVP_CIPHER_CTX *ctx;
    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, enc_out, &enc_len, buffer, sizeof(config))) handleErrors();
    len = enc_len;
    if (1 != EVP_EncryptFinal_ex(ctx, enc_out + enc_len, &enc_len)) handleErrors();
    len += enc_len;
    EVP_CIPHER_CTX_free(ctx);

解密函数

c 复制代码
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, dec_out, &enc_len, enc_out, len)) handleErrors();
    len = enc_len;
    if (1 != EVP_DecryptFinal_ex(ctx, dec_out + enc_len, &enc_len)) handleErrors();
    len += enc_len;
    EVP_CIPHER_CTX_free(ctx);
相关推荐
内核程序员kevin1 小时前
TCP Listen 队列详解与优化指南
linux·网络·tcp/ip
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
搬砖的小码农_Sky5 小时前
C语言:数组
c语言·数据结构
朝九晚五ฺ6 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
自由的dream6 小时前
Linux的桌面
linux
xiaozhiwise6 小时前
Makefile 之 自动化变量
linux
Swift社区6 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman7 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
先鱼鲨生7 小时前
数据结构——栈、队列
数据结构