C++ CryptoPP使用AES加解密

来源:https://www.cnblogs.com/LyShark/archive/2023/11/30/17866880.html
-Advertisement-
Play Games

Crypto++ (CryptoPP) 是一個用於密碼學和加密的 C++ 庫。它是一個開源項目,提供了大量的密碼學演算法和功能,包括對稱加密、非對稱加密、哈希函數、消息認證碼 (MAC)、數字簽名等。Crypto++ 的目標是提供高性能和可靠的密碼學工具,以滿足軟體開發中對安全性的需求。高級加密標準(... ...


Crypto++ (CryptoPP) 是一個用於密碼學和加密的 C++ 庫。它是一個開源項目,提供了大量的密碼學演算法和功能,包括對稱加密、非對稱加密、哈希函數、消息認證碼 (MAC)、數字簽名等。Crypto++ 的目標是提供高性能和可靠的密碼學工具,以滿足軟體開發中對安全性的需求。

高級加密標準(Advanced Encryption Standard,AES)是一種對稱密鑰加密標準,用於保護電腦上的敏感數據。AES是由美國國家標準與技術研究院(NIST)於2001年確定的,它取代了過時的數據加密標準(Data Encryption Standard,DES)。

以下是AES加密演算法的主要特點和概述:

  1. 對稱密鑰演算法: AES是一種對稱密鑰演算法,意味著相同的密鑰用於加密和解密數據。這就要求通信雙方在通信前共用密鑰,並確保其保密性。
  2. 分組密碼: AES將明文數據分成固定大小的塊(128比特),然後對每個塊進行獨立的加密。這個固定大小的塊稱為分組。AES支持多種分組長度,包括128比特、192比特和256比特。
  3. 輪數: AES加密演算法的安全性與其輪數相關。輪數表示對數據塊的處理迴圈次數,不同密鑰長度的AES使用不同數量的輪數。通常,128比特密鑰使用10輪,192比特密鑰使用12輪,256比特密鑰使用14輪。
  4. 密鑰長度: AES支持多種密鑰長度,包括128比特、192比特和256比特。密鑰長度的選擇直接影響加密演算法的安全性。
  5. SubBytes、ShiftRows、MixColumns和AddRoundKey: 這些是AES加密演算法中的四個主要操作,它們通過多輪迭代來加密數據。SubBytes和ShiftRows引入非線性性,MixColumns和AddRoundKey提供了擴散和混淆。
  6. 強安全性: AES被廣泛認為是一種安全、可靠的加密演算法。它經過廣泛的密碼分析和評估,並且在許多應用中得到了廣泛的應用,包括加密通信、文件加密和硬體加密。

總體而言,AES是一種高效、安全且廣泛應用的加密演算法,適用於多種應用場景。其在加密強度和性能之間取得了良好的平衡,因此成為許多信息安全應用的首選演算法。

使用AES演算法

AES(Advanced Encryption Standard)廣泛應用於保護敏感數據的加密和解密過程。以下是AES演算法的概述:

1. 對稱加密演算法:

AES是一種對稱加密演算法,這意味著加密和解密都使用相同的密鑰。密鑰是保護數據安全的關鍵,因此對稱加密演算法需要確保密鑰的安全分發和管理。

2. 密鑰長度:

AES支持不同長度的密鑰,包括128位、192位和256位。密鑰長度越長,通常意味著更高的安全性,但也可能導致加密和解密的計算成本增加。

3. 塊加密演算法:

AES是塊加密演算法,它按照固定大小的數據塊(128位)進行加密。加密和解密的過程都是對這些數據塊的操作。

4. 加解密過程:

加密:

  • 數據分塊:將明文分成固定大小的數據塊(128位)。
  • 初始輪密鑰加:將明文和初始密鑰進行一次簡單的混淆操作。
  • 輪加密:通過多輪的替代和置換操作(SubBytes、ShiftRows、MixColumns、AddRoundKey),對數據塊進行混淆。
  • 最終輪:在最後一輪中,省略MixColumns操作。
  • 得到密文。

解密:

  • 初始輪密鑰解:將密文和初始密鑰進行一次簡單的混淆操作。
  • 輪解密:通過多輪的逆操作(InvSubBytes、InvShiftRows、InvMixColumns、AddRoundKey),對數據塊進行逆操作。
  • 最終輪:在最後一輪中,省略InvMixColumns操作。
  • 得到明文。

5. 使用場景:

AES廣泛用於保護敏感數據,如文件、資料庫、網路通信等。它是許多安全協議和標準的基礎,包括TLS(安全套接層)、IPsec(Internet協議安全)等。

6. 安全性:

AES被廣泛接受並認為是安全可靠的加密演算法。密鑰長度的選擇對安全性至關重要,一般建議使用128位、192位或256位的密鑰以滿足特定安全需求。

總體而言,AES作為一種高效且安全的對稱加密演算法,在現代加密通信中扮演著重要的角色。AES的使用需要引入頭文件#include <aes.h>其他部分與《C++ 通過CryptoPP計算Hash值》文章中的頭文件引入保持一致。

如下AESEncrypt是一個使用AES演算法進行加密的函數。下麵是對函數的主要步驟的註釋:

  1. AES加密對象初始化:
    • 創建AESEncryption對象用於AES加密。
    • 定義AES加密需要的數據塊:inBlock(輸入數據塊)、outBlock(輸出數據塊)、xorBlock(異或數據塊)。
  2. 計算加密數據塊大小:
    • 計算需要的加密數據塊數量,考慮到原始數據大小可能不是AES塊大小的整數倍。
  3. 分配加密後的數據緩衝區:
    • 根據計算得到的加密數據塊大小分配記憶體。
  4. 設置AES加密密鑰:
    • 調用SetKey函數設置AES加密密鑰。
  5. AES加密過程:
    • 迴圈處理原始數據塊,每次處理一個AES塊大小的數據。
    • 將原始數據塊拷貝到輸入數據塊。
    • 使用AES演算法進行加密。
    • 將加密後的數據塊拷貝到輸出緩衝區。
  6. 返回加密結果:
    • 返回加密後的數據緩衝區和大小。

請註意,在實際使用中,要確保釋放了分配的記憶體,以防止記憶體泄漏。

BOOL AESEncrypt(BYTE *pOriginalData, DWORD dwOriginalDataSize, BYTE *pAESKey, DWORD dwAESKeySize, BYTE **ppEncryptData, DWORD *pdwEncryptData)
{
	// 定義AES加密需要的數據塊
	AESEncryption aesEncryptor;
	// 加密原文數據塊
	unsigned char inBlock[AES::BLOCKSIZE];
	// 加密後密文數據塊
	unsigned char outBlock[AES::BLOCKSIZE];
	// 必須設定全為0
	unsigned char xorBlock[AES::BLOCKSIZE];

	DWORD dwOffset = 0;
	BYTE *pEncryptData = NULL;
	DWORD dwEncryptDataSize = 0;

	// 計算需要的加密數據塊大小, 並按 128位 即 16位元組 對齊, 不夠則 填充0 對齊
	// 商
	DWORD dwQuotient = dwOriginalDataSize / AES::BLOCKSIZE;
	// 餘數
	DWORD dwRemaind = dwOriginalDataSize % AES::BLOCKSIZE;
	if (0 != dwRemaind)
	{
		dwQuotient++;
	}

	// 申請動態記憶體
	dwEncryptDataSize = dwQuotient * AES::BLOCKSIZE;

	// 分配加密後的數據緩衝區
	pEncryptData = new BYTE[dwEncryptDataSize];
	if (NULL == pEncryptData)
	{
		return FALSE;
	}

	// 設置AES加密密鑰
	aesEncryptor.SetKey(pAESKey, dwAESKeySize);

	do
	{
		// 初始化數據塊
		RtlZeroMemory(inBlock, AES::BLOCKSIZE);
		RtlZeroMemory(xorBlock, AES::BLOCKSIZE);
		RtlZeroMemory(outBlock, AES::BLOCKSIZE);

		// 獲取加密塊
		if (dwOffset <= (dwOriginalDataSize - AES::BLOCKSIZE))
		{
			RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), AES::BLOCKSIZE);
		}
		else
		{
			RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), (dwOriginalDataSize - dwOffset));
		}

		// 使用AES演算法進行加密
		aesEncryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);

		// 將加密後的數據塊拷貝到輸出緩衝區
		RtlCopyMemory((PVOID)(pEncryptData + dwOffset), outBlock, AES::BLOCKSIZE);

		// 更新數據
		dwOffset = dwOffset + AES::BLOCKSIZE;
		dwQuotient--;
	} while (0 < dwQuotient);

	// 返回數據
	*ppEncryptData = pEncryptData;
	*pdwEncryptData = dwEncryptDataSize;

	return TRUE;
}

如下AESDecrypt是一個使用AES演算法進行解密的函數。以下是對函數的主要步驟的註釋:

  1. AES解密對象初始化:
    • 創建AESDecryption對象用於AES解密。
    • 定義AES解密需要的數據塊:inBlock(輸入數據塊)、outBlock(輸出數據塊)、xorBlock(異或數據塊)。
  2. 計算解密數據塊大小:
    • 計算需要的解密數據塊數量,考慮到加密數據大小可能不是AES塊大小的整數倍。
  3. 分配解密後的數據緩衝區:
    • 根據計算得到的解密數據塊大小分配記憶體。
  4. 設置AES解密密鑰:
    • 調用SetKey函數設置AES解密密鑰。
  5. AES解密過程:
    • 迴圈處理加密數據塊,每次處理一個AES塊大小的數據。
    • 將加密數據塊拷貝到輸入數據塊。
    • 使用AES演算法進行解密。
    • 將解密後的數據塊拷貝到輸出緩衝區。
  6. 返回解密結果:
    • 返回解密後的數據緩衝區和大小。

請註意,在實際使用中,要確保釋放了分配的記憶體,以防止記憶體泄漏。

BOOL AESDecrypt(BYTE *pEncryptData, DWORD dwEncryptData, BYTE *pAESKey, DWORD dwAESKeySize, BYTE **ppDecryptData, DWORD *pdwDecryptData)
{
	// 定義AES解密需要的數據塊
	AESDecryption aesDecryptor;				
	// 解密密文數據塊
	unsigned char inBlock[AES::BLOCKSIZE];		
	// 解密後後明文數據塊
	unsigned char outBlock[AES::BLOCKSIZE];			
	// 必須設定全為0
	unsigned char xorBlock[AES::BLOCKSIZE];						
	DWORD dwOffset = 0;
	BYTE *pDecryptData = NULL;
	DWORD dwDecryptDataSize = 0;

	// 計算密文長度, 並按 128位 即 16位元組 對齊, 不夠則填充0對齊
	// 商
	DWORD dwQuotient = dwEncryptData / AES::BLOCKSIZE;
	// 餘數
	DWORD dwRemaind = dwEncryptData % AES::BLOCKSIZE;		
	if (0 != dwRemaind)
	{
		dwQuotient++;
	}

	// 分配解密後的數據緩衝區
	dwDecryptDataSize = dwQuotient * AES::BLOCKSIZE;
	pDecryptData = new BYTE[dwDecryptDataSize];
	if (NULL == pDecryptData)
	{
		return FALSE;
	}

	// 設置AES解密密鑰
	aesDecryptor.SetKey(pAESKey, dwAESKeySize);

	do
	{
		// 初始化數據塊
		RtlZeroMemory(inBlock, AES::BLOCKSIZE);
		RtlZeroMemory(xorBlock, AES::BLOCKSIZE);
		RtlZeroMemory(outBlock, AES::BLOCKSIZE);

		// 將加密數據塊拷貝到輸入數據塊
		if (dwOffset <= (dwDecryptDataSize - AES::BLOCKSIZE))
		{
			RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), AES::BLOCKSIZE);
		}
		else
		{
			RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), (dwEncryptData - dwOffset));
		}

		// 使用AES演算法進行解密
		aesDecryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);

		// 將解密後的數據塊拷貝到輸出緩衝區
		RtlCopyMemory((PVOID)(pDecryptData + dwOffset), outBlock, AES::BLOCKSIZE);

		// 更新數據
		dwOffset = dwOffset + AES::BLOCKSIZE;
		dwQuotient--;
	} while (0 < dwQuotient);

	// 返回數據
	*ppDecryptData = pDecryptData;
	*pdwDecryptData = dwDecryptDataSize;

	return TRUE;
}

AESEncrypt 函數用於對輸入的原始數據進行AES加密,加密使用指定的AES密鑰。函數通過參數返回加密後的數據和數據大小。

函數原型:

BOOL AESEncrypt(
    BYTE *pOriginalData,     // [in] 原始數據的指針
    DWORD dwOriginalDataSize, // [in] 原始數據的大小
    BYTE *pAESKey,           // [in] AES加密密鑰的指針
    DWORD dwAESKeySize,       // [in] AES加密密鑰的大小
    BYTE **ppEncryptData,    // [out] 指向指針的指針,用於存儲加密後的數據
    DWORD *pdwEncryptData    // [out] 指向DWORD的指針,用於存儲加密後的數據大小
);
  • pOriginalData: 指向要加密的原始數據的指針。
  • dwOriginalDataSize: 原始數據的大小。
  • pAESKey: 指向用於AES加密的密鑰的指針。
  • dwAESKeySize: AES加密密鑰的大小。
  • ppEncryptData: 指向指針的指針,用於存儲加密後的數據。該指針需要在函數外釋放分配的記憶體。
  • pdwEncryptData: 指向DWORD的指針,用於存儲加密後的數據大小。

函數返回一個BOOL值,表示操作是否成功。如果函數返回TRUE,則表示加密成功,否則表示加密失敗。

AESDecrypt 函數用於對輸入的加密後的數據進行AES解密,解密使用指定的AES密鑰。函數通過參數返回解密後的數據和數據大小。

函數原型:

BOOL AESDecrypt(
    BYTE *pEncryptData,      // [in] 加密後的數據的指針
    DWORD dwEncryptDataSize, // [in] 加密後的數據的大小
    BYTE *pAESKey,           // [in] AES解密密鑰的指針
    DWORD dwAESKeySize,       // [in] AES解密密鑰的大小
    BYTE **ppDecryptData,    // [out] 指向指針的指針,用於存儲解密後的數據
    DWORD *pdwDecryptData    // [out] 指向DWORD的指針,用於存儲解密後的數據大小
);
  • pEncryptData: 指向要解密的加密後數據的指針。
  • dwEncryptDataSize: 加密後數據的大小。
  • pAESKey: 指向用於AES解密的密鑰的指針。
  • dwAESKeySize: AES解密密鑰的大小。
  • ppDecryptData: 指向指針的指針,用於存儲解密後的數據。該指針需要在函數外釋放分配的記憶體。
  • pdwDecryptData: 指向DWORD的指針,用於存儲解密後的數據大小。

函數返回一個BOOL值,表示操作是否成功。如果函數返回TRUE,則表示解密成功,否則表示解密失敗。

調用時通過AESEncrypt加密數據,AESDecrypt則用於解密數據;

void ShowData(BYTE *pData, DWORD dwSize)
{
	for (int i = 0; i < dwSize; i++)
	{
		if ((0 != i) &&
			(0 == i % 16))
		{
			printf("\n");
		}
		else if ((0 != i) &&
			(0 == i % 8))
		{
			printf(" ");
		}

		printf("%02X ", pData[i]);
	}
	printf("\n");
}

int main(int argc, char* argv[])
{
	BYTE *pEncryptData = NULL;
	DWORD dwEncryptDataSize = 0;
	BYTE *pDecryptData = NULL;
	DWORD dwDecryptDataSize = 0;
	char szOriginalData[] = "It’s better to be alone than to be with someone you’re not happy to be with.";

	char szAESKey[] = "ABCDEFGHIJKIMNOP";
	BOOL  bRet = FALSE;

	// 加密
	bRet = AESEncrypt((BYTE *)szOriginalData, (1 + ::lstrlen(szOriginalData)), (BYTE *)szAESKey, ::lstrlen(szAESKey), &pEncryptData, &dwEncryptDataSize);
	if (FALSE == bRet)
	{
		return 1;
	}

	// 解密
	bRet = AESDecrypt(pEncryptData, dwEncryptDataSize, (BYTE *)szAESKey, ::lstrlen(szAESKey), &pDecryptData, &dwDecryptDataSize);
	if (FALSE == bRet)
	{
		return 2;
	}

	// 顯示
	printf("原文數據:\n");
	ShowData((BYTE *)szOriginalData, (1 + ::lstrlen(szOriginalData)));
	printf("密文數據:\n");
	ShowData(pEncryptData, dwEncryptDataSize);
	printf("解密後數據:\n");
	ShowData(pDecryptData, dwDecryptDataSize);

	// 釋放記憶體
	delete[]pEncryptData;
	pEncryptData = NULL;
	delete[]pDecryptData;
	pDecryptData = NULL;

	system("pause");
	return 0;
}

運行後對szOriginalData中的數據進行加密,密鑰是szAESKey中的長度,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17866880.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 寫在前面 先吐槽兩句,搞個mysql安裝配置弄了4個小時,怎麼都是外網無法訪問,我靠,我特麽也是服了。 當然,後來我投降了,明天再說,學什麼不是學,娘的,換個方向,狀態依然在! Sijax是什麼? 代表 Simple Ajax ,它是一個 Python / jQuery 庫,使用 jQuery.aj ...
  • Sun公司提供了JavaMail用來實現郵件發送,但是配置煩瑣,Spring中提供了JavaMailSender用來簡化郵件配置,Spring Boot則提供了MailSenderAutoConfiguration對郵件的發送做了進一步簡化。 v準備工作 開通POP3/SMTP服務或者IMAP/SM ...
  • C語言分支結構詳解 1. if 語句 在本篇博客文章中,我們將深入探討C語言中的if語句及其相關用法。if語句是一種用於條件判斷的分支語句,它允許我們根據條件的真假來執行不同的代碼塊。 1.1 if 語句的基本語法和用法 if語句的基本語法如下所示: if (條件) { // 條件為真時執行的代碼塊 ...
  • CF786 我不會告訴你鏈接在圖片里 CF786A CF786A題意 給出一個大小為 \(n\) 的環,點順時針從 \(1\to n\) 編號,兩個人(設為 \(0,1\))輪流移動其中的一個棋子。 對於第 \(opt\) 人,他能夠將這個棋子順時針移動 \(x\in S_{opt}\)(\(S_{ ...
  • public class RandomNickName { public enum Gender{ MAN, WOMAN, UNKNOWN, ; } public static void main(String[] args) { String nickName = nickName(Gender. ...
  • 1 簡介 任務是需要資源(CPU 時間、記憶體、存儲、網路帶寬等)在指定時間內完成的一段計算工作。 通過智能地將資源分配給任務以滿足任務級和系統級目標的系統稱為任務調度程式。 任務調度程式: 及時決定和分配資源給任務的過程稱為任務調度。 當我們在 Facebook 發表評論時。我們不會讓評論發佈者等待 ...
  • 本文從源碼層面主要分析了線程池的創建、運行過程,通過上述的分析,可以看出當線程池中的線程數量超過核心線程數後,會先將任務放入等待隊列,隊列放滿後當最大線程數大於核心線程數時,才會創建新的線程執行。 ...
  • 大家好,夜鶯項目發佈 v6.4.0 版本,新增全局巨集變數功能,本文為大家簡要介紹一下相關更新內容。 全局巨集變數功能 像 SMTP 的配置中密碼類型的信息,之前都是以明文的方式在頁面展示,夜鶯支持全局巨集變數之後,可以在變數管理配置一個 smtp_password 的密碼類型的變數,在 SMTP 配置頁 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...