C++ 使用 Crypto++ 庫實現常用的加密算法
Crypto++ 庫是開源的 C++ 數據加密算法庫,支持如下算法:RSA、MD5、DES、AES、SHA-256 等等,其中對於加密有對稱加密和非對稱加密。本實驗通過 Cryto++ 庫對字符串進行 MD5 校驗,並用 AES 加密和解密。
1.1 知識點
- 安裝 Crypto++ 庫並檢驗
- 學習 MD5 摘要算法
- 學習 AES 加密算法
1.2 效果截圖
二、加密算法介紹
據記載,公元前400年,古希臘人發明了置換密碼。1881年世界上的第一個電話保密專利出現。在第二次世界大戰期間,德國軍方啓用“恩尼格瑪”密碼機,密碼學在戰爭中起着非常重要的作用。 隨着信息化和數字化社會的發展,人們對信息安全和保密的重要性認識不斷提高,於是在1997年,美國國家標準局公佈實施了“美國數據加密標準(DES)”,民間力量開始全面介入密碼學的研究和應用中,採用的加密算法有 DES、RSA、SHA 等。隨着對加密強度需求的不斷提高,近期又出現了 AES、ECC 等。 使用密碼學可以達到以下目的:保密性:防止用戶的標識或數據被讀取。數據完整性:防止數據被更改。身份驗證:確保數據發自特定的一方。
2.1 加密的類型介紹
根據密鑰類型不同將現代密碼技術分爲兩類: 對稱加密算法(祕密鑰匙加密)和非對稱加密算法(公開密鑰加密)。
對稱鑰匙加密系統是加密和解密均採用同一把祕密鑰匙,而且通信雙方都必須獲得這把鑰匙,並保持鑰匙的隱祕。 非對稱密鑰加密系統採用的加密鑰匙(公鑰)和解密鑰匙(私鑰)是不同的。
對稱加密算法:
- DES(Data Encryption Standard):數據加密標準,速度較快,適用於加密大量數據的場合。
- 3DES(Triple DES):是基於DES,對一塊數據用三個不同的密鑰進行三次加密,強度更高
- AES(Advanced EncryptionStandard):高級加密標準,是下一代的加密算法標準,速度快,安全級別高
非對稱算法:
- RSA:由 RSA 公司發明,是一個支持變長密鑰的公共密鑰算法,需要加密的文件塊的長度也是可變的。
- DSA(Digital Signature Algorithm):數字簽名算法,是一種標準的 DSS(數字簽名標準)
- ECC(Elliptic Curves Cryptography):橢圓曲線密碼編碼學。
散列算法:
- MD5(Message Digest Algorithm 5):是RSA數據安全公司開發的一種單向散列算法。
- SHA(Secure Hash Algorithm):可以對任意長度的數據運算生成一個160位的數值。
著名的密碼學算法庫還有 OpenSSL 。OpenSSL 爲網絡通信提供安全及數據完整性的一種安全協議 ,囊括了主要的密碼算法、常用的密鑰和證書封裝管理功能以及 SSL 協議,並提供了豐富的應用程序供測試或其它目的使用。通過比較 Crypto++ 和 Openssl,我們可以發現 Crypto++ 支持的算法比 Openssl 多。
三、安裝 Crypto++
安裝 Crypto++ 有兩種方法:
3.1 用 ubuntu 的源安裝
sudo apt-get update
apt-cache pkgnames | grep -i crypto++
sudo apt-get install libcrypto++-dev libcrypto++-doc libcrypto++-utils libcrypto++9 libcrypto++9-dbg
3.2 測試
安裝完成之後編寫一個測試程序檢測是否安裝成功:
cd /home/shiyanlou/
vim test1.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "cryptopp/integer.h"
using CryptoPP::Integer;
int main( int, char** ) {
Integer i;
cout << "i: " << i << endl;
cout << "Yo, man!" << endl;
return 0;
}
編寫完後退出 vim ,編譯程序
g++ -o test1 test1.cpp -lcryptopp # 這裏的lcryptopp 就是我們的 cryto++ 庫,在 fedora 上可能是 -lcrypto++
./test1
四、編寫實現 MD5 摘要算法
MD5 的全稱是 Message-Digest Algorithm 5,在90年代初由 MIT 的計算機科學實驗室和 RSA Data Security Inc 發明,由 MD2/MD3/MD4 發展而來的。MD5 的實際應用是對一段 Message (字節串)產生 fingerprint (指紋),可以防止被“篡改”,因此通常用於密碼的加密存儲,數字簽名,文件完整性驗證等。 在很多操作系統中,用戶的密碼是以 MD5 值(或類似的其它算法)的方式保存的,用戶 Login 的時候,系統是把用戶輸入的密碼計算成 MD5 值, 然後再去和系統中保存的MD5值進行比較,來驗證該用戶的合法性。 使用 crypto++ 庫生成 MD5 值很簡單,理解這個函數即可:
以下是我們用到函數的原型:
StringSource (const std::string &string,
bool pumpAll,
BufferedTransformation *attachment=NU
stringsource
是一個字符數組和字符串的源,源(Source)就是指數據的來源。
Crypto++ 提供以下常用的源:
- File
- Random Number
- Socket
- String
- Windows Pipe
關於它的更多細節,請查閱官方手冊
打開終端輸入:
cd /home/shiyanlou
vim md5.cpp
4.1 核心代碼
int main()
{
std::string digest, inData;
std::cin >> inData;
Weak1::MD5 md5; //創建 md5 對象
StringSource(inData, true, new HashFilter(md5, new HexEncoder(new StringSink(digest)))); //轉換的關鍵
std::cout<< digest ;
}
#endif//_CRYPTO_UTIL_H_
這裏提供了完整的代碼:
wget http://labfile.oss.aliyuncs.com/courses/654/md5.cpp
4.2 編譯生成程序
g++ -o md5 md5.cpp -lcryptopp
./md5
五、編寫實現 AES 加密解密
AES 採用的是對稱加密,在密碼學中又稱 Rijndael 加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的 DES ,已經被多方分析且廣爲全世界所使用。 AES 設計有三個密鑰長度:128,192,256 比特, 相對而言, AES 的128比特密鑰比 DES 的56比特密鑰強10^21倍 。 AES有五種加密模式:
- 電碼本模式(ECB模式): 這種模式是將整個明文分成若干段相同的小段,然後對每一小段進行加密。
- 密碼分組鏈接模式(CBC模式):這種模式是先將明文切分成若干小段,然後每一小段與初始塊或者上一段的密文段進行異或運算後,再與密鑰進行加密。
- 計算器模式(CTR模式):計算器模式不常見,在CTR模式中, 有一個自增的算子,這個算子用密鑰加密之後的輸出和明文異或的結果得到密文,相當於一次一密。
- 密碼反饋模式(CFB模式):與ECB和CBC模式只能夠加密塊數據不同,CFB能夠將塊密文(Block Cipher)轉換爲流密文(Stream Cipher)。
- 輸出反饋模式(OFB模式):OFB是先用塊加密器生成密鑰流(Keystream),然後再將密鑰流與明文流異或得到密文流,解密是先用塊加密器生成密鑰流, 再將密鑰流與密文流異或得到明文,由於異或操作的對稱性所以加密和解密的流程是完全一樣的。
本實驗採用 CBC 模式。在設計類 CCryptoUtil 中,分爲兩個模塊, encrypt4aes 加密, decrypt4aes 解密。
5.1 設計加密的模塊
在 /home/shiyanlou/
目錄下新建 aes.cpp
文件並添加如下代碼:
static int encrypt4aes(const std::string &inData, const std::string &strKey,
std::string &outData, std::string &errMsg)
{
outData = "";
errMsg = "";
if (inData.empty() || strKey.empty()) // 判斷待加密的字符串或者密鑰是否爲空
{
errMsg = "indata or key is empty!!";
return -1;
}
unsigned int iKeyLen = strKey.length();
if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24 //判斷密鑰的長度是否符合要求
&& iKeyLen != AES_KEY_LENGTH_32)
{
errMsg = "aes key invalid!!";
return -2;
}
byte iv[AES::BLOCKSIZE];
int iResult = 0;
try
{
CBC_Mode<AES>::Encryption e; //CBC 模式加密
e.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
StringSource ss(inData, true, new StreamTransformationFilter(e, new StringSink(outData))); //加密的關鍵, outData 就是加密後的數據
} catch (const CryptoPP::Exception& e)
{
errMsg = "Encryptor throw exception!!";
iResult = -3;
}
return iResult;
}
5.2 設計解密的模塊
在 /home/shiyanlou/aes.cpp
文件下添加如下代碼:
static int decrypt4aes(const std::string &inData, const std::string &strKey,
std::string &outData, std::string &errMsg)
{
outData = "";
errMsg = "";
if (inData.empty() || strKey.empty()) // 判斷待加密的字符串或者密鑰是否爲空
{
errMsg = "indata or key is empty!!";
return -1;
}
unsigned int iKeyLen = strKey.length();
if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24 //判斷密鑰的長度是否符合要求
&& iKeyLen != AES_KEY_LENGTH_32)
{
errMsg = "aes key invalid!!";
return -2;
}
byte iv[AES::BLOCKSIZE];
int iResult = 0;
try
{
CBC_Mode<AES>::Decryption d; //CBC 模式解密
d.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
StringSource ss(inData, true,
new StreamTransformationFilter(d, new StringSink(outData))); //解密的函數,outData 是解密的結果
}
catch (const CryptoPP::Exception& e)
{
errMsg = "Encryptor throw exception";
iResult = -3;
}
return iResult;
}
5.3 main 函數
在 /home/shiyanlou/aes.cpp
文件下添加如下代碼:
int main(int argc, char **argv)
{
std::string strCipher; //待加密的字符串
std::string strKey; //用來加解密的密鑰
std::cout << "Please enter a string" << std::endl;
std::cin >> strCipher;
std::cout << "please enter a key, you just can write 16,24 or 32 words as a key" << std::endl;
std::cin << strKey;
std::string strResult;
std::string strErrMsg;
int iResult = CCryptoUtil::encrypt4aes(strCipher, strKey, strResult, strErrMsg);
std::cout << "the result is :" << strResult << std::endl;
if(iResult)
{
std::cout<<"CCryptoUtil::encrypt4aes failed,errMsg:"<<strErrMsg;
return -1;
}
std::string strPlainText;
iResult = CCryptoUtil::decrypt4aes(strResult,strKey,strPlainText,strErrMsg);
if(iResult)
{
std::cout<<"CCryptoUtil::decrypt4aes failed,errMsg:"<<strErrMsg;
return -2;
}
std::cout << "PlainText:"<<strPlainText << std::endl;
}
這裏也提供了完整的代碼,可以直接編譯:
wget http://labfile.oss.aliyuncs.com/courses/654/aes.cpp
六、總結
這一節實驗我們學習安裝了 crypto++ 庫,從中學習到 crypto 庫的基本用法,瞭解了 MD5 和 AES 的基本知識,並實現了對它們的應用。但是在實現 AES 加解密的時候沒用將加密後的字符串打印出來,因爲直接輸出到屏幕上它是亂碼的,那應該怎麼 辦呢?請同學們思考思考。