LV025-AES实例
一、OpenSSL 简介
AES 这种加密算法在网上都是有多种开源库的,C 语言版本、Java 版本等等。我使用的是嵌入式平台,所以这里学习一下 C 语言平台 AES 加解密实现。
1. OpenSSL 是什么
首先来了解一下 OpenSSL,在 计算机网络 上,OpenSSL 是一个 开放原始码 的 软体 函式库 套件,应用程式可以使用这个套件来进行安全通讯,避免窃听,同时确认另一端连线者的身份。这个套件广泛被应用在网际网路的网页伺服器上。其主要 函式库 是以 C 语言 所写成,实作了基本的 加密 功能,实作了 SSL 与 TLS 协定。OpenSSL 可以运行在 OpenVMS、 Microsoft Windows 以及绝大多数 类 Unix 作业系统上(包括 Solaris,Linux,Mac OS X 与各种版本的开放原始码 BSD 作业系统)。
OpenSSL 计划在 1998 年开始,其目标是发明一套自由的加密工具,在网际网路上使用。OpenSSL 以 Eric Young 以及 Tim Hudson 两人开发的 SSLeay 为基础,随著两人前往 RSA 公司 任职,SSLeay 在 1998 年 12 月停止开发。因此在 1998 年 12 月,社群另外分支出 OpenSSL,继续开发下去。
2. 支持哪些算法?
OpenSSL 支持许多不同的加密算法:
AES、Blowfish、Camellia、Chacha20、Poly1305、SEED(英语:SEED)、CAST-128(英语:CAST-128)、DES、IDEA、RC2(英语:RC2)、RC4、RC5、TDES、GOST 28147-89(英语:GOST (block cipher))、SM4
MD5、MD4、MD2、SHA-1、SHA-2、SHA-3、RIPEMD-160、MDC-2(英语:MDC-2)、GOST R 34.11-94(英语:GOST (hash function))、BLAKE2、Whirlpool、SM3
RSA、DSA、ECDSA、ECDHE、迪菲-赫尔曼密钥交换、椭圆曲线密码学、X25519、Ed25519(英语:EdDSA#Ed25519)、X448(英语:Curve448)、Ed448(英语:Curve448)、GOST R 34.10-2001、SM2
3. 源码在哪?
官网在这里:openssl.org。然后我们点开第一个 【OpenSSL Library】→【Resources】→【Github Repository】,可以找到源码仓库:openssl/openssl,仓库里面有多种加密的源码,我们所需要的 AES 算法源码在这里 openssl/crypto/aes
核心主要是这个 openssl/crypto/aes/aes_core.c 文件。还有一些 ecb、ofb 等命名的文件,这些都是不同的加密模式,我这里就用了最基本的 ecb 模式,也就是直接使用 aes_core.c 中的函数即可。
二、使用实例
这个 demo 主要是用来解密和加密 WAV 格式的音频文件。
1. 源码实现
1.1 main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "./aes_core.h"
#include "./wav.h"
#define USER_AES_KEY "sumu"
#define USER_AES_KEY_LEN (128)
#define AUDIO_FRAME_LEN (1280)
#define FILE_OFFSET (44)
typedef struct __user_aes_key
{
/* data */
unsigned char user_key[32];
unsigned int user_key_len;
}user_aes_key;
unsigned char audio_buff[AUDIO_FRAME_LEN * 1280] = {0};
user_aes_key aes_key_info = {0};
WAVE_HEADER wav_header = {0};
extern char *optarg;
extern int optind, opterr, optopt;
int usage_info(void)
{
printf("\n========================================\n");
printf("help info:\n");
printf("./app -t aes_type -f file_name\n");
printf("\n");
printf("aes_type : 0, AES_DECRYPT; 1, AES_ENCRYPT\n");
printf("file_name: < 63 Bytes\n");
printf("========================================\n");
return 0;
}
int wav_info(WAVE_HEADER * head_info)
{
if(head_info == NULL)
{
printf("[error]wav head info is NULL\n");
return -1;
}
printf("===========================\n");
printf("WAV header information:\n");
printf("ChunkID \t%c%c%c%c\n", head_info->riff.ChunkID[0], head_info->riff.ChunkID[1], head_info->riff.ChunkID[2], head_info->riff.ChunkID[3]);
printf("ChunkSize \t%d\n", head_info->riff.ChunkSize);
printf("Format \t\t%c%c%c%c\n", head_info->riff.Format[0], head_info->riff.Format[1], head_info->riff.Format[2], head_info->riff.Format[3]);
printf("\n");
printf("Subchunk1ID \t%c%c%c%c\n", head_info->fmt.Subchunk1ID[0], head_info->fmt.Subchunk1ID[1], head_info->fmt.Subchunk1ID[2], head_info->fmt.Subchunk1ID[3]);
printf("Subchunk1Size \t%d\n", head_info->fmt.Subchunk1Size);
printf("AudioFormat \t%d\n", head_info->fmt.AudioFormat);
printf("NumChannels \t%d\n", head_info->fmt.NumChannels);
printf("SampleRate \t%d\n", head_info->fmt.SampleRate);
printf("ByteRate \t%d\n", head_info->fmt.ByteRate);
printf("BlockAlign \t%d\n", head_info->fmt.BlockAlign);
printf("BitsPerSample \t%d\n", head_info->fmt.BitsPerSample);
printf("\n");
printf("blockID \t%c%c%c%c\n", head_info->data.Subchunk2ID[0], head_info->data.Subchunk2ID[1], head_info->data.Subchunk2ID[2], head_info->data.Subchunk2ID[3]);
printf("blockSize \t%d\n", head_info->data.Subchunk2Size);
printf("\n");
//duration = Subchunk2Size / ByteRate
printf("duration \t%d\n", head_info->data.Subchunk2Size / head_info->fmt.ByteRate);
printf("===========================\n");
return 0;
}
int main(int argc, char *argv[])
{
/* code */
int ret = 0;
int i = 0;
FILE *fp_src = NULL;
FILE *fp_dst = NULL;
unsigned int src_file_size = 0;
char file_name[64] = {0};
int aes_type = -1;
printf("\n***[%s][%d] Build Time : [%s %s], Git version:%s remote: %s ***\n\n",
__func__, __LINE__,__DATE__, __TIME__, GIT_VERSION, GIT_PATH);
// 参数数量校验
if(argc < 2)
{
ret = usage_info();
if(ret != 0)
{
printf("[error]usage_info fail!ret=%d\n", ret);
}
return -1;
}
// 打印main函数参数
printf("argc=%d,argv[]=", argc);
for(i = 0; i < argc; i++)
{
printf("%s ", argv[i]);
}
printf("\n");
// 参数解析
int opt = 0;
int opt_arg = 0;
char *opt_string = NULL;
while ((opt = getopt(argc, argv, "t:f:")) != -1)
{
switch (opt)
{
case 't':
opt_arg = atoi(optarg);
break;
case 'f':
opt_string = optarg;
break;
default: /* '?' */
usage_info();
exit(EXIT_FAILURE);
}
}
//参数赋值
switch(opt_arg)
{
case 0:
aes_type = AES_DECRYPT;
break;
case 1:
aes_type = AES_ENCRYPT;
break;
default:
printf("[error]not support!\n");
exit(EXIT_FAILURE);
}
if(opt_string == NULL || strlen(opt_string) > 63)
{
printf("[error]file name is NULL or too long(> 63Byte))!\n");
exit(EXIT_FAILURE);
}
// 设置aes解密密钥
snprintf((char *)aes_key_info.user_key, sizeof(aes_key_info.user_key), "%s", USER_AES_KEY);
aes_key_info.user_key_len = USER_AES_KEY_LEN;
printf("aes_key_info user_key=%s len=%d,aes_type=%d file_name=%s\n",
aes_key_info.user_key, aes_key_info.user_key_len,
aes_type, opt_string);
printf("get aes key char is:");
for (i = 0; i < sizeof(aes_key_info.user_key); i++)
{
/* code */
if(aes_key_info.user_key[i] != '\0')
{
printf("%c-0x%x ", aes_key_info.user_key[i], aes_key_info.user_key[i]);
}
}
printf("\n");
func_aes_secret_key_config(aes_key_info.user_key, aes_key_info.user_key_len, aes_type);
// 打开加密文件
snprintf(file_name, sizeof(file_name), "%s", opt_string);
fp_src = fopen(file_name, "ab+");
if(fp_src == NULL)
{
printf("%s open failed!\n", file_name);
return -1;
}
fseek(fp_src, 0, SEEK_END);
src_file_size = ftell(fp_src);
fseek(fp_src, 0, SEEK_SET);
ret = fread(&wav_header, 1, sizeof(wav_header), fp_src);
if(ret < sizeof(WAVE_HEADER))
{
printf("[error]%s wav header read fail!ret=%d\n", file_name, ret);
return -1;
}
printf("file %s size is %d, now fp pointer is in %ld\n", file_name, src_file_size, ftell(fp_src));
ret = wav_info(&wav_header);
if(ret < 0)
{
printf("[error]%s wav header read fail!ret=%d\n", file_name, ret);
return -1;
}
char *dst_file_type = (aes_type == AES_DECRYPT)?"decrypt":"encrypt";
snprintf(file_name, sizeof(file_name), "%s_%s", dst_file_type, opt_string);
fp_dst = fopen(file_name, "w");
if(fp_dst == NULL)
{
printf("%s open failed!\n", file_name);
return -1;
}
fwrite(&wav_header, 1, sizeof(WAVE_HEADER), fp_dst);
while (1)
{
/* code */
ret = fread(audio_buff, 1, AUDIO_FRAME_LEN, fp_src);
if(ret < AUDIO_FRAME_LEN)
{
printf("audio frame read end!ret=%d\n", ret);
break;
}
// 加密或者解密
switch(aes_type)
{
case AES_DECRYPT:
func_aes_decrypt_pack(NULL, audio_buff, AUDIO_FRAME_LEN);
break;
case AES_ENCRYPT:
func_aes_encrypt_pack(NULL, audio_buff, AUDIO_FRAME_LEN);
break;
default:
printf("[error]not support!\n");
break;
}
fwrite(audio_buff, 1, AUDIO_FRAME_LEN, fp_dst);
}
fclose(fp_src);
fclose(fp_dst);
return 0;
}1.2 wav.h
#ifndef __WAV_H__
#define __WAV_H__
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long int uint64_t;
typedef struct WAV_RIFF {
/* chunk "riff" */
char ChunkID[4]; /* "RIFF" 标志*/
/* sub-chunk-size */
uint32_t ChunkSize; /* 36 + Subchunk2Size */
/* sub-chunk-data */
char Format[4]; /* "WAVE" */
} RIFF_t;
typedef struct WAV_FMT {
/* sub-chunk "fmt" */
char Subchunk1ID[4]; /* "fmt " */
/* sub-chunk-size */
uint32_t Subchunk1Size; /* 16 for PCM */
/* sub-chunk-data */
uint16_t AudioFormat; /* PCM = 1*/
uint16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
uint32_t SampleRate; /* 8000, 44100, etc. */
uint32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
uint16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
} FMT_t;
typedef struct WAV_data {
/* sub-chunk "data" */
char Subchunk2ID[4]; /* "data" */
/* sub-chunk-size */
uint32_t Subchunk2Size; /* data size */
/* sub-chunk-data */
// Data_block_t block;
} Data_t;
//typedef struct WAV_data_block {
//} Data_block_t;
typedef struct WAV_fotmat {
RIFF_t riff;
FMT_t fmt;
Data_t data;
} WAVE_HEADER;
#endif /* __WAV_H__ */1.3 aes_core.c
主要参考 openssl/crypto/aes/aes_core.c,但是里面有些东西我们是不需要的需要注释掉。另外还需要添加一些函数,具体的可以看之前写的实例:crypto/aes/aes_core.c · 苏木/openssl
1.4 aes_core.h
#ifndef __AES_CORE_H
#define __AES_CORE_H
//#define OPENSSL_AES_CONST_TIME
//#define AES_ASM
//数据类型定义
typedef unsigned char u8; /**< UNSIGNED 8-bit data type */
typedef unsigned short u16; /**< UNSIGNED 16-bit data type */
typedef unsigned int u32; /**< UNSIGNED 32-bit data type */
typedef unsigned long long u64; /**< UNSIGNED 64-bit data type */
typedef signed char s8; /**< SIGNED 8-bit data type */
typedef signed short s16; /**< SIGNED 16-bit data type */
typedef signed int s32; /**< SIGNED 32-bit data type */
typedef signed long long s64; /**< SIGNED 64-bit data type */
#define AES_BLOCK_SIZE (16)
#define AES_ENCRYPT (1)
#define AES_DECRYPT (0)
#define AES_MAXNR (14)
#define OK (0)
#undef GETU32
#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
/* This should be a hidden type, but EVP requires that the size be known */
struct aes_key_st {
#ifdef AES_LONG
unsigned long rd_key[4 * (AES_MAXNR + 1)];
#else
unsigned int rd_key[4 * (AES_MAXNR + 1)];
#endif
int rounds;
};
typedef struct aes_key_st AES_KEY;
int func_aes_secret_key_config(unsigned char *userKey, int length, int flg);
int func_aes_encrypt_pack(unsigned char *expkey, unsigned char *encrypt_data, int size);
int func_aes_decrypt_pack(unsigned char *expkey, unsigned char *encrypt_data, int size);
#endif1.5 Makefile
TARGET ?= aes_128
TEST_DATA_PATH = ~/1sharedfiles/test/aes/
OBJ = main.o aes_core.o
INCDIRS += ./
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
CC = gcc
CFLAGS += -g -Wall
#GIT_SHA = $(shell git rev-list HEAD | awk 'NR==1')
GIT_SHA = $(shell git rev-parse --short HEAD | awk 'NR==1')
GIT_SEQ = $(shell git rev-list HEAD | wc -l)
GIT_VER_INFO = $(GIT_SHA)-$(GIT_SEQ)
GIT_SVR_PATH = $(shell git remote -v | awk 'NR==1' | sed 's/[()]//g' | sed 's/\t/ /g' |cut -d " " -f2)
ifneq ($(GIT_VER_INFO),)
CFLAGS += -DGIT_VERSION=\"$(GIT_VER_INFO)\"
else
CFLAGS += -DGIT_VERSION=\"unknown\"
endif
ifneq ($(GIT_SVR_PATH),)
CFLAGS += -DGIT_PATH=\"$(GIT_SVR_PATH)\"
else
CFLAGS += -DGIT_PATH=\"unknown\"
endif
$(TARGET): ${OBJ}
$(CC) $(CFLAGS) $(OBJ) -o $(TARGET)
$(OBJ):%.o:%.c
$(CC) $(CFLAGS) -c $(INCLUDE) $< -o $@
.PHONY: clean
clean:
rm -rf *.o $(TARGET)
cpy:
@cp -v $(TARGET) $(TEST_DATA_PATH)2. 编译运行
不再赘述。