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