AES雜談

  1. 獲取加密數據

  2. 識別加密算法和模式: 我們需要確定使用了哪種加密算法(這里是 AES)以及具體的加密模式(例如 CBC, CTR, GCM, ECB 等)。這可能需要通過以下方式:

    • 元數據: 如果加密數據包含元數據(例如文件頭、協議字段),可能會直接指示使用的算法和模式
    • 分析數據結構: 如果數據結構是固定的(例如,鹽值固定長度,IV 固定長度),可以根據這些長度來推測可能的模式。
    • 嘗試和錯誤: 如果沒有明確指示,可能需要嘗試不同的 AES 模式進行解密。
    • 代碼分析: 如果可以獲取到加密程序的代碼,直接分析代碼是確定算法和模式最直接的方式
  3. 提取鹽值 (Salt): 如果加密數據中包含了鹽值,我們需要從數據中正確地提取出鹽值。這通常需要知道鹽值的長度和在數據中的位置 (如果存在的話)

  4. 提取 IV (Initialization Vector): 如果使用的加密模式需要 IV,并且 IV 包含在加密數據中,我們需要從數據中正確地提取出 IV。這需要知道 IV 的長度和在數據中的位置。IV 的長度取決于使用的 AES 模式 (ECB不需要iv)

  5. 確定密鑰派生函數 (KDF) 和參數: 我們需要知道加密時使用了哪種 KDF(例如 PBKDF2)以及相關的參數:

    • KDF 算法: 例如 PBKDF2。
    • 哈希函數: KDF 中使用的哈希函數(例如 SHA256)。
    • 迭代次數 (Iterations): 這是 KDF 的重要參數,影響派生密鑰的計算成本。
    • 派生密鑰的長度: AES 密鑰的長度(16, 24 或 32 字節(jié))。 這些信息可能存儲在元數據中,或者需要通過代碼分析或嘗試來確定
  6. 獲取用戶密碼: 這是最困難的一步。在用戶使用密碼進行加密的場景下,攻擊者需要獲取到用戶設置的原始密碼。這通常無法通過密碼學手段直接從密文或派生密鑰中獲取

  7. 使用獲取的信息進行解密: 一旦獲取了以下信息:

    • 用戶密碼
    • 鹽值
    • KDF 算法和參數(哈希函數、迭代次數、密鑰長度)
    • IV (如果需要)
    • AES 加密模式

    然后就可以按照以下步驟進行解密:

    • 使用獲取的密碼、鹽值、KDF 算法和參數重新派生出 AES 密鑰
    • 使用派生出的密鑰、獲取的 IV (如果需要) 和確定的 AES 模式,調用解密函數對密文進行解密

接下來直接上調試測試

(lldb) process status
Process 22883 stopped
* thread #1, name = 'commandxxx', stop reason = breakpoint 1.1
    frame #0: 0x00000055556ddd18 commandxxx`RAND_bytes
commandxxx`RAND_bytes:
->  0x55556ddd18 <+0>:  stp    x29, x30, [sp, #-0x20]!
    0x55556ddd1c <+4>:  stp    x20, x19, [sp, #0x10]
    0x55556ddd20 <+8>:  mov    x29, sp
    0x55556ddd24 <+12>: mov    w19, w1
(lldb) reg r x0 x1
      x0 = 0xb400007df52eb4b0 // -> 0x5555793b68
      x1 = 0x0000000000000010
      
從這里逆向思考可以猜測,這個函數可能是在生成鹽或者是生成隨機iv
所以可以記錄一下這個 x0地址,觀察一下 x/x -s1 -c0x30 $x0

識別加密算法:由上文我們可以知道加解密是在下面兩個函數

__owur int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx,
                                  const EVP_CIPHER *cipher, ENGINE *impl,
                                  const unsigned char *key,
                                  const unsigned char *iv);

__owur int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx,
                                  const EVP_CIPHER *cipher, ENGINE *impl,
                                  const unsigned char *key,
                                  const unsigned char *iv);
                                  
ctx : 加密上下文,它是一個不透明的結構體,用于存儲加密操作的狀態(tài)信息
cipher : 結構體定義了要使用的具體的加密算法和模式
impl : 參數允許您指定一個特定的 OpenSSL ENGINE 來執(zhí)行加密操作。ENGINE 是一種機制,允許 OpenSSL 使用硬件加速器或其他外部模塊來執(zhí)行加密任務
key : 加密的密鑰。密鑰的長度必須與 cipher 參數指定的算法模式所需的密鑰長度相匹配(例如,AES-256 需要 32 字節(jié)的密鑰)
iv : 加密的初始化向量 (IV), 對于不需要 IV 的模式(如 ECB),可以將此參數設置為 NULL

識別加密函數其實就是識別cipher,直接給這兩個函數上斷點

(lldb) process status
Process 27260 stopped
* thread #1, name = 'commandxxx', stop reason = breakpoint 2.1
    frame #0: 0x000000555562ba34 commandxxx`EVP_EncryptInit_ex
commandxxx`EVP_EncryptInit_ex:
->  0x555562ba34 <+0>: mov    w5, #0x1 ; =1 
    0x555562ba38 <+4>: b      0x555562aff8   ; EVP_CipherInit_ex

commandxxx`EVP_DecryptInit:
    0x555562ba3c <+0>: mov    w4, wzr
    0x555562ba40 <+4>: b      0x555562af98   ; EVP_CipherInit
(lldb) x/a -c5 -s8 $x1
0x55557635a0: 0x00000010000001aa
0x55557635a8: 0x0000000000000020
0x55557635b0: 0x0000000000001001
0x55557635b8: 0x00000055556265f8 commandxxx`aes_init_key
0x55557635c0: 0x000000555562685c commandxxx`aes_ecb_cipher

// 往下我們可以再去驗證一下其他加密形式是否可以用這個來進行判斷
(lldb) call (void*)EVP_aes_256_cbc()
(void *) $4 = 0x0000005555763548
(lldb) x/a -c5 -s8 0x0000005555763548
0x5555763548: 0x00000010000001ab
0x5555763550: 0x0000001000000020
0x5555763558: 0x0000000000001002
0x5555763560: 0x00000055556265f8 commandxxx`aes_init_key
0x5555763568: 0x0000005555626798 commandxxx`aes_cbc_cipher
(lldb) call (void*)EVP_aes_256_cfb128()
(void *) $5 = 0x0000005555763650
(lldb) x/a -c5 -s8 $5
0x5555763650: 0x00000001000001ad
0x5555763658: 0x0000001000000020
0x5555763660: 0x0000000000001003
0x5555763668: 0x00000055556265f8 commandxxx`aes_init_key
0x5555763670: 0x0000005555626960 commandxxx`aes_cfb_cipher
(lldb) call (void*)EVP_aes_256_cfb8()
(void *) $6 = 0x0000005555763700
(lldb) x/a -c5 -s8 $6
0x5555763700: 0x000000010000028f
0x5555763708: 0x0000001000000020
0x5555763710: 0x0000000000000003
0x5555763718: 0x00000055556265f8 commandxxx`aes_init_key
0x5555763720: 0x0000005555626b78 commandxxx`aes_cfb8_cipher

// 由此可見在cipher指針指向的內存便宜五個指針單位就存放著EVP接口底層的加密實現函數

當然這里從源碼中還看到一種方式來識別

std::cout << "Selected Cipher Mode: " << EVP_CIPHER_nid(cipher_mode) << " ("
     << OBJ_nid2ln(EVP_CIPHER_nid(cipher_mode)) << ")" << std::endl;
            
(lldb) reg r x0 x1 x2 x3 x4
      x0 = 0xb400007e73781090
      x1 = 0x00000055557635a0
      x2 = 0x0000000000000000
      x3 = 0xb400007e03778250
      x4 = 0x0000000000000000
(lldb) call (int)EVP_CIPHER_nid($x1)
(int) $3 = 426
(lldb) call (char*)OBJ_nid2ln(426)
(char *) $4 = 0x00000055555a7d39 "aes-256-ecb"

上述確定了加密算法,接下來就是去跟iv和key

(這里的學習使用的示例代碼比較簡單,ida引用一下就可以定位到這個函數)

int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
                      const unsigned char *salt, int saltlen, int iter,
                      const EVP_MD *digest, int keylen, unsigned char *out);
                      
pass : 用戶輸入的原始密碼
passlen : 密碼的長度(以字節(jié)為單位)
salt : 用于 PBKDF2 的鹽值 (Salt), 鹽值必須是隨機生成的
saltlen : 鹽值的長度(以字節(jié)為單位)
iter : PBKDF2 的迭代次數
digest : 結構體定義了 PBKDF2 中使用的 HMAC 哈希函數, 例如 EVP_md5()、EVP_sha1()、EVP_sha256()、EVP_sha512() 等
keylen : 希望派生出的密鑰的長度(以字節(jié)為單位), 這個長度應該與您要用于加密算法(如 AES)的密鑰長度相匹配
out : 輸出參數,用于存儲派生出的密鑰

(lldb) reg r x0 x1 x2 x3 x4 x5 x6 x7
      x0 = 0x0000007ffffffb69 // 堆上
      x1 = 0x0000000000000006
      x2 = 0xb400007df317c5b0 // 棧上
      x3 = 0x0000000000000010
      x4 = 0x00000000000186a0
      x5 = 0x000000555576a520
      x6 = 0x0000000000000020
      x7 = 0xb400007e03181580
(lldb) x/s $x0
0x7ffffffb69: "123123"
(lldb) x/x -c0x10 -s1 $x2
0x7df317c5b0: 0x9f 0x2e 0x09 0x92 0x40 0x98 0xe6 0xc2
0x7df317c5b8: 0x0f 0xb9 0xfa 0x60 0x2a 0xb9 0xe8 0x26
(lldb) p/u $x4
(unsigned long) 100000
(lldb) image lookup -a $x5 // 這里就是我們代碼里面寫的 const EVP_MD *digest = EVP_sha256();
      Address: commandxxx[0x0000000000215520] (commandxxx.PT_LOAD[1]..data.rel.ro + 30056)
      Summary: commandxxx`sha256_md

然后這里順帶去看看源碼中 case 其他的隨機iv的實現

  • 基于時間
    std::chrono::steady_clock::now() → clock_gettime

  • 基于偽隨機數生成器

    strcpy(__token._*r*._*value*.__s._*data*, "/dev/urandom")

    std::random_device::random_device(v36, &__token);

    v5 = __open_2(data, 0LL);

…… 還有更多的實現方式歡迎補充 ……

往下就是加密流程

__owur int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
                                 int *outl, const unsigned char *in, int inl);
ctx : 加密上下文
out : 輸出參數,用于存儲加密后的密文數據
outl :輸出參數,用于存儲實際寫入到 out 緩沖區(qū)中的密文數據的長度(以字節(jié)為單位)
in :加密的明文數據
inl :加密的明文數據的長度(以字節(jié)為單位)
EVP_EncryptUpdate 函數返回一個 int 值, 成功 1 失敗 0

* thread #1, name = 'commandxxx', stop reason = breakpoint 5.1
    frame #0: 0x000000555562b420 commandxxx`EVP_EncryptUpdate
commandxxx`EVP_EncryptUpdate:
->  0x555562b420 <+0>:  stp    x29, x30, [sp, #-0x10]!
    0x555562b424 <+4>:  mov    x29, sp
    0x555562b428 <+8>:  ldr    w8, [x0, #0x10]
    0x555562b42c <+12>: cbz    w8, 0x555562b438 ; <+24>
    
斷點EVP_CipherUpdate然后直接去看x1和x2的地址記下來
(這里有坑,RAND_bytes函數也會瘋狂調用EVP_EncryptUpdate,注意區(qū)分?。?
(lldb) reg read x1 x2
      x1 = 0xb400007e432a7cc0
      x2 = 0x0000007ffffffa3c
      
跳出這個函數

    0x55556dc840 <+216>:  add    x3, sp, #0x40
    0x55556dc844 <+220>:  mov    w4, w22
    0x55556dc848 <+224>:  bl     0x555562b410   ; EVP_CipherUpdate
->  0x55556dc84c <+228>:  mov    w8, w0
    0x55556dc850 <+232>:  mov    w0, wzr

再去看到之前記下來的x1(密文) x2(長度) 下文即是密文
(lldb) x/d 0x0000007ffffffa3c
0x7ffffffa3c: 64
(lldb) x/x -c48 -s1 0xb400007e432a7cc0
0x7e432a7cc0: 0xaf 0x64 0xc6 0x3a 0x00 0xbb 0x6f 0x62
0x7e432a7cc8: 0x8e 0x9e 0xf8 0x52 0x98 0x92 0xec 0x12
0x7e432a7cd0: 0x6e 0x09 0x4e 0x5a 0xd1 0x9a 0x75 0xa0
0x7e432a7cd8: 0x67 0x4f 0x91 0x84 0x5c 0xf4 0x36 0xc3
0x7e432a7ce0: 0x84 0x2d 0x0a 0x0e 0xea 0x84 0xea 0x6b
0x7e432a7ce8: 0x6e 0x3b 0x8c 0x08 0xbe 0x0c 0x91 0x0c

這一步還沒完成 還有最后一步 分組的填充
tips:即使數據長度剛好是分組大小的整數倍,通常情況下仍然需要調用 EVP_EncryptFinal_ex,OpenSSL 默認使用 PKCS7 填充。PKCS7 填充的規(guī)則是,即使明文長度已經是分組大小的整數倍,也會添加一個完整的填充塊

__owur int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,
                                   int *outl);
ctx : 加密上下文
out : 輸出參數,用于存儲最后生成的密文數據
outl : 輸出參數,用于存儲實際寫入到 out 緩沖區(qū)中的密文數據的長度(以字節(jié)為單位)

* thread #1, name = 'commandxxx', stop reason = breakpoint 7.1
    frame #0: 0x000000555562b738 commandxxx`EVP_EncryptFinal_ex
commandxxx`EVP_EncryptFinal_ex:
->  0x555562b738 <+0>:  stp    x29, x30, [sp, #-0x40]!
    0x555562b73c <+4>:  str    x23, [sp, #0x10]
    0x555562b740 <+8>:  stp    x22, x21, [sp, #0x20]
    0x555562b744 <+12>: stp    x20, x19, [sp, #0x30]
    
還是一樣的記下 x1 x2
(lldb) reg r x1 x2
      x1 = 0xb400007e432a7d00
      x2 = 0x0000007ffffffa3c

然后跳出函數

(lldb) x/d 0x0000007ffffffa3c
0x7ffffffa3c: 16
(lldb) x/x -c16 -s1 0xb400007e432a7d00
0x7e432a7d00: 0x0c 0x36 0xbd 0x3b 0xf0 0xd1 0x67 0x82
0x7e432a7d08: 0x3f 0x5f 0xc5 0x8b 0x63 0x1c 0x95 0x1c

拼接起來或者是直接從x2得到完整的密文 (64+16=80)
(lldb) x/x -c80 -s1 0xb400007e432a7d00-64
0x7e432a7cc0: 0xaf 0x64 0xc6 0x3a 0x00 0xbb 0x6f 0x62
0x7e432a7cc8: 0x8e 0x9e 0xf8 0x52 0x98 0x92 0xec 0x12
0x7e432a7cd0: 0x6e 0x09 0x4e 0x5a 0xd1 0x9a 0x75 0xa0
0x7e432a7cd8: 0x67 0x4f 0x91 0x84 0x5c 0xf4 0x36 0xc3
0x7e432a7ce0: 0x84 0x2d 0x0a 0x0e 0xea 0x84 0xea 0x6b
0x7e432a7ce8: 0x6e 0x3b 0x8c 0x08 0xbe 0x0c 0x91 0x0c
0x7e432a7cf0: 0x81 0xda 0x46 0xd7 0xb1 0x1b 0x2b 0xa4
0x7e432a7cf8: 0x32 0x26 0x42 0x02 0x82 0xc3 0xdb 0x3a
// 上面這部分是 x2 往上回推 64 字節(jié)的數據,也就是第一部分加密的結果
0x7e432a7d00: 0x0c 0x36 0xbd 0x3b 0xf0 0xd1 0x67 0x82
0x7e432a7d08: 0x3f 0x5f 0xc5 0x8b 0x63 0x1c 0x95 0x1c

當然這一切都是介于由openssl的符號,沒有話也有別的辦法讓它有,這里就不細說了,然后解密不多說了,和加密就差不多一樣的 …


最后是測試代碼,有興趣的可以自己編譯玩玩

#include <algorithm>
#include <chrono> // 用于基于時間的 IV 生成 (不安全)
#include <iostream>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h> // 仍然包含,但我們將展示不使用它的方法
#include <openssl/sha.h>
#include <random> // 用于偽隨機 IV 生成 (不安全)
#include <string>
#include <vector>

// 函數用于使用 PBKDF2 從密碼和鹽值派生密鑰 (與之前相同)
bool derive_key_from_password(const std::string &password,
                              const std::vector<unsigned char> &salt,
                              size_t key_length, int iterations,
                              std::vector<unsigned char> &derived_key) {
  derived_key.resize(key_length);
  const EVP_MD *digest = EVP_sha256();

  if (PKCS5_PBKDF2_HMAC(password.c_str(), password.length(), salt.data(),
                        salt.size(), iterations, digest, derived_key.size(),
                        derived_key.data()) != 1) {
    std::cerr << "Error: Failed to derive key from password." << std::endl;
    ERR_print_errors_fp(stderr);
    return false;
  }
  return true;
}

// 函數用于生成 IV (多種方式,不推薦非隨機方式)
std::vector<unsigned char> generate_iv(size_t iv_size, int method = 0) {
  std::vector<unsigned char> iv(iv_size);

  switch (method) {
  case 0: // 推薦方式: 使用密碼學安全的隨機數生成器
    if (RAND_bytes(iv.data(), iv.size()) != 1) {
      std::cerr << "Warning: Failed to generate random IV using RAND_bytes. "
                   "Using fallback (unsafe)."
                << std::endl;
      // Fallback to a less secure method if RAND_bytes fails (should not happen
      // in a healthy system) This fallback is for demonstration only and should
      // be avoided in production.
      method = 1; // Use time-based fallback
    } else {
      std::cout << "Generated IV using RAND_bytes (Recommended)." << std::endl;
      return iv;
    }
    // Fallthrough to fallback if RAND_bytes failed
  case 1: // 不安全方式: 基于當前時間生成 IV
    // **警告:** 基于時間的 IV 是可預測的,非常不安全!
    {
      auto now = std::chrono::high_resolution_clock::now();
      auto duration = now.time_since_epoch();
      auto nanoseconds =
          std::chrono::duration_cast<std::chrono::nanoseconds>(duration)
              .count();
      // 將時間戳的字節(jié)復制到 IV 中
      size_t copy_size = std::min(iv_size, sizeof(nanoseconds));
      std::copy(reinterpret_cast<const unsigned char *>(&nanoseconds),
                reinterpret_cast<const unsigned char *>(&nanoseconds) +
                    copy_size,
                iv.begin());
      // 剩余部分填充 0
      std::fill(iv.begin() + copy_size, iv.end(), 0);
      std::cerr << "Warning: Generated IV based on time (UNSAFE!)."
                << std::endl;
    }
    break;
  case 2: // 不安全方式: 基于偽隨機數生成器生成 IV
    // **警告:** 偽隨機數生成器不是密碼學安全的,生成的 IV 是可預測的!
    {
      std::mt19937 rng(std::random_device{}()); // 使用硬件熵源初始化
      std::uniform_int_distribution<unsigned int> dist(0, 255);
      for (size_t i = 0; i < iv_size; ++i) {
        iv[i] = static_cast<unsigned char>(dist(rng));
      }
      std::cerr << "Warning: Generated IV using pseudo-random number generator "
                   "(UNSAFE!)."
                << std::endl;
    }
    break;
  case 3: // 不安全方式: 固定 IV (最不安全!)
    // **警告:** 固定 IV 會導致 IV 重復使用,非常不安全!
    {
      std::fill(iv.begin(), iv.end(), 0x00); // 例如,全部填充 0
      std::cerr << "Warning: Using a fixed IV (MOST UNSAFE!)." << std::endl;
    }
    break;
  default:
    std::cerr << "Error: Invalid IV generation method specified. Using default "
                 "(RAND_bytes)."
              << std::endl;
    return generate_iv(iv_size, 0); // Fallback to recommended method
  }

  return iv;
}

// 函數用于執(zhí)行 AES 加密,并將鹽值和 IV 附加到密文前面
// 參數:
//   plaintext: 要加密的明文
//   password: 用戶輸入的密碼
//   salt: 用于 PBKDF2 的鹽值 (必須是隨機且唯一的)
//   iterations: PBKDF2 的迭代次數
//   cipher_mode: 選擇的 AES 加密模式 (例如 EVP_aes_256_cbc())
//   iv_generation_method: IV 生成方式 (0: RAND_bytes, 1: Time, 2:
//   Pseudo-random, 3: Fixed) encrypted_data: 用于存儲包含鹽值、IV
//   和密文的完整數據 (輸出參數)
// 返回值:
//   成功返回 true,失敗返回 false
bool aes_encrypt_with_salt_and_iv(const std::string &plaintext,
                                  const std::string &password,
                                  const std::vector<unsigned char> &salt,
                                  int iterations, const EVP_CIPHER *cipher_mode,
                                  int iv_generation_method,
                                  std::vector<unsigned char> &encrypted_data) {
  size_t aes_key_length =
      EVP_CIPHER_key_length(cipher_mode); // 根據模式獲取密鑰長度

  // 1. 從密碼和鹽值派生 AES 密鑰
  std::vector<unsigned char> derived_key;
  if (!derive_key_from_password(password, salt, aes_key_length, iterations,
                                derived_key)) {
    std::cerr << "Failed to derive key from password during encryption."
              << std::endl;
    return false;
  }

  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  if (!ctx) {
    std::cerr << "Error: Failed to create EVP_CIPHER_CTX." << std::endl;
    return false;
  }

  size_t iv_size = EVP_CIPHER_iv_length(cipher_mode);
  std::vector<unsigned char> iv;

  // 對于不需要 IV 的模式 (如 ECB),IV 大小為 0
  if (iv_size > 0) {
    // 2. 生成 IV
    iv = generate_iv(iv_size, iv_generation_method);
    if (iv.empty() &&
        iv_size > 0) { // Check if IV generation failed for non-ECB modes
      EVP_CIPHER_CTX_free(ctx);
      return false;
    }
  } else {
    // ECB 模式不需要 IV
    std::cout << "Using a cipher mode that does not require an IV (e.g., ECB)."
              << std::endl;
  }

  // 初始化加密操作
  // 注意:對于不需要 IV 的模式,EVP_EncryptInit_ex 的 iv 參數可以為 nullptr
  if (EVP_EncryptInit_ex(ctx, cipher_mode, nullptr, derived_key.data(),
                         (iv_size > 0 ? iv.data() : nullptr)) != 1) {
    std::cerr << "Error: Failed to initialize encryption." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }

  // 加密明文
  int len;
  int ciphertext_len;
  std::vector<unsigned char> ciphertext(plaintext.size() +
                                        EVP_CIPHER_block_size(cipher_mode));

  if (EVP_EncryptUpdate(ctx, ciphertext.data(), &len,
                        (const unsigned char *)plaintext.data(),
                        plaintext.size()) != 1) {
    std::cerr << "Error: Failed to encrypt data." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }
  ciphertext_len = len;

  if (EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len) != 1) {
    std::cerr << "Error: Failed to finalize encryption." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }
  ciphertext_len += len;
  ciphertext.resize(ciphertext_len);

  // 清理上下文
  EVP_CIPHER_CTX_free(ctx);

  // 將鹽值、IV (如果存在) 附加到密文前面
  encrypted_data.resize(salt.size() + iv.size() + ciphertext.size());
  std::copy(salt.begin(), salt.end(), encrypted_data.begin());
  if (iv_size > 0) {
    std::copy(iv.begin(), iv.end(), encrypted_data.begin() + salt.size());
  }
  std::copy(ciphertext.begin(), ciphertext.end(),
            encrypted_data.begin() + salt.size() + iv.size());

  return true;
}

// 函數用于執(zhí)行 AES 解密,從數據塊中提取鹽值、IV 和密文
// 參數:
//   encrypted_data: 包含鹽值、IV 和密文的完整數據
//   password: 用戶輸入的密碼
//   iterations: PBKDF2 的迭代次數 (與加密時相同)
//   cipher_mode: 選擇的 AES 加密模式 (與加密時相同)
//   plaintext: 用于存儲解密后的明文 (輸出參數)
// 返回值:
//   成功返回 true,失敗返回 false
bool aes_decrypt_with_salt_and_iv(
    const std::vector<unsigned char> &encrypted_data,
    const std::string &password, int iterations, const EVP_CIPHER *cipher_mode,
    std::string &plaintext) {
  size_t aes_key_length =
      EVP_CIPHER_key_length(cipher_mode); // 根據模式獲取密鑰長度
  size_t salt_size = 16;                  // 與加密時相同的鹽值大小

  if (encrypted_data.size() < salt_size) {
    std::cerr << "Error: Encrypted data is too short to contain salt."
              << std::endl;
    return false;
  }

  // 1. 從數據塊中提取鹽值
  std::vector<unsigned char> salt(encrypted_data.begin(),
                                  encrypted_data.begin() + salt_size);

  // 2. 從密碼和提取出的鹽值派生 AES 密鑰
  std::vector<unsigned char> derived_key;
  if (!derive_key_from_password(password, salt, aes_key_length, iterations,
                                derived_key)) {
    std::cerr << "Failed to derive key from password during decryption."
              << std::endl;
    return false;
  }

  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  if (!ctx) {
    std::cerr << "Error: Failed to create EVP_CIPHER_CTX." << std::endl;
    return false;
  }

  size_t iv_size = EVP_CIPHER_iv_length(cipher_mode);
  std::vector<unsigned char> iv;
  size_t data_offset = salt_size;

  // 對于需要 IV 的模式,從數據中提取 IV
  if (iv_size > 0) {
    if (encrypted_data.size() < salt_size + iv_size) {
      std::cerr << "Error: Encrypted data is too short to contain IV."
                << std::endl;
      EVP_CIPHER_CTX_free(ctx);
      return false;
    }
    // 3. 從數據塊中提取 IV
    iv.assign(encrypted_data.begin() + salt_size,
              encrypted_data.begin() + salt_size + iv_size);
    data_offset += iv_size;
  } else {
    // ECB 模式不需要 IV
    std::cout << "Using a cipher mode that does not require an IV (e.g., ECB) "
                 "for decryption."
              << std::endl;
  }

  // 提取密文
  if (encrypted_data.size() < data_offset) {
    std::cerr << "Error: Encrypted data is too short to contain ciphertext."
              << std::endl;
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }
  std::vector<unsigned char> ciphertext(encrypted_data.begin() + data_offset,
                                        encrypted_data.end());

  // 初始化解密操作
  // 注意:對于不需要 IV 的模式,EVP_DecryptInit_ex 的 iv 參數可以為 nullptr
  if (EVP_DecryptInit_ex(ctx, cipher_mode, nullptr, derived_key.data(),
                         (iv_size > 0 ? iv.data() : nullptr)) != 1) {
    std::cerr << "Error: Failed to initialize decryption." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }

  // 解密密文
  int len;
  int plaintext_len;
  std::vector<unsigned char> plaintext_bytes(
      ciphertext.size() + EVP_CIPHER_block_size(cipher_mode));

  if (EVP_DecryptUpdate(ctx, plaintext_bytes.data(), &len, ciphertext.data(),
                        ciphertext.size()) != 1) {
    std::cerr << "Error: Failed to decrypt data." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }
  plaintext_len = len;

  if (EVP_DecryptFinal_ex(ctx, plaintext_bytes.data() + len, &len) != 1) {
    std::cerr << "Error: Failed to finalize decryption." << std::endl;
    ERR_print_errors_fp(stderr);
    EVP_CIPHER_CTX_free(ctx);
    return false;
  }
  plaintext_len += len;
  plaintext.assign(plaintext_bytes.begin(),
                   plaintext_bytes.begin() + plaintext_len);

  // 清理上下文
  EVP_CIPHER_CTX_free(ctx);

  return true;
}

// 輔助函數:將字節(jié)向量轉換為十六進制字符串 (與之前相同)
std::string bytes_to_hex(const std::vector<unsigned char> &bytes) {
  std::string hex_string;
  const char hex_chars[] = "0123456789abcdef";
  for (unsigned char byte : bytes) {
    hex_string += hex_chars[(byte >> 4) & 0x0F];
    hex_string += hex_chars[byte & 0x0F];
  }
  return hex_string;
}

int main() {
  // 示例用法

  // 1. 獲取用戶輸入的密碼
  std::string user_password;
  std::cout << "Enter your password: ";
  // 注意:在實際應用中,應該使用更安全的方式獲取密碼,例如禁用回顯
  std::cin >> user_password;

  // 2. 生成一個隨機的鹽值 (Salt)
  std::vector<unsigned char> salt(16); // 通常使用 16 字節(jié)的鹽值
  if (RAND_bytes(salt.data(), salt.size()) != 1) {
    std::cerr << "Error: Failed to generate random salt." << std::endl;
    return 1;
  }
  std::cout << "Generated Salt (hex): " << bytes_to_hex(salt) << std::endl;

  // 3. 定義 PBKDF2 迭代次數
  int pbkdf2_iterations = 100000; // 迭代次數

  // 4. 要加密的明文
  std::string plaintext =
      "This is a secret message that needs to be encrypted using a password.";
  std::cout << "Original Plaintext: " << plaintext << std::endl;

  // 5. 選擇 AES 加密模式
  // 您可以在這里選擇不同的模式:
  // const EVP_CIPHER* cipher_mode = EVP_aes_256_cbc(); // AES-256-CBC (需要 IV)
  // const EVP_CIPHER* cipher_mode = EVP_aes_256_ctr(); // AES-256-CTR (需要 IV)
  // const EVP_CIPHER* cipher_mode = EVP_aes_256_gcm(); // AES-256-GCM (需要
  // IV,AEAD 模式)
  const EVP_CIPHER *cipher_mode =
      EVP_aes_256_ecb(); // AES-256-ECB (不需要 IV,不安全!)

  if (!cipher_mode) {
    std::cerr << "Error: Failed to select cipher mode." << std::endl;
    return 1;
  }
  std::cout << "Selected Cipher Mode: " << EVP_CIPHER_nid(cipher_mode) << " ("
            << OBJ_nid2ln(EVP_CIPHER_nid(cipher_mode)) << ")" << std::endl;

  // 6. 選擇 IV 生成方式 (僅用于演示不安全方式,實際應用請使用 0)
  // int iv_generation_method = 0; // 推薦: RAND_bytes
  // int iv_generation_method = 1; // 不安全: Time-based
  // int iv_generation_method = 2; // 不安全: Pseudo-random
  int iv_generation_method =
      3; // 最不安全: Fixed IV (僅用于演示 ECB 模式下 IV 大小為 0 的情況)

  // 7. 存儲包含鹽值、IV 和密文的完整加密數據
  std::vector<unsigned char> encrypted_data;

  // 8. 執(zhí)行加密 (使用密碼、鹽值、模式和 IV 生成方式)
  if (aes_encrypt_with_salt_and_iv(plaintext, user_password, salt,
                                   pbkdf2_iterations, cipher_mode,
                                   iv_generation_method, encrypted_data)) {
    std::cout << "Encryption successful." << std::endl;
    std::cout << "Encrypted Data (Salt + IV + Ciphertext, hex): "
              << bytes_to_hex(encrypted_data) << std::endl;

    // 9. 存儲解密后的明文
    std::string decrypted_plaintext;

    // 10. 執(zhí)行解密 (使用相同的密碼、迭代次數和模式,從數據中提取鹽值和 IV)
    std::cout << "\nEnter password again for decryption: ";
    std::string decryption_password;
    std::cin >> decryption_password;

    if (aes_decrypt_with_salt_and_iv(encrypted_data, decryption_password,
                                     pbkdf2_iterations, cipher_mode,
                                     decrypted_plaintext)) {
      std::cout << "Decryption successful." << std::endl;
      std::cout << "Decrypted Plaintext: " << decrypted_plaintext << std::endl;

      // 11. 驗證解密后的明文是否與原始明文一致
      if (plaintext == decrypted_plaintext) {
        std::cout << "Verification successful: Decrypted text matches original."
                  << std::endl;
      } else {
        std::cerr
            << "Verification failed: Decrypted text does not match original."
            << std::endl;
      }
    } else {
      std::cerr << "Decryption failed." << std::endl;
    }
  } else {
    std::cerr << "Encryption failed." << std::endl;
  }

  return 0;
}

// 02c9a5266522f0a1cd682ffa8069d319
// 1d5bc3969ecde06e8a74f4f7b45dd84a1ab847bdbc9828a74a5fca2d62b38b754b38d9fffc1d63c8056d38d6fd94d4636e596bb86933e2e2f4324f74b7772ca22c4962c0e16904ed9cc0395a7aecd583
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容