源码
#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;
}
错误处理函数
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的长度数据就会出现数据越界。
#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
};
这种情况我们只能尽可能的多开辟空间。
unsigned char buffer[sizeof(config)];
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));
}
序列化步骤
-
计算长度:
id_len
和pwd_len
分别计算data->id
和data->pwd
的长度,包括字符串末尾的空字符\0
。
-
复制长度:
- 使用
memcpy
函数将id_len
和pwd_len
的值复制到buffer
的开始位置。memcpy
函数的第一个参数是目标地址,第二个参数是源地址,第三个参数是要复制的字节数。
- 使用
-
复制字符串:
- 紧接着长度信息之后,使用
memcpy
将data->id
和data->pwd
的内容复制到buffer
中。字符串的复制包括了字符串的长度和空字符。
- 紧接着长度信息之后,使用
-
复制数值:
- 将
data->age
和data->number
的值复制到buffer
中,它们紧跟在字符串之后。这些是整数值,所以直接使用memcpy
复制它们的内存表示。
- 将
反序列化函数
这段代码是 deserialize
函数的实现,它的作用是将序列化后的数据(存储在 buffer
中)反序列化回 info
结构体。这个过程是 serialize
函数的逆过程,用于从字节流中恢复原始数据结构。以下是对这段代码的详细解释:
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));
}
函数定义
void deserialize(unsigned char *buffer, info *data) {
size_t id_len, pwd_len;
buffer
:指向包含序列化数据的字节缓冲区的指针。data
:指向info
结构体的指针,该结构体将存储反序列化的数据。
读取并存储长度信息
memcpy(&id_len, buffer, sizeof(size_t));
memcpy(&pwd_len, buffer + sizeof(size_t) + id_len, sizeof(size_t));
- 使用
memcpy
函数从buffer
中复制size_t
大小的数据到id_len
和pwd_len
变量中。这些变量存储了id
和pwd
字段的长度,包括字符串的空字符。
读取并存储字符串数据
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
- 再次使用
memcpy
将id
和pwd
的数据从buffer
复制到data
结构体中。注意,字符串数据紧跟在它们的长度信息之后。 - 为了确保字符串正确终止,将最后一个字符设置为 null 字符(
'\0'
)。这是必要的,因为 C 字符串以 null 字符结束。
读取并存储数值数据
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));
- 使用
memcpy
将age
和number
的数据从buffer
复制到data
结构体中。这些数据紧跟在pwd
字符串之后。
加密函数
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);
解密函数
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);