C/C++

C++ 語言使用 Crypto++ 實作 RSA 數位簽章教學與範例

介紹如何在 C++ 語言中使用 Crypto++ 加密函式庫,實作 RSA 數位簽章的簽署與驗證。

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

附件數位簽章

以下的範例是使用 RSA 演算法產生獨立的數位簽章,這樣的數位簽章會以附件的方式跟原始資料一起存放或傳遞,在驗證簽章有效性的時候,必須連同原始資料一起附上。

#include <string>
#include <iostream>
#include <cstdlib>
#include "rsa.h"
#include "pssr.h"
#include "sha.h"
#include "osrng.h"
#include "secblock.h"

int main(int argc, char* argv[]) {
    using namespace CryptoPP;

    // 建立隨機亂數產生器
    AutoSeededRandomPool prng;

    try {
        // 產生 RSA 私鑰
        RSA::PrivateKey rsaPrivKey;
        rsaPrivKey.GenerateRandomWithKeySize(prng, 3072);

        // 產生對應的 RSA 公鑰
        RSA::PublicKey rsaPubKey(rsaPrivKey);

        // 資料內容
        std::string message = "Yoda said, Do or Do Not. There is not try.";

        // 簽署器
        RSASS<PSS, SHA256>::Signer signer(rsaPrivKey);

        // 建立簽章儲存空間
        size_t length = signer.MaxSignatureLength();
        SecByteBlock signature(length);

        // 對資料進行簽署
        signer.SignMessage(prng, (const byte*) message.c_str(),
            message.length(), signature);

        // 簽章驗證器
        RSASS<PSS, SHA256>::Verifier verifier(rsaPubKey);

        // 驗證簽章
        bool result = verifier.VerifyMessage((const byte*) message.c_str(),
            message.length(), signature, signature.size() );

        // 檢查驗證結果
        if(result) {
            std::cout << "簽章驗證成功" << std::endl;
        } else {
            std::cerr << "簽章驗證失敗" << std::endl;
        }

    } catch(CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return EXIT_SUCCESS;
}

將這段程式碼儲存至 rsa_pss.cpp,使用以下指令進行編譯與執行:

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

# 執行應用程式
./rsa_pss

以下是使用 Crypto++ 函式庫的 Filter 架構來改寫的版本,功能相同但程式碼可以比較精簡:

#include <string>
#include <iostream>
#include <cstdlib>
#include "rsa.h"
#include "pssr.h"
#include "sha.h"
#include "osrng.h"
#include "secblock.h"

int main(int argc, char* argv[]) {
    using namespace CryptoPP;

    // 建立隨機亂數產生器
    AutoSeededRandomPool prng;

    try {
        // 產生 RSA 私鑰
        RSA::PrivateKey rsaPrivKey;
        rsaPrivKey.GenerateRandomWithKeySize(prng, 3072);

        // 產生對應的 RSA 公鑰
        RSA::PublicKey rsaPubKey(rsaPrivKey);

        // 資料內容
        std::string message = "Yoda said, Do or Do Not. There is not try.";

        // 數位簽章
        std::string signature;

        // 簽署器
        RSASS<PSS, SHA256>::Signer signer(rsaPrivKey);

        // 對資料進行簽署
        StringSource(message, true,
            new SignerFilter(prng, signer,
                new StringSink(signature)
            ) // SignerFilter
        ); // StringSource

        // 簽章驗證器
        RSASS<PSS, SHA256>::Verifier verifier(rsaPubKey);

        // 驗證數位簽章
        StringSource(message+signature, true,
            new SignatureVerificationFilter(
                verifier, NULL,
                SignatureVerificationFilter::THROW_EXCEPTION
                | SignatureVerificationFilter::SIGNATURE_AT_END
            ) // SignatureVerificationFilter
        ); // StringSource

        std::cout << "簽章驗證成功" << std::endl;

    } catch(CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return EXIT_SUCCESS;
}

資料混合數位簽章

另外一種建立 RSA 數位簽章的方式是直接將資料與產生的數位簽章混合存放,這樣在驗證簽章時就不需要另外輸入原始資料:

#include <string>
#include <iostream>
#include <cstdlib>
#include "rsa.h"
#include "pssr.h"
#include "sha.h"
#include "osrng.h"
#include "secblock.h"

int main(int argc, char* argv[]) {
    using namespace CryptoPP;

    // 建立隨機亂數產生器
    AutoSeededRandomPool prng;

    try {
        // 產生 RSA 私鑰
        RSA::PrivateKey rsaPrivKey;
        rsaPrivKey.GenerateRandomWithKeySize(prng, 3072);

        // 產生對應的 RSA 公鑰
        RSA::PublicKey rsaPubKey(rsaPrivKey);

        // 資料內容
        std::string message = "Yoda said, Do or Do Not. There is not try.";

        // 簽署器、簽章驗證器
        RSASS<PSSR, SHA256>::Signer signer(rsaPrivKey);
        RSASS<PSSR, SHA256>::Verifier verifier(rsaPubKey);

        // 建立簽章儲存空間(內含資料)
        SecByteBlock signature(signer.MaxSignatureLength(message.length()));

        // 對資料進行簽署
        size_t signatureLen = signer.SignMessageWithRecovery(
                prng, (const byte*) message.c_str(),
                message.length(), NULL, 0, signature);

        // 將簽章大小調整至實際值
        signature.resize(signatureLen);

        // 解開資料的放置空間
        SecByteBlock recovered(
            verifier.MaxRecoverableLengthFromSignatureLength(signatureLen)
        );

        // 驗證數位簽章、解開資料
        DecodingResult result = verifier.RecoverMessage(recovered, NULL,
            0, signature, signatureLen);

        // 檢查數位簽章驗證結果
        if (result.isValidCoding) {
            std::cout << "簽章驗證成功" << std::endl;
        } else {
            std::cerr << "簽章驗證失敗" << std::endl;
            return EXIT_FAILURE;
        }

        // 將資料大小調整至實際值
        recovered.resize(result.messageLength);

        // 輸出資料
        std::string recoveredStr(reinterpret_cast<const char*>(
                &recovered[0]), recovered.size());
        std::cout << "解開資料:" << recoveredStr << std::endl;

    } catch(CryptoPP::Exception&e) {
        std::cerr << e.what() << std::endl;
    }
    return EXIT_SUCCESS;
}

將這段程式碼儲存至 rsa_pssr.cpp,使用以下指令進行編譯與執行:

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

# 執行應用程式
./rsa_pssr
簽章驗證成功
解開資料:Yoda said, Do or Do Not. There is not try.

以下是改用 Filter 的寫法,寫法較簡潔但功能相同:

#include <string>
#include <iostream>
#include "rsa.h"
#include "pssr.h"
#include "sha.h"
#include "osrng.h"

int main(int argc, char* argv[]) {
    using namespace CryptoPP;

    // 建立隨機亂數產生器
    AutoSeededRandomPool prng;

    try {
        // 產生 RSA 私鑰
        RSA::PrivateKey rsaPrivKey;
        rsaPrivKey.GenerateRandomWithKeySize(prng, 3072);

        // 產生對應的 RSA 公鑰
        RSA::PublicKey rsaPubKey(rsaPrivKey);

        // 資料內容
        std::string message = "Yoda said, Do or Do Not. There is not try.";

        // 數位簽章、解開資料
        std::string signature, recovered;

        // 簽署器
        RSASS<PSSR, SHA256>::Signer signer(rsaPrivKey);

        // 對資料進行簽署
        StringSource(message, true,
            new SignerFilter(prng, signer,
                new StringSink(signature),
                true // putMessage for recovery
            ) // SignerFilter
        ); // StringSource

        // 簽章驗證器
        RSASS<PSSR, SHA256>::Verifier verifier(rsaPubKey);

        // 驗證數位簽章
        StringSource(signature, true,
            new SignatureVerificationFilter(
                verifier,
                new StringSink(recovered),
                SignatureVerificationFilter::THROW_EXCEPTION
                | SignatureVerificationFilter::PUT_MESSAGE
            ) // SignatureVerificationFilter
        ); // StringSource

        std::cout << "簽章驗證成功" << std::endl;
        std::cout << "解開資料:" << recovered << std::endl;

    } catch(CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return EXIT_SUCCESS;
}

檔案數位簽章

以下是對檔案產生獨立的 RSA 數位簽章,並驗證簽章有效性的範例。

#include <string>
#include <iostream>
#include "rsa.h"
#include "pssr.h"
#include "sha.h"
#include "osrng.h"
#include "files.h"
#include "secblock.h"

int main(int argc, char* argv[]) {
    using namespace CryptoPP;

    // 建立隨機亂數產生器
    AutoSeededRandomPool prng;

    try {
        // 產生 RSA 私鑰
        RSA::PrivateKey rsaPrivKey;
        rsaPrivKey.GenerateRandomWithKeySize(prng, 3072);

        // 產生對應的 RSA 公鑰
        RSA::PublicKey rsaPubKey(rsaPrivKey);

        // 數位簽章
        std::string signature;

        // 簽署器
        RSASS<PSS, SHA256>::Signer signer(rsaPrivKey);

        // 對檔案內容進行簽署
        FileSource("my_file.txt", true,
            new SignerFilter(prng, signer,
                new StringSink(signature)
            ) // SignerFilter
        ); // FileSource

        // 簽章驗證器
        RSASS<PSS, SHA256>::Verifier verifier(rsaPubKey);

        byte result = 0;

        // 建立驗證簽章 Filter(預設選項為 SIGNATURE_AT_BEGIN | PUT_RESULT)
        SignatureVerificationFilter filter(verifier,
                new ArraySink(&result, sizeof(result)));

        // 建立簽章與資料的 Source
        StringSource ss(signature, true);
        FileSource fs("my_file.txt", true);

        // 先輸入簽章,後輸入資料
        ss.TransferTo(filter);
        fs.TransferTo(filter);

        // 結束資料輸入,進行簽章驗證
        filter.MessageEnd();

        // 檢查驗證結果
        if (result)
            std::cout << "簽章驗證成功" << std::endl;
        else
            std::cerr << "簽章驗證失敗" << std::endl;

    } catch(CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return EXIT_SUCCESS;
}

在實務上我們可以將產生的數位簽章直接透過 FileSink 寫入一個獨立的簽章檔案,跟原始檔案放在一起,後續驗證數位簽章時再從檔案中讀取後進行驗證。

參考資料

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