C/C++

C++ 語言使用 Crypto++ 實作 MD5、SHA1、SHA2、BLAKE2 雜湊教學與範例

介紹如何在 C++ 語言中使用 Crypto++ 加密函式庫,實作 MD5、SHA-1、SHA-2、SHA-3 與 BLAKE2 等雜湊演算法。

在使用 Crypto++ 函式庫之前,請先確認系統上有安裝好該函式庫,Ubuntu Linux 可以參考 Ubuntu Linux 安裝、使用 Crypto++ 加密函式庫教學

計算雜湊值

以下是一個使用 Crypto++ 實作雜湊演算法的範例程式,可自由抽換不同的雜湊演算法,包含 SHA-1、SHA-2、SHA-3、BLAKE2 與 MD5 等:

#include <iostream>
#include <cstdlib>
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "sha.h"    // SHA-1 與 SHA-2 雜湊
#include "sha3.h"   // SHA-3 雜湊
#include "blake2.h" // BLAKE2 雜湊
#include "hex.h"

// 啟用不安全的演算法(MD5 專用)
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include "md5.h"  // MD5 雜湊

int main () {
    using namespace CryptoPP;

    // 指定採用的雜湊演算法
    SHA1 hash;         // SHA-1
    // SHA224 hash;    // SHA-224
    // SHA256 hash;    // SHA-256
    // SHA384 hash;    // SHA-384
    // SHA512 hash;    // SHA-512
    // SHA3_224 hash;  // SHA3-224
    // SHA3_256 hash;  // SHA3-256
    // SHA3_384 hash;  // SHA3-384
    // SHA3_512 hash;  // SHA3-512
    // BLAKE2b hash;   // BLAKE2
    // Weak::MD5 hash; // MD5

    // 原始資料
    std::string msg = "Crypto++ is a free C++ library for cryptography.";

    // 預留儲存雜湊值的空間
    std::string digest;
    digest.resize(hash.DigestSize());

    // 輸入資料
    hash.Update((const byte*) msg.data(), msg.size());

    // 計算雜湊值
    hash.Final((byte*) digest.c_str());

    // 輸出資料與雜湊值
    std::cout << "Message: " << msg << std::endl;
    std::cout << hash.AlgorithmName() << " Digest: ";
    StringSource(digest, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return EXIT_SUCCESS;
}

將這份程式碼儲存為 my_hash.cpp,並以 g++ 編譯器進行編譯:

# 編譯 Crypto++ 應用程式
g++ -I/usr/include/crypto++ -o my_hash my_hash.cpp -lcryptopp

# 執行應用程式
./my_hash
Message: Crypto++ is a free C++ library for cryptography.
SHA-1 Digest: 1A7588E2529D53DD5200371F6CF5084754A9B76C

由於 MD5 雜湊值屬於過時且不安全的雜湊演算法,所以在使用時要先將 CRYPTOPP_ENABLE_NAMESPACE_WEAK 設為 1,啟用不安全的演算法之後,才能使用 MD5 雜湊演算法。

在實務上若輸入的資料量很大,可以將資料分批以多次 Update() 輸入資料,等所有資料都輸入完之後,再呼叫 Final() 計算雜湊值。如果資料量很小,也可以使用 CalculateDigest() 輸入所有資料並計算出雜湊值:

#include <iostream>
#include <cstdlib>
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "sha.h"
#include "hex.h"
int main () {
    using namespace CryptoPP;

    // 原始資料
    std::string msg = "Crypto++ is a free C++ library for cryptography.";

    // 預留儲存雜湊值的空間
    std::string digest;
    digest.resize(SHA1::DIGESTSIZE);

    // 計算 SHA1 雜湊值
    SHA1().CalculateDigest((byte*) digest.c_str(),
                           (byte*) msg.c_str(), msg.length());

    // 輸出資料與雜湊值
    std::cout << "Message: " << msg << std::endl;
    std::cout << "Digest: ";
    StringSource(digest, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return EXIT_SUCCESS;
}

管線(Pipelining)

這裡的 StringSource()HexEncoder()FileSink() 是 Crypto++ 所提供的各種 Filter,多個 Filters 可以組成一條 管線(Pipelining),概念類似 Linux 中的指令管線(pipeline),將不同的小工具串接後,以資料流的概念來處理資料。

這裡的 HexEncoder() 是一個可以將二進位資料轉換為十六進位(hex)文字的 Filter,轉換時可以調整英文字母的大小寫、分隔符號等,可以依照自己的需求自行設定。

以下是採用 Pipelining 寫法的雜湊計算程式碼範例:

#include <iostream>
#include <cstdlib>
#include "cryptlib.h"
#include "filters.h"
#include "sha.h"
#include "hex.h"
int main () {
    using namespace CryptoPP;

    // 指定採用 SHA1 雜湊演算法
    SHA1 hash;

    // 原始資料與儲存雜湊值的空間
    std::string msg = "Crypto++ is a free C++ library for cryptography.";
    std::string digest;

    // 以 Pipelining 計算雜湊值
    StringSource ss(msg, true,
        new HashFilter(hash,
            new HexEncoder(
                new StringSink(digest)
                ) // HexEncoder
            ) // HashFilter
       ); // StringSource

    // 輸出資料與雜湊值
    std::cout << "Message: " << msg << std::endl;
    std::cout << "Digest: " << digest << std::endl;

    return EXIT_SUCCESS;
}

計算檔案雜湊值

若要計算檔案內容的雜湊值,可以使用 FileSource() 這個 Filter:

#include <iostream>
#include <cstdlib>
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "sha.h"
#include "hex.h"
int main (int argc, char *argv[]) {
    using namespace CryptoPP;

    // 指定採用 SHA1 雜湊演算法
    SHA1 hash;

    // 儲存雜湊值的空間
    std::string digest;

    // 以 Pipelining 計算雜湊值
    FileSource fs(argv[1], true,
        new HashFilter(hash,
            new HexEncoder(
                new StringSink(digest)
                ) // HexEncoder
            ) // HashFilter
       ); // StringSource

    // 輸出雜湊值
    std::cout << "Digest: " << digest << std::endl;

    return EXIT_SUCCESS;
}

將這份程式碼儲存為 file_sha1.cpp,並以 g++ 編譯器進行編譯:

# 編譯 Crypto++ 應用程式
g++ -I/usr/include/crypto++ -o file_sha1 file_sha1.cpp -lcryptopp

執行程式,並指定要計算雜湊值的檔案:

# 執行應用程式
./file_sha1 my_file.txt
Digest: 49FDC0FCAA06B59375CDDD9CB5C84D7AC37BC216

我們可以使用 Linux 中的 sha1sum 指令來驗證自己的計算是否正確:

# 以 sha1sum 指令計算雜湊碼
sha1sum my_file.txt
49fdc0fcaa06b59375cddd9cb5c84d7ac37bc216  my_file.txt

驗證雜湊值

Crypto++ 的各種雜湊演算法也可以用來驗證資料的雜湊值是否正確,以下是驗證 SHA-1 雜湊值的範例:

#include <iostream>
#include <cstdlib>
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "sha.h"
#include "hex.h"
int main () {
    using namespace CryptoPP;

    // 指定採用的雜湊演算法
    SHA1 hash;

    // 原始資料
    std::string msg = "Crypto++ is a free C++ library for cryptography.";

    // SHA1 雜湊值
    unsigned char digest[] = {
    	0x1A, 0x75, 0x88, 0xE2, 0x52, 0x9D, 0x53, 0xDD, 0x52, 0x00,
    	0x37, 0x1F, 0x6C, 0xF5, 0x08, 0x47, 0x54, 0xA9, 0xB7, 0x6C};

    // 輸入資料
    hash.Update((const byte*) msg.data(), msg.size());

    // 驗證雜湊值
    if (hash.Verify((const byte*) digest))
    	std::cout << "驗證成功" << std::endl;
    else
    	std::cout << "驗證失敗" << std::endl;

    return EXIT_SUCCESS;
}

將這份程式碼儲存為 sha1_vfy.cpp,並以 g++ 編譯器進行編譯:

# 編譯 Crypto++ 應用程式
g++ -I/usr/include/crypto++ -o sha1_vfy sha1_vfy.cpp -lcryptopp

# 執行應用程式
./sha1_vfy
驗證成功

這裡只示範 SHA-1 雜湊值的驗證,其他雜湊演算法的驗證方式也都大同小異,只是抽換雜湊演算法而已。

參考資料

Share
Published by
Office Guide

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago