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);
相关推荐
m0_748238273 分钟前
WebClient HTTP 请求问题处理模板(泛型响应、忽略 SSL 证书等)
网络协议·http·ssl
刚学HTML35 分钟前
leetcode 05 回文字符串
算法·leetcode
真真-真真42 分钟前
WebXR
linux·运维·服务器
Yan.love1 小时前
开发场景中Java 集合的最佳选择
java·数据结构·链表
୧⍢⃝୨ LonelyCoder1 小时前
FreePBX修改IP地址和端口以及添加SSL证书开启HTTPS访问
tcp/ip·https·ssl
AC使者1 小时前
#B1630. 数字走向4
算法
冠位观测者1 小时前
【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序
数据结构·算法·leetcode
轩辰~1 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
古希腊掌管学习的神2 小时前
[搜广推]王树森推荐系统笔记——曝光过滤 & Bloom Filter
算法·推荐算法
qystca2 小时前
洛谷 P1706 全排列问题 C语言
算法