Skip to content

LV025-AES实例

一、OpenSSL 简介

AES 这种加密算法在网上都是有多种开源库的,C 语言版本、Java 版本等等。我使用的是嵌入式平台,所以这里学习一下 C 语言平台 AES 加解密实现。

1. OpenSSL 是什么

首先来了解一下 OpenSSL,在 计算机网络 上,OpenSSL 是一个 开放原始码软体 函式库 套件,应用程式可以使用这个套件来进行安全通讯,避免窃听,同时确认另一端连线者的身份。这个套件广泛被应用在网际网路的网页伺服器上。其主要 函式库 是以 C 语言 所写成,实作了基本的 加密 功能,实作了 SSL 与 TLS 协定。OpenSSL 可以运行在 OpenVMSMicrosoft Windows 以及绝大多数 类 Unix 作业系统上(包括 SolarisLinuxMac OS X 与各种版本的开放原始码 BSD 作业系统)。

OpenSSL 计划在 1998 年开始,其目标是发明一套自由的加密工具,在网际网路上使用。OpenSSL 以 Eric Young 以及 Tim Hudson 两人开发的 SSLeay 为基础,随著两人前往 RSA 公司 任职,SSLeay 在 1998 年 12 月停止开发。因此在 1998 年 12 月,社群另外分支出 OpenSSL,继续开发下去。

2. 支持哪些算法?

OpenSSL 支持许多不同的加密算法:

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. 源码实现

openssl: openssl库学习

1.1 main.c

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

c
#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

c
#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);

#endif

1.5 Makefile

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. 编译运行

不再赘述。