通过OpenSSL接口实现AES加密

目录

前言

OpenSSL

主要功能包括:

使用 OpenSSL 的常见场景:

EVP接口

基本流程

接口介绍

示例代码

前言

AES(Advanced Encryption Standard, 高级加密标准)是一种广泛使用的对称密钥加密算法。它是由比利时密码学家 Joan Daemen 和 Vincent Rijmen 设计的,并在2001年被美国国家标准与技术研究院(NIST)采纳为官方标准。

AES 的主要特点包括:

  • 安全性:AES 被认为是非常安全的,至今没有已知的有效攻击可以破解其完整版本。

  • 效率:AES 在软件和硬件实现上都非常高效,适合多种计算环境。

  • 密钥长度:AES 支持128位、192位和256位的密钥长度,分别对应 AES-128AES-192AES-256

  • 块大小:AES 处理的数据块大小固定为128位。

AES 的工作模式也很重要,常见的工作模式包括 ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB(密码反馈模式)、OFB(输出反馈模式)以及更现代的 GCM(Galois/Counter Mode)等。

AES 常用于各种场景,例如网络通信加密、硬盘加密、文件传输加密等。它是现代加密技术的一个基石。

OpenSSL

OpenSSL 是一个强大的安全套件,广泛用于实现安全的网络通信。它基于 SSL 和 TLS 协议,提供了多种加密算法、密码学功能以及安全协议的实现。OpenSSL 不仅可以用来实现服务器端和客户端之间的安全通信,还可以用于加密文件存储、安全电子邮件传输等多种应用场景。

主要功能包括:

  1. 加密算法:支持多种加密算法,如 AES、DES、3DES、RSA、DSA、ECC 等。

  2. 安全协议:实现了 SSL/TLS 协议,用于保护网络通信的安全。

  3. 证书管理:可以用来生成数字证书和公私钥对。

  4. 随机数生成器:提供了安全的随机数生成器,这对于密码学应用至关重要。

  5. 密码学哈希函数:支持多种哈希算法,如 SHA-1、SHA-256、SHA-512 等。

  6. 消息认证码:实现 HMAC 等算法,用于消息完整性验证。

  7. 密钥协商:支持 Diffie-Hellman 密钥交换等协议。

  8. 安全套接字层(SSL)/传输层安全(TLS):提供 API 用于实现安全的客户端-服务器通信。

使用 OpenSSL 的常见场景:

  • Web 服务器:使用 SSL/TLS 保护 HTTP 请求。

  • 客户端应用程序:实现与 Web 服务器的安全通信。

  • 安全电子邮件:使用 S/MIME 协议加密邮件。

  • 文件加密:使用 OpenSSL 命令行工具加密文件。

  • 安全数据库连接:通过 SSL/TLS 连接数据库服务器。

下面主要是学习通过调用 OpenSSL 接口实现 AES 加密。

EVP接口

EVP(Encryption and Decryption Primitives)是 OpenSSL 中的一个高级加密接口,它简化了使用 OpenSSL 中的加密算法的过程。EVP 提供了一组简单的函数,可以用来处理各种加密操作,而无需直接与底层的密码算法打交道。

基本流程

  1. 初始化上下文 (EVP_CIPHER_CTX):创建一个新的上下文结构体,并设置加密算法类型。

  2. 初始化加解/密 (EVP_EncryptInit_exEVP_DecryptInit_ex):初始化加密或解密上下文。

  3. 更新加密/解密操作 (EVP_EncryptUpdateEVP_DecryptUpdate):实际执行加密或解密操作。

  4. 最终化加密/解密操作 (EVP_EncryptFinal_exEVP_DecryptFinal_ex):完成加密或解密操作,并处理任何剩余的数据。

  5. 清理上下文 (EVP_CIPHER_CTX_free):释放上下文结构体。

接口介绍

头文件

#include <openssl/evp.h>
#include <openssl/aes.h>

数据类型

// 使用到的数据类型
// OpenSSL 内部用于处理加密和解密操作的上下文结构,结构体包含了所有用于执行加密或解密操作所需的状态信息
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
​
// 加密算法的封装,提供了加密算法的基本信息和相关的操作接口,结构体包含了所有用于描述加密算法所需的信息。
typedef struct evp_cipher_st EVP_CIPHER;

初始化加密算法

// 用于设置使用 256 位密钥长度的 AES 加密算法,并采用 CBC(Cipher Block Chaining)模式。
const EVP_CIPHER *cipherType = EVP_aes_256_cbc();

除了 EVP_aes_256_cbc(),OpenSSL 提供了多种其他接口来支持不同的加密算法和模式,具体可以查看头文件 openssl/evp.h 。

初始化上下文

// 用途:分配一个新的 EVP_CIPHER_CTX 结构体实例。
// 返回值:返回一个新的 EVP_CIPHER_CTX 指针,如果失败则返回 NULL。
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
​
// 初始化 EVP_CIPHER_CTX 上下文,初始化结构体参数
void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);

通过密码获取秘钥和IV

下面接口可以从给定的密码(通常是一个字节数组)和盐(salt)生成加密密钥和初始化向量(Initialization Vector, IV);

/*
 * 参数说明
 * type: 指定要使用的加密算法类型,如 EVP_aes_256_cbc()。
 * md: 指定用于密钥扩展的摘要算法,如 EVP_sha256()、EVP_md5()。
 * salt: 指向盐的指针,盐是用来增加密码强度的随机数据。
 * data: 指向密码的指针,密码通常是一个字节数组。
 * datal: 密码的长度(以字节为单位)。
 * count: 密钥派生函数的迭代次数,较大的值通常会使攻击者更难以暴力破解密码。
 * key: 指向存储生成密钥的缓冲区的指针。
 * iv: 指向存储生成初始化向量的缓冲区的指针。
 *
 * 如果成功,EVP_BytesToKey 返回 1;如果失败,则返回 0。
 */
int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
                   const unsigned char *salt, const unsigned char *data,
                   int datal, int count, unsigned char *key,
                   unsigned char *iv);

这个函数特别适用于基于密码的加密场景,例如当你需要使用一个密码来加密或解密数据时。

加密秘钥和初始化向量值也可以手动指定。

初始化加解/密

/* 
 * 参数:
 * ctx:指向 EVP_CIPHER_CTX 的指针。
 * type:指定使用的加密算法。
 * impl:可选参数,通常为 NULL,表示使用默认的实现。
 * key:密钥。
 * iv:初始向量(如果算法需要的话)。
 *
 * 成功返回 1,失败返回 0
 */
// 初始化 EVP_CIPHER_CTX 以便执行加密/解密操作。
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
                       ENGINE *impl, const unsigned char *key,
                       const unsigned char *iv);
​
int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
                       ENGINE *impl, const unsigned char *key,
                       const unsigned char *iv);

更新加密/解密操作

/*
 * 参数:
 * ctx:指向 EVP_CIPHER_CTX 的指针。
 * out:指向缓冲区的指针,用于存放加密/解密后的数据。
 * outl:指向整型变量的指针,该变量用于接收加密/解密后数据的实际长度。
 * in:指向缓冲区的指针,其中包含待加密/待解密的数据。
 * inl:待加密/待解密数据的长度。
 *
 * 返回值:成功返回 1,失败返回 0。
 */
// 更新加密/解密操作,处理数据块。
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
                      const unsigned char *in, int inl);
​
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
                      const unsigned char *in, int inl);

最终化加密/解密操作

/*
 * 参数:
 * ctx:指向 EVP_CIPHER_CTX 的指针。
 * out:指向缓冲区的指针,用于存放加密后的数据。
 * outl:指向整型变量的指针,该变量用于接收加密后数据的实际长度。
 *
 * 返回值:成功返回 1,失败返回 0。
 */
// 完成加密/解密操作,并处理最后一个数据块。
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);

清理上下文

/*
 * 参数:
 * ctx:指向 EVP_CIPHER_CTX 的指针。
 */
// 清理 EVP_CIPHER_CTX 结构体的状态
int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
​
// 释放 EVP_CIPHER_CTX 结构体实例。
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
​

示例代码

现在我们要使用 AES-256-CBC 加密模式进行加密和解密。

加密部分

/*
*****************************************************************************************
*   函 数 名: aes_encrypt_string
*   功能说明: AES加密字符串
*   形    参:   _pPassword  :   密码
*               _pInput     :   输入数据
*               _InLen      :   输入数据长度
*               _pOutBuf    :   输出AES编码数据
*               _pOutLen    :   输出AES编码数据长度
*   返 回 值: 0:成功, -1:失败
*****************************************************************************************
*/
int aes_encrypt_string(char *_pPassword, char *_pInput, int _InLen, char *_pOutBuf, int *_pOutLen)
{
    // 上下文结构
    EVP_CIPHER_CTX *pEn_ctx = NULL;
    
    int ret = -1;
    int flen = 0, outlen = 0;
    int i, nrounds = 1;
    
    // 存储秘钥和初始化向量
    unsigned char key[32] = {}; 
    unsigned char iv[32] = {};
 
    // 参数判断
    if (_pPassword == NULL || _pInput == NULL || _pOutBuf == NULL || _pOutLen == NULL) {
        return ret;
    }
    
    // 设置使用 256 位密钥长度的 AES 加密算法,并采用 CBC 模式。
    const EVP_CIPHER *cipherType = EVP_aes_256_cbc();
    if( cipherType == NULL ){
        goto clean;
    }
 
    /*
    * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
    * nrounds is the number of times the we hash the material. More rounds are more secure but
    * slower.
    */
    // 通过输入密码产生密钥key和初始化向量iv
    i = EVP_BytesToKey(cipherType, EVP_md5(), NULL, _pPassword, strlen(_pPassword), nrounds, key, iv);  
    if (i != 32) {
        printf("Key size is %d bits - should be 256 bits\n", i);
        goto clean;
    }
 
    pEn_ctx = EVP_CIPHER_CTX_new();                             //创建加密上下文
    EVP_CIPHER_CTX_init(pEn_ctx);                               //初始化 EVP_CIPHER_CTX 上下文
    EVP_EncryptInit_ex(pEn_ctx, cipherType, NULL, key, iv);     //初始化加密操作
 
    /* Update cipher text */
    if (!EVP_EncryptUpdate(pEn_ctx, (unsigned char*)_pOutBuf, &outlen,(unsigned char*)_pInput, _InLen)) {   //处理数据
        perror("\n Error,ENCRYPR_UPDATE:");
        goto clean;
    }
 
    /* updates the remaining bytes */
    if (!EVP_EncryptFinal_ex(pEn_ctx, (unsigned char*)(_pOutBuf + outlen), &flen)) {    //完成加密操作,处理剩余字节
        perror("\n Error,ENCRYPT_FINAL:");
        goto clean;
    }
    
    *_pOutLen = outlen + flen;
    
    ret = 0;    /* SUCCESS */
    
clean:
    // 清理内存
    if( pEn_ctx )
        EVP_CIPHER_CTX_cleanup(pEn_ctx);
    if( pEn_ctx )
        EVP_CIPHER_CTX_free(pEn_ctx);
 
    return ret;
}

解密部分

/*
*****************************************************************************************
*   函 数 名: aes_decrypt_string
*   功能说明: AES解密得到字符串
*   形    参:   _pPassword  :   密码
*               _pInput     :   输入需解密的数据
*               _InLen      :   输入需解密的数据长度
*               _pOutBuf    :   输出AES解密后的字符串
*               _pOutLen    :   输出AES编码数据长度
*   返 回 值: 0:成功, -1:失败
*****************************************************************************************
*/
int aes_decrypt_string(char *_pPassword, char *_pInput, int _InLen, char *_pOutBuf, int *_pOutLen)
{
    // 上下文结构
    EVP_CIPHER_CTX *pDe_ctx = NULL;
    
    int ret = -1;
    int flen = 0, outlen = 0;
    int i, nrounds = 1;
    
    // 存储秘钥和初始化向量
    unsigned char key[32] = {}; 
    unsigned char iv[32] = {};
 
    // 参数判断
    if (_pPassword == NULL || _pInput == NULL || _pOutBuf == NULL || _pOutLen == NULL) {
        return ret;
    }
    
    // 设置使用 256 位密钥长度的 AES 加密算法,并采用 CBC 模式。
    const EVP_CIPHER *cipherType = EVP_aes_256_cbc();
    if( cipherType == NULL ){
        goto clean;
    }
 
    /*
    * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
    * nrounds is the number of times the we hash the material. More rounds are more secure but
    * slower.
    */
    // 通过输入密码产生密钥key和初始化向量iv
    i = EVP_BytesToKey(cipherType, EVP_md5(), NULL, _pPassword, strlen(_pPassword), nrounds, key, iv);
    if (i != 32) {
        printf("Key size is %d bits - should be 256 bits\n", i);
        goto clean;
    }
 
    pDe_ctx = EVP_CIPHER_CTX_new();                             //创建加密上下文
    EVP_CIPHER_CTX_init(pDe_ctx);                               //初始化 EVP_CIPHER_CTX 上下文
    EVP_DecryptInit_ex(pDe_ctx, cipherType, NULL, key, iv);     //初始化解密操作
 
    /* Update cipher text */
    if (!EVP_DecryptUpdate(pDe_ctx, (unsigned char*)_pOutBuf, &outlen, (unsigned char*)_pInput, _InLen)) {   //处理数据
        perror("\n Error,ENCRYPR_UPDATE:");
        goto clean;
    }
 
    /* updates the remaining bytes */
    if (EVP_DecryptFinal_ex(pDe_ctx, (unsigned char*)(_pOutBuf + outlen), &flen) != 1) {    //完成解密操作,处理剩余字节
        perror("\n Error,ENCRYPT_FINAL:");
        goto clean;
    }
 
    *_pOutLen = outlen + flen;
    
clean:
    // 清理内存
    if( pDe_ctx )
        EVP_CIPHER_CTX_cleanup(pDe_ctx);
    if( pDe_ctx )
        EVP_CIPHER_CTX_free(pDe_ctx);
 
    return ret;
}

主函数实现

int main(int argc, char *argv[])
{
    if( argc < 3 )
    {
        printf("Usage: %s <STRING> <PASSWORD>\n", argv[0]);
        return -1;
    }
​
    int i;
    char acString[96]    = {0};
    char acPassword[96]  = {0};
    char acEncrypt[2048] = {0}; 
    char acDecrypt[2048] = {0};
    int enLen = 0, deLen = 0;
 
    strcpy(acString, argv[1]);
    strcpy(acPassword, argv[2]);
 
    /* 打印加密的字符串和密码 */
    printf("===========Org=============\n");
    printf("password: %s\n", acPassword);
    printf("string: %s\n", acString);
    printf("slen: %d\n", strlen(acString));
    printf("===========================\n\n");
​
    /* AES加密 */
    printf("===========ENC=============\n");
    i = aes_encrypt_string( acPassword, acString, strlen(acString), acEncrypt, &enLen );
    if( i < 0 ){
        printf("enc error.\n");
        return -1;
    }
    printf("Enc: %s\n", acEncrypt);                                                           //打印加密数据
    printf("elen: %d\n", enLen);  
    printf("===========================\n\n");
​
    /* AES解密 */
    printf("============DEC=============\n");
    i = aes_decrypt_string( acPassword, acEncrypt, enLen, acDecrypt, &deLen );
    if( i < 0 ){
        printf("dec error.\n");
        return -1;
    }
    
    acDecrypt[deLen] = 0;
    printf("Dec: %s\n", acDecrypt);                                                                 
    //打印解密数据
    printf("dlen: %d\n", deLen);
    printf("===========================\n\n");
 
    return 0;
}

编写Makefile

# 编译器
CC := gcc
CPP := g++
​
# 编译选项
#CFLAGS := -Wall -Werror -g
CFLAGS := -g
​
# 目标文件
TARGET := aesTest
# 源文件
SRCS := main.c
​
# 头文件路径
INC_DIRS := -I /opt/linux/openssl/include
# 动态库路径
LIB_DIRS := -L /opt/linux/openssl/lib
# 链接的动态库
LD_LIBS := -lcrypto
​
# 生成目标文件
OBJS := $(SRCS:.c=.o)
​
# 生成可执行文件
$(TARGET): $(OBJS)
    $(CC) $^ -o $@ $(CFLAGS) $(INC_DIRS) $(LIB_DIRS) $(LD_LIBS)
​
# 生成目标文件
%.o: %.c
    $(CC) -c $< -o $@ $(CFLAGS) $(INC_DIRS) $(LIB_DIRS) $(LD_LIBS)
​
# 清理目标文件和可执行文件
clean:
    rm -f $(OBJS) $(TARGET)

执行make,生成可执行文件,运行结果如下:


通过上面的学习,我们可以自己写一个简易的文件加解密的工具,工程用Qt打开;

该工程的源码查看:study: 用于C++的学习同步 - Gitee.com

编译运行后显示主界面如下:

选择文件进行加密,设置输出文件:

点击加密,开始加密,查看加密后的文件:

两个文件的md5值不同,已经加密成功;

下面开始解密,选择加密文件和解密后文件路径,点击解密:

解密后文件的md5值与源文件一样,说明已经解密成功了;

该工程的源码查看:study: 用于C++的学习同步 - Gitee.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值