AES加密简介

       AES加密标准又称为高级加密标准Rijndael加密法,是美国国家标准技术研究所NIST旨在取代DES的21世纪的加密标准。AES的基本要求是,采用对称分组密码体制,密钥长度可以为128、192或256位,分组长度128位,算法应易在各种硬件和软件上实现。1998年NIST开始AES第一轮分析、测试和征集,共产生了15个候选算法。

      1999年3月完成了第二轮AES2的分析、测试。2000年10月2日美国政府正式宣布选中比利时密码学家Joan Daemen和Vincent Rijmen提出的一种密码算法Rijndael作为AES的加密算法。

       AES加密数据块和密钥长度可以是128b、192b、256b中的任意一个。

1、加密种类

       分组密码有五种工作体制:

              1.电码本模式(Electronic Codebook Book (ECB));

              2.密码分组链接模式(Cipher Block Chaining (CBC));

              3.计算器模式(Counter (CTR));

              4.密码反馈模式(Cipher FeedBack (CFB));

              5.输出反馈模式(Output FeedBack (OFB))。

1.1、电码本模式(Electronic Codebook Book (ECB)

        这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。

                                        

AESUtil默认加密模式 aes加密格式_加密

1.2、密码分组链接模式(Cipher Block Chaining (CBC))

         这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

                                                

AESUtil默认加密模式 aes加密格式_c语言_02

1.3、计算器模式(Counter (CTR))

        计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。CTR的示意图如下所示:

                                    

AESUtil默认加密模式 aes加密格式_AESUtil默认加密模式_03

1.4、密码反馈模式(Cipher FeedBack (CFB))

        这种模式较复杂。

 

                                            

AESUtil默认加密模式 aes加密格式_#include_04

1.5、输出反馈模式(Output FeedBack (OFB))

                                                 

AESUtil默认加密模式 aes加密格式_#include_05

 

2、padding

       AES对小段逐一进行加密,对于不足16个字节的小段,使用padding补充。

       主要padding种类:

              ZeroPadding:使用“0”作为填充数据

              Pkcs5Padding:PKCS7Padding的子集,只是块大小固定为8字节;

              Pkcs7Padding:假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小

       Pkcs7Padding的块长度可以是1-255之间,而Pkcs5Padding只能是8.

BASE64编码简介

         Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。可查看RFC2045~RFC2049

所使用的的字符如下所示

                            

AESUtil默认加密模式 aes加密格式_AESUtil默认加密模式_06

 

实例

环境:ubuntu 16.04, openssl

demo.c

##########################################################################
#include <stdio.h>
#include <openssl/aes.h>
#include <string.h>
#include <stdlib.h>

#define OUR_AES_BLOCK_SIZE AES_BLOCK_SIZE /* 16 */
struct aes_key_st encrypt_key, decrypt_key;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
static int ncs_key_init(unsigned char *key)
{
	if (AES_set_encrypt_key(key, 128, &encrypt_key) < 0) {
		printf("Unable to set encryption key in AES");
		return -1;
	}
	if (AES_set_decrypt_key(key, 128, &decrypt_key) < 0) {
		printf("Unable to set encryption key in AES");
		return -1;
	}
	printf("ENCRYPT key init OK!\n");
	return 0;
}
static uint8_t decode_getbyte(char c) 
{
	char *dict = "+/=";
	char *dict2 = "-_.";
	
    //if (c == '+') {
    if (c == dict[0] || c == dict2[0]) {
        return 62;
    //} else if (c == '/') {
    } else if (c == dict[1] || c == dict2[1]) {
        return 63;
    } else if (c <= '9') {
        return (uint8_t)(c - '0' + 52);
    //} else if (c == '=') {
    } else if(c == dict[2] || c == dict2[2]) {
        return 64;
    } else if (c <= 'Z') {
        return (uint8_t)(c - 'A');
    } else if (c <= 'z') {
        return (uint8_t)(c - 'a' + 26);
    } 
    return 64;
}

uint32_t base64_encode(const char *src, uint32_t srclen, char *dest, int url_safe) 
{
    uint8_t input[3];
    uint8_t output[4];
    uint32_t i;
    uint32_t index_src = 0;
    uint32_t index_dest = 0;
	
	char *dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	if(url_safe)
	{
		dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/.";
	}

    for (i = 0;i < srclen;i += 3) {
        input[0] = src[index_src++];
        output[0] = (uint8_t)((input[0] >> 2)&0x3f);
        dest[index_dest++] = dict[output[0]];

        if (index_src < srclen) {
            input[1] = src[index_src++];
            output[1] = (uint8_t)(((input[0] & 0x03) << 4) + ((input[1] >> 4)&0x0f));
            dest[index_dest++] = dict[output[1]];
        } else {
            output[1] = (uint8_t)((input[0] & 0x03) << 4);
            dest[index_dest++] = dict[output[1]];
            //dest[index_dest++] = '=';
            //dest[index_dest++] = '=';
            dest[index_dest++] = dict[64];
            dest[index_dest++] = dict[64];
            break;
        }

        if (index_src < srclen) {
            input[2] = src[index_src++];
            output[2] = (uint8_t)(((input[1] & 0x0f) << 2) + ((input[2] >> 6)&0x03));
            dest[index_dest++] = dict[output[2]];
        } else {
            output[2] = (uint8_t)((input[1] & 0x0f) << 2);
            dest[index_dest++] = dict[output[2]];
            //dest[index_dest++] = '=';
            dest[index_dest++] = dict[64];
            break;
        }

        output[3] = (uint8_t)(input[2] & 0x3f);
        dest[index_dest++] = dict[output[3]];
    }

    dest[index_dest] = 0;

    return index_dest;
}
uint32_t base64_decode(const char *src, uint32_t srclen, char *dest) 
{
    uint8_t data[4];
    uint32_t i;
    uint32_t index = 0;
	char paddingchar = '=';
	char paddingchar2 = '.';

    for (i=0; i<srclen; i+=4) {
        data[0] = decode_getbyte(src[i]);
        data[1] = decode_getbyte(src[i + 1]);
        dest[index++] = (data[0] << 2) + (data[1] >> 4);

        //if (src[i+2] != '=') {
        if (src[i+2] != paddingchar && src[i+2] != paddingchar2) {
            data[2] = decode_getbyte(src[i+2]);
            if(64 > data[2])
			{
            	dest[index++] = ((data[1] & 0x0f) << 4) + (data[2] >> 2);
            }
        }

        //if (src[i+3] != '=') {
        if (src[i+3] != paddingchar && src[i+3] != paddingchar2) {
            data[3] = decode_getbyte(src[i + 3]);
			if(64 > data[3])
			{
            	dest[index++] = ((data[2] & 0x03) << 6) + (data[3]&0x3f);
			}
        }
    }

    dest[index] = '\0';

    return index;
}

static int ncs_encrypt(unsigned char *in, unsigned char *out)
{
	char *input, *output, *tmpin, *tmpout = NULL;
	int inputlen, inlen = 0;
	int padding = 0;
	int i;
	/* get aes in string */
	printf("in %s\n", in);
	inlen = strlen(in);
	inputlen = (((inlen + OUR_AES_BLOCK_SIZE)/OUR_AES_BLOCK_SIZE)*OUR_AES_BLOCK_SIZE);
	input = (char*)malloc(inputlen + 1);
	if (input == NULL) {
		return -1;
	}
	memset((void *)input, 0, inputlen + 1);
	output = (char*)malloc(inputlen + 1);
	if (output == NULL) {
		return -1;
	}
	memset((void *)output, 0, inputlen + 1);
	
	strcpy(input, in);

	padding = inputlen - inlen;
	for (i = 0; i < padding; i++) { /* pkcs7padding */
		input[inlen + i] = padding;
	}

	/* aes ecb */
	i = 0;
	tmpin = input;
	tmpout = output;
	while (i < inputlen) {
		//AES_ecb_encrypt(tmpin, tmpout, &encrypt_key, AES_ENCRYPT);
		AES_encrypt(tmpin, tmpout, &encrypt_key);
		tmpin += OUR_AES_BLOCK_SIZE;
		tmpout += OUR_AES_BLOCK_SIZE;
		i += OUR_AES_BLOCK_SIZE;
	}

	base64_encode(output, inputlen, out, 0);
	printf("encode %s; len %d;\n", out, strlen(out));
	return 0;
}
static int ncs_decrypt(unsigned char *in, unsigned char *out)
{
	const char *data_en;
	char data[4096] = {0};
	char *input, *output, *tmpin, *tmpout = NULL;
	int inputlen, inlen = 0;
	int i, j;
	int padding = 0;

	int padding_len, de_len;
	data_en = in;

	for (i = 0, j =0; i < strlen(data_en); i++) {
		if (data_en[i] != '"') {
			data[j++] = data_en[i];
		}
	}
	printf("decode %s; len %d;\n", data, strlen(data));
	inlen = strlen(data);
	input = (char*)malloc(inlen + OUR_AES_BLOCK_SIZE);
	if (input == NULL) {
		return -1;
	}
	memset((void *)input, 0, inlen + OUR_AES_BLOCK_SIZE);
	output = (char*)malloc(inlen + OUR_AES_BLOCK_SIZE);
	if (output == NULL) {
		return -1;
	}
	memset((void *)output, 0, inlen + OUR_AES_BLOCK_SIZE);
	de_len = base64_decode(data, inlen, input);
	padding_len = (((de_len+OUR_AES_BLOCK_SIZE-1)/OUR_AES_BLOCK_SIZE)*OUR_AES_BLOCK_SIZE);
	i = 0;
	tmpin = input;
	tmpout = output;
	while (i < inlen) {
		AES_decrypt(tmpin, tmpout, &decrypt_key);
		tmpin += OUR_AES_BLOCK_SIZE;
		tmpout += OUR_AES_BLOCK_SIZE;
		i += OUR_AES_BLOCK_SIZE;
	}
	padding = output[padding_len -1];
	for (i = 0; i < padding; i++) {
		output[padding_len-i-1] = 0;
	}
	printf("out %s\n", output);

	return 0;
}

int ncs_encrypt_decrypt_test(void)
{
	unsigned char data[16] = "1234567890";
	unsigned char endata[256] = {0};
	unsigned char dedata[256] = {0};
	int i = 0;
	ncs_key_init("5c44crn9ap98api7");
	ncs_encrypt(data, endata);
	ncs_decrypt(endata, dedata);
	return 0;
}

void main()
{
	ncs_encrypt_decrypt_test();
}

然后使用gcc 编译即可

gcc demo.c -o demo -I/usr/local/ssl/include/ -lssl -lcrypto  -ldl -L/usr/local/ssl/lib

运行可执行文件

./demo

结果如下

ENCRYPT key init OK!
in 1234567890
encode cnBnIUTu+yEhshQbXoduHg==; len 24;
decode cnBnIUTu+yEhshQbXoduHg==; len 24;
out 1234567890