安装参考http://p-nand-q.com/programming/windows/building_openssl_with_visual_studio_2013.html

此链接也提供编译好的dll和lib,如果是为了学习,完全可以相信此网站。如果是商用,建议从源代码开始编译。


现在使用openssl库的AES加解密功能。比较常用的教材例子是CBC。CBC是AES加解密的一种方式。它有以下特性:

1)密文的长度与明文的长度一致。

2)加密秘钥的字节序列与解密秘钥的字节序列一致。因为AES是一种对称加密算法。

3)秘钥的长度支持128位,196位,256位三种长度。

4)需要初始矩阵参与加密解密。初始矩阵一般是16个字节,加密和解密用相同的初始矩阵。

5)宏AES_BLOCK_SIZE被定义为16,明文被分块计算,每个块的长度等于16

7)openssl有bug,遇到明文长度大于AES_BLOCK_SIZE,并且不是AES_BLOCK_SIZE整倍数时,最后一个分块会被计算错。

8)对于7)的问题,有两种规避方法。第一种是把明文凑整,使明文的长度等于AES_BLOCK_SIZE的整倍数。但是这种方法比较恶心,因为它

      改变了明文。但是对于以字符串0结束的明文,在字符串0结束符后填充点东西是不会改变字符串的。但是对于非字符串的明文,就不能使用这个办法了。

     下面的代码演示了主要思路,请忽略内存泄漏问题

     

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

void test1()
{
    char* text = "1234567890ABCDEF[]";
    size_t len = strlen(text) + 1;
    if (len % AES_BLOCK_SIZE != 0) {
        len = (len / AES_BLOCK_SIZE + 1)*AES_BLOCK_SIZE;  //len取整
    }
    
    //存放明文的字节数组
    unsigned char* plainText = new unsigned char[len];
    strcpy((char*)plainText, text);

    //128-bit的秘钥字节数组,占16个字节。可以存放任意值。
    unsigned char my_16bytes_key[16] = { 0 };
    for (int i = 0; i<16; ++i) {
        my_16bytes_key[i] = 0x30;
    }

    //生成openssl库使用的秘钥结构体
    AES_KEY aes_key;
    if (AES_set_encrypt_key(my_16bytes_key, 128, &aes_key) < 0) {
        fprintf(stderr, "Unable to set encryption key in AES\n");
        exit(-1);
    }

    //初始向量,实际上就是一个16字节的字节序列,可以是任意值。
    unsigned char iv[AES_BLOCK_SIZE];        // init vector
    for (int i = 0; i<AES_BLOCK_SIZE; ++i) {
        iv[i] = 0;
    }

    //加密。加密的结果被存放到cyperText。cyperText的长度应该等于plainText的长度
    //秘钥和初始向量也是必要的参数。
    unsigned char* cyperText = new unsigned char[len];
    AES_cbc_encrypt(plainText, cyperText, len, &aes_key, iv, AES_ENCRYPT);

    //存放解密后的明文数据。因为明文和密文长度一致,当然知道解密后也要用len字节的内存。
    unsigned char* decryptText = new unsigned char[len];

    //初始向量。应与加密时的初始向量一致。
    //这里再次给iv数组赋值的原因:前一次调用AES_cbc_encrypt后,iv的内容已被改变了。这里是恢复为期望的值。
    for (int i = 0; i<AES_BLOCK_SIZE; ++i) {
        iv[i] = 0;
    }

    //初始化秘钥结构体。应该与加密时的秘钥相同,这才叫“对称加密”
    if (AES_set_decrypt_key(my_16bytes_key, 128, &aes_key) < 0) {
        fprintf(stderr, "Unable to set decryption key in AES\n");
        exit(-1);
    }

    //解密。
    AES_cbc_encrypt(cyperText, decryptText, len, &aes_key, iv, AES_DECRYPT);

    // print
    printf("plainText = %s\n", plainText);
    printf("cyperText = ");
    for (int i = 0; i<len; ++i) {
        printf("%x%x", (cyperText[i] >> 4) & 0xf,
            cyperText[i] & 0xf);
    }
    printf("\n");
    printf("decryptText = %s\n", decryptText);

    return ;
}



9)第二种办法是:  使用openssl的高级函数。参考https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption

10)提供一种改进的代码,但是文档性的支持说明这个方法总是有效的。

void test2()
{

    //存放明文的字节数组,存放二进制数据
    unsigned char plainText[19];
    memcpy(plainText, "1234567890ABCDEF[]", 19);
    
    //128-bit的秘钥字节数组,占16个字节。可以存放任意值。
    unsigned char my_16bytes_key[16] = { 0 };

    //生成openssl库使用的秘钥结构体
    AES_KEY aes_key;
    AES_set_encrypt_key(my_16bytes_key, 128, &aes_key);

    //初始向量,实际上就是一个16字节的字节序列,可以是任意值。
    unsigned char iv1[AES_BLOCK_SIZE] = {0};        // init vector

    //存放加密后的密文的数组,需要分配AES_BLOCK_SIZE的整倍数
    unsigned char* cyperText = new unsigned char[32];

    // unsigned char* cyperText = new unsigned char[19]; 不要分配不足16整倍数的空间

    //加密
    AES_cbc_encrypt(plainText, cyperText, 19 , &aes_key, iv1, AES_ENCRYPT);

    //存放解密后的明文数据。因为明文和密文长度一致,当然知道解密后也要用len字节的内存。
    unsigned char * decryptText = new unsigned char[19];
   
    //初始向量。应与加密时的初始向量一致。
    unsigned char iv2[AES_BLOCK_SIZE] = { 0 };        // init vector

    //初始化秘钥结构体。应该与加密时的秘钥相同,这才叫“对称加密”
    AES_set_decrypt_key(my_16bytes_key, 128, &aes_key);

    //解密。
    AES_cbc_encrypt(cyperText, decryptText, 19 , &aes_key, iv2, AES_DECRYPT);

    // print
    printf("plainText = %s\n", plainText);
    printf("cyperText = ");
    for (int i = 0; i<19; ++i) {
        printf("%x%x", (cyperText[i] >> 4) & 0xf,cyperText[i] & 0xf);
    }
    printf("\n");
    printf("decryptText = %s\n", decryptText);


    return;
}