介紹如何在 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
寫入一個獨立的簽章檔案,跟原始檔案放在一起,後續驗證數位簽章時再從檔案中讀取後進行驗證。