C++ 使用 Crypto++ 庫實現常用的加密算法

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++ 有兩種方法:

  • 1 直接用 apt-get 安裝
  • 2 在官方下載源碼安裝,或者在 github 上獲取源代碼。

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

5.3-1

 

六、總結

這一節實驗我們學習安裝了 crypto++ 庫,從中學習到 crypto 庫的基本用法,瞭解了 MD5 和 AES 的基本知識,並實現了對它們的應用。但是在實現 AES 加解密的時候沒用將加密後的字符串打印出來,因爲直接輸出到屏幕上它是亂碼的,那應該怎麼 辦呢?請同學們思考思考。

七、參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章