20.3 OpenSSL 對稱AES加解密演算法

lyshark發表於2023-10-30

AES演算法是一種對稱加密演算法,全稱為高階加密標準(Advanced Encryption Standard)。它是一種分組密碼,以128位元為一個分組進行加密,其金鑰長度可以是128位元、192位元或256位元,因此可以提供不同等級的安全性。該演算法採用了替代、置換和混淆等技術,以及多輪加密和金鑰擴充套件等機制,使得其加密效果優秀,安全性高,被廣泛應用於各種領域中,如資料加密、檔案加密、網路安全等。

AES演算法加密和解密使用的金鑰是相同的,該演算法加密和解密速度較快,適用於對大量資料進行加密解密的場景。在實際應用中,通常採用混合加密方式,即使用RSA演算法加密對稱加密演算法中的金鑰,再使用對稱加密演算法加密資料,以保證資料的機密性和加密解密的效率。

AES演算法常用兩種加密模式,即CBC和ECB模式,它們分別具有不同的優缺點。

  • ECB(Electronic Codebook,電子密碼本)模式是最簡單的分組密碼工作模式,將每個明文塊獨立加密,同樣的金鑰加密同樣的明文塊得到的密文也是一樣的,因此容易被攻擊者利用重複的密文進行分析破解。ECB模式加密效率高,適用於短報文加密,但不適用於長報文加密。

  • CBC(Cipher Block Chaining,密碼塊鏈)模式是一種分組密碼工作模式,先將明文分組,然後對每個分組進行加密,加密時使用上一塊密文作為輸入,因此相同的明文塊在不同位置上得到的密文是不同的,可以防止被攻擊者利用重複的密文進行分析破解。CBC模式加密效率較低,但適用於長報文加密,因為不同的明文塊之間互相影響,增加了安全性。

在實際應用中,通常採用CBC模式進行加密,因為它比ECB模式更安全,但加密效率較低。此外,還有其他的加密模式,如CFB、OFB、CTR等,不同的加密模式適用於不同的場景,需要根據實際需求進行選擇。

OpenSSL庫提供了對AES加密的支援,但在使用時讀者還是需要自行封裝一些通用加解密函式,如下程式碼片段是筆者常用的一些函式總結,其中aes_cbc_encrypt函式用於使用CBC模式對特定字串加密,aes_cbc_decrypt則使用CBC模式對字串進行解密,第二個函式AES函式則是使用OpenSSL庫預設的加解密函式二次封裝實現的。

#include <iostream>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
#include <openssl/pem.h>

extern "C"
{
#include <openssl/applink.c>
}

#pragma comment(lib,"libssl_static.lib")
#pragma comment(lib,"libcrypto.lib")

// CBC模式加密
int aes_cbc_encrypt(char* in, char* key, char* out)
{
    if (!in || !key || !out)
        return 0;

    unsigned char iv[AES_BLOCK_SIZE];
    for (int i = 0; i < AES_BLOCK_SIZE; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    int len = strlen(in);
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
    return 1;
}

// CBC模式解密
int aes_cbc_decrypt(char* in, char* key, char* out)
{
    if (!in || !key || !out)
        return 0;

    // 加密的初始化向量
    unsigned char iv[AES_BLOCK_SIZE];

    // iv一般設定為全0
    for (int i = 0; i < AES_BLOCK_SIZE; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    int len = strlen(in);
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    return 1;
}

// 將加密與解密整合在一起
void AES(unsigned char* InBuff, unsigned char* OutBuff, unsigned char* key, char* Type)
{
    if (strcmp(Type, "encode") == 0)
    {
        AES_KEY AESEncryptKey;
        AES_set_encrypt_key(key, 256, &AESEncryptKey);
        AES_encrypt(InBuff, OutBuff, &AESEncryptKey);
    }
    else if (strcmp(Type, "decode") == 0)
    {
        AES_KEY AESDecryptKey;
        AES_set_decrypt_key(key, 256, &AESDecryptKey);
        AES_decrypt(InBuff, OutBuff, &AESDecryptKey);
    }
}

有了上述演算法封裝,接下來筆者將依次演示這幾種不同的加密函式是如何被應用的,首先簡單介紹一下aes_cbc_encryptaes_cbc_decrypt這兩個函式都是自己封裝的AES加解密演算法,這兩個演算法引數傳遞保持一致,第一個引數都是指定需要加密的緩衝區,第二個引數則是指定加密所使用的key,第三個引數是處理後的結果。

int main(int argc, char* argv[])
{
  char szBuffer[1024] = "hello lyshark";
  char szDst[1024] = { 0 };
  char szSrc[1024] = { 0 };

  // 計算一串金鑰
  char key[AES_BLOCK_SIZE] = { 0 };
  for (int x = 0; x < AES_BLOCK_SIZE; x++)
  {
    key[x] = 32 + x;
  }

  // AES加密
  if (aes_cbc_encrypt(szBuffer, key, szDst) != 0)
  {
    std::cout << "加密後長度: " << strlen(szDst) << std::endl;
  }

  // AES解密
  if (aes_cbc_decrypt(szDst, key, szSrc) != 0)
  {
    std::cout << "解密內容: " << szSrc << std::endl;
  }

  system("pause");
  return 0;
}

上述程式碼片段則是透過AES實現對資料加解密處理的功能,如下是這段程式碼的輸出效果;

第二種呼叫方式是採用API實現,其中的AES函式,透過AES_set_encrypt_key設定加密金鑰,並直接呼叫AES_encrypt實現資料加密,反之,透過AES_set_decrypt_key設定解密金鑰,並呼叫AES_decrypt解密,這段程式碼呼叫方式如下所示;

int main(int argc, char* argv[])
{
  unsigned char Buffer[1024] = "hello lyshark";
  unsigned char EncodeBuf[1024] = { 0 };
  unsigned char DecodeBuf[1024] = { 0 };
  unsigned char aes_key[32] = { 0 };

  // 隨機生成金鑰
  for (int x = 0; x < 32; x++)
  {
    int ch = rand() % 5;
    aes_key[x] = (char)ch;
  }

  AES(Buffer, EncodeBuf, (unsigned char *)aes_key, (char*)"encode");
  std::cout << "加密資料長度: " << strlen((char *)EncodeBuf) << std::endl;

  AES(EncodeBuf, DecodeBuf, (unsigned char*)aes_key, (char*)"decode");
  std::cout << "解密資料: " << DecodeBuf << std::endl;

  system("pause");
  return 0;
}

如上程式碼,透過呼叫AES函式時,傳入encode實現資料加密,傳入decode實現資料解密,如下圖所示;

相關文章