OpenSSL 使用AES对文件加解密

打印 上一主题 下一主题

主题 891|帖子 891|积分 2673

AES(Advanced Encryption Standard)是一种对称加密算法,它是目前广泛使用的加密算法之一。AES算法是由美国国家标准与技术研究院(NIST)于2001年发布的,它取代了原先的DES(Data Encryption Standard)算法,成为新的标准。AES是一种对称加密算法,意味着加密和解密使用相同的密钥。这就要求密钥的安全性非常重要,因为任何拥有密钥的人都能进行加密和解密操作。其密钥长度,包括128位、192位和256位。不同长度的密钥提供了不同级别的安全性,通常更长的密钥长度意味着更高的安全性。
该算法支持多种工作模式,其中两种常见的模式是CBC(Cipher Block Chaining)和ECB(Electronic Codebook)。

  • CBC 模式(Cipher Block Chaining):

    • 工作原理:

      • CBC模式对每个明文块进行加密前,先与前一个密文块进行异或操作。首个块使用一个初始化向量(IV)与明文异或。这种链式反馈机制使得每个密文块的加密都依赖于前一个块的密文,从而增加了安全性。

    • 特点:

      • 带有初始化向量,对同样的明文块加密得到的密文块会随着其前面的明文块的不同而不同。
      • 适用于加密长度超过一个块的数据。

    • 优点和缺点:

      • 优点:提供更高的安全性,适用于加密大块的数据。
      • 缺点:由于加密是依赖于前一个块的密文,所以无法进行并行加密处理。


  • ECB 模式(Electronic Codebook):

    • 工作原理:

      • ECB模式将明文分割成块,每个块独立加密,然后再组合成密文。相同的明文块将始终加密为相同的密文块。

    • 特点:

      • 不需要初始化向量,同样的明文会得到同样的密文。
      • 适用于加密独立的数据块,但对于相同的块,ECB模式下的输出相同。

    • 优点和缺点:

      • 优点:简单,易于实现。
      • 缺点:相同的明文块生成相同的密文块,可能导致安全性问题。不适用于加密大块的数据。


在选择模式时,需要根据具体的应用场景和需求权衡安全性和性能。一般来说,CBC模式是更安全的选择,而ECB模式可能更容易实现和理解。在实际应用中,还可以考虑其他模式,如CTR(Counter)模式和GCM(Galois/Counter Mode)模式等,这些模式结合了安全性和性能的考虑。
本次案例中所需要使用的头文件信息如下所示;
  1. #define  _CRT_SECURE_NO_WARNINGS
  2. #define _WINSOCK_DEPRECATED_NO_WARNINGS
  3. #include <iostream>
  4. #include <openssl/err.h>
  5. #include <openssl/aes.h>
  6. #include <openssl/evp.h>
  7. #include <openssl/crypto.h>
  8. #include <openssl/pem.h>
  9. extern "C"
  10. {
  11. #include <openssl/applink.c>
  12. }
  13. #pragma comment(lib,"libssl_static.lib")
  14. #pragma comment(lib,"libcrypto.lib")
复制代码
使用CBC模式加解密

Cipher Block Chaining (CBC) 模式是一种对称加密的分组密码工作模式。在 CBC 模式中,明文被分成固定大小的块,并使用加密算法逐个处理这些块。每个块都与前一个块的密文进行异或运算,然后再进行加密。这个过程导致了一种“链接”效果,因此得名 Cipher Block Chaining。
以下是 CBC 模式的详细概述:
初始向量 (Initialization Vector, IV)

  • 在 CBC 模式中,每个消息的第一个块使用一个初始向量 (IV)。IV 是一个固定长度的随机数,它在每次加密不同消息时都应该是唯一的。IV 的作用是在每个块的加密中引入随机性,以防止相同的明文块生成相同的密文块。
分组加密

  • 消息被分成固定大小的块(通常为 64 比特或 128 比特),然后每个块都被分组加密。最常用的块加密算法是 AES。
异或运算

  • 在每个块加密之前,明文块与前一个密文块进行异或运算。这就是“链接”发生的地方。第一个块与 IV 异或。
加密

  • 异或运算后的结果被送入块加密算法进行加密。得到的密文块成为下一个块的 IV。
解密

  • 在解密时,密文块被送入块解密算法进行解密。解密后的结果与前一个密文块进行异或运算,得到明文块。
模式串行化

  • CBC 模式是串行的,因为每个块的加密都依赖于前一个块的密文。这也意味着无法并行处理整个消息。
填充

  • 如果明文的长度不是块大小的整数倍,需要进行填充。常见的填充方案有 PKCS#7 填充。
安全性

  • 当使用 CBC 模式时,密文块的顺序对安全性至关重要。如果消息的两个块对调,解密后会得到不同的明文。因此,必须保证密文块的顺序不被篡改。
使用场景

  • CBC 模式常用于保护传输层安全协议(如 TLS)中,以提供加密和数据完整性。
总体而言,CBC 模式提供了一种相对强大的加密方法,但在实现时需要注意使用随机且不可预测的 IV 以及处理填充的问题。
AES_set_encrypt_key 函数。具体来说,它用于将原始密钥设置为可以在 AES 加密算法中使用的格式。以下是该函数的原型:
  1. int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
复制代码

  • userKey:指向用于设置密钥的输入数据的指针,即原始密钥。
  • bits:密钥长度,以比特为单位。在使用 AES 加密算法时,通常为 128、192 或 256。
  • key:指向 AES_KEY 结构的指针,用于存储设置后的密钥信息。
该函数返回值为零表示成功,非零表示失败。成功调用后,key 参数中存储了经过格式化的密钥信息,可以在后续的 AES 加密操作中使用。
AES_cbc_encrypt 是 OpenSSL 库中用于执行 AES 算法中的 Cipher Block Chaining (CBC) 模式的函数。在 CBC 模式中,每个明文块在加密之前会与前一个密文块进行异或运算,以增加密码的随机性。
以下是 AES_cbc_encrypt 函数的原型:
  1. void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);
复制代码

  • in:指向输入数据(明文)的指针。
  • out:指向输出数据(密文)的指针。
  • length:数据的长度,以字节为单位。
  • key:指向 AES_KEY 结构的指针,其中包含了加密密钥。
  • ivec:Initialization Vector(IV),用于增强密码的随机性,也是前一个密文块。在 CBC 模式中,IV 对于第一个数据块是必需的,之后的 IV 由前一个密文块决定。
  • enc:指定操作是加密(AES_ENCRYPT)还是解密(AES_DECRYPT)。
AES_set_decrypt_key 函数。该函数用于将加密时使用的密钥调整为解密时使用的密钥,以便进行解密操作。
以下是 AES_set_decrypt_key 函数的原型:
  1. int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
复制代码

  • userKey:指向用于设置解密密钥的输入密钥数据的指针。
  • bits:密钥长度,以比特为单位。支持的长度包括 128、192 和 256 比特。
  • key:指向 AES_KEY 结构的指针,该结构将存储设置后的解密密钥。
实现加解密功能,如下openssl_aes_cbc_encrypt用于使用CBC模式加密数据,openssl_aes_cbc_decrypt则相反用于解密数据。
  1. // 初始化密钥
  2. const unsigned char key[AES_BLOCK_SIZE] = { 0x12,0x55,0x64,0x69,0xf1 };
  3. // 初始化向量
  4. unsigned char iv[AES_BLOCK_SIZE] = { 0 };
  5. // AES CBC 模式加密
  6. // 参数:
  7. // - in: 待加密的数据
  8. // - len: 待加密数据的长度
  9. // - out: 存放加密结果的缓冲区
  10. // 返回值:
  11. // - 返回填充后加密数据的长度,失败返回-1
  12. int openssl_aes_cbc_encrypt(char* in, size_t len, char* out)
  13. {
  14.         AES_KEY aes;
  15.         // 填充数据为AES_BLOCK_SIZE的整数倍
  16.         char* aesIn;
  17.         int blockNum, aesInLen;
  18.         // 设置加密密钥
  19.         if (AES_set_encrypt_key(key, 128, &aes) < 0)
  20.         {
  21.                 return -1;
  22.         }
  23.         // 判断原始数据长度是否AES_BLOCK_SIZE的整数倍
  24.         if ((len % AES_BLOCK_SIZE) != 0)
  25.         {
  26.                 // 不是整数倍则用0填充
  27.                 blockNum = len / AES_BLOCK_SIZE + 1;
  28.                 aesInLen = blockNum * AES_BLOCK_SIZE;
  29.                 aesIn = (char*)calloc(aesInLen, 1);
  30.                 memcpy(aesIn, in, len);
  31.         }
  32.         else
  33.         {
  34.                 aesInLen = len;
  35.                 aesIn = (char*)calloc(aesInLen, 1);
  36.                 memcpy(aesIn, in, len);
  37.         }
  38.         // AES CBC 模式加密
  39.         AES_cbc_encrypt((unsigned char*)aesIn, (unsigned char*)out, aesInLen, &aes, iv, AES_ENCRYPT);
  40.         // 释放分配的内存
  41.         free(aesIn);
  42.         // 返回填充后加密数据的长度
  43.         return aesInLen;
  44. }
  45. // AES CBC 模式解密
  46. // 参数:
  47. // - in: 待解密的数据
  48. // - len: 待解密数据的长度
  49. // - out: 存放解密结果的缓冲区
  50. // 返回值:
  51. // - 成功返回0,失败返回-1
  52. int openssl_aes_cbc_decrypt(char* in, size_t len, char* out)
  53. {
  54.         AES_KEY aes;
  55.        
  56.         // 设置解密密钥
  57.         if (AES_set_decrypt_key(key, 128, &aes) < 0)
  58.         {
  59.                 return -1;
  60.         }
  61.         // AES CBC 模式解密
  62.         AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
  63.         // 返回成功
  64.         return 0;
  65. }
复制代码
当需要对数据加密时,首先打开被加密文件这里我们打开的时csdn.zip文件,加密后会写出为csdn.cbc文件;
[code]int main(int argc, char* argv[]){        // 存放填充字节数的数组        char offset[4] = { '0' };        char* src = nullptr, *dst = nullptr;        int inlen, outlen, size;        FILE* srcFile, *dstFile;        // 打开被加密源文件        srcFile = fopen("d://comp/csdn.zip", "rb");        // 加密后写出文件        dstFile = fopen("d://comp/csdn.cbc", "wb+");        // 获取文件大小        fseek(srcFile, 0, SEEK_END);        inlen = ftell(srcFile);        if (inlen < 0)        {                return 0;        }        fseek(srcFile, 0, SEEK_SET);        // -------------------------------------------------------        // 开始加密        src = (char*)calloc(inlen, 1);        size = fread(src, 1, inlen, srcFile);        std::cout
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美丽的神话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表