基於cyptopp庫的rsa加解密詳解
阿新 • • 發佈:2019-02-16
編譯Cryptopp
編譯過程沒有什麼特別,需要注意的是,如果使用dll版本的庫,只包含IPS認證的演算法,而編譯靜態連結庫則包含全部演算法,具體參考[1,2]。
- cryptopp - This builds the DLL. Please note that if you wish to use Crypto++ as a FIPS validated module, you must use a pre-built DLL that has undergone the FIPS validation process instead of building your own.
- dlltest - This builds a sample application that only uses the DLL.
- cryptest Non-DLL-Import Configuration - This builds the full static library along with a full test driver.
- cryptest DLL-Import Configuration - This builds a static library containing only algorithms not in the DLL, along with a full test driver that uses both the DLL and the static library.
本人編譯的lib庫大小為50多M。
RSA演算法介紹
關於rsa演算法的介紹可以參考阮一峰的兩篇兩篇博文,在文末[3,4]以列出。
在RSA演算法中有幾個需要注意的點,我羅列一下:
非對稱加密:對稱加密演算法在加密和解密時使用的是同一個祕鑰;而非對稱加密演算法需要兩個金鑰來進行加密和解密,這兩個祕鑰是公開金鑰(public key,進行加密)和私有金鑰(private key,用於解密)。
RSA用途:RSA加密演算法除了用於少量資料加密之外,最主要的應用就是數字簽名。
數字簽名:包含3個步驟。詳見[5]:
- 待發送訊息(message)利用Hash函式,生成資訊的摘要;
- 私鑰加密摘要,生成”數字簽名”(signature)
- 傳送message+signature
- 公鑰解密簽名
- message重新生成摘要,與傳送過來的摘要進行比較
公鑰和私鑰:公鑰和私鑰是成對的,它們互相解密。公鑰加密,私鑰解密。私鑰數字簽名,公鑰驗證。
RSA祕鑰長度:cyptopp至少要求祕鑰長度為1024;祕鑰長度即為n值大小,即n=128byte
明文長度:一般應小於等於金鑰長度(Bytes)-11,末尾採用填充。
解決長度限制:主要有2種方式
- 先用對稱加密演算法(AES/DES等)加密資料,然後用RSA公鑰加密對稱加密金鑰,用RSA的私鑰解密得到對稱加密的金鑰,然後完成反向操作得到明文。
- 分段進行RSA加密
示例程式碼
加密與解密
#include "rsa.h"
using CryptoPP::RSA;
using CryptoPP::InvertibleRSAFunction;
using CryptoPP::RSAES_OAEP_SHA_Encryptor;
using CryptoPP::RSAES_OAEP_SHA_Decryptor;
#include "sha.h"
using CryptoPP::SHA1;
#include "filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::PK_EncryptorFilter;
using CryptoPP::PK_DecryptorFilter;
#include "files.h"
using CryptoPP::FileSink;
using CryptoPP::FileSource;
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;
#include "SecBlock.h"
using CryptoPP::SecByteBlock;
#include "cryptlib.h"
using CryptoPP::Exception;
using CryptoPP::DecodingResult;
using CryptoPP::PrivateKey;
using CryptoPP::PublicKey;
using CryptoPP::BufferedTransformation;
#include <string>
using std::string;
#include <stdexcept>
using std::runtime_error;
#include <exception>
using std::exception;
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <queue.h>
using CryptoPP::ByteQueue;
#include <integer.h>
using CryptoPP::Integer;
#include <base64.h>
using CryptoPP::Base64Encoder;
using CryptoPP::Base64Decoder;
#include <assert.h>
////////////////////////////////////////////////
//儲存公鑰和私鑰
void SavePrivateKey(const string& filename, const PrivateKey& key);
void SavePublicKey(const string& filename, const PublicKey& key);
////////////////////////////////////////////////
//base64編碼
void SaveBase64PrivateKey(const string& filename, const PrivateKey& key);
void SaveBase64PublicKey(const string& filename, const PublicKey& key);
////////////////////////////////////////////////
//儲存
void Save(const string& filename, const BufferedTransformation& bt);
void SaveBase64(const string& filename, const BufferedTransformation& bt);
//讀取key
void LoadPrivateKey(const string& filename, PrivateKey& key);
void LoadPublicKey(const string& filename, PublicKey& key);
void LoadBase64PrivateKey(const string& filename, PrivateKey& key);
void LoadBase64PublicKey(const string& filename, PublicKey& key);
void LoadBase64(const string& filename, BufferedTransformation& bt);
void Load(const string& filename, BufferedTransformation& bt);
int main(int argc, char* argv[])
{
try
{
////////////////////////////////////////////////
// 生成私鑰和公鑰
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize( rng, 1024 ); //生成1024bit金鑰,即n=128byte
const Integer& n = parameters.GetModulus();
const Integer& p = parameters.GetPrime1();
const Integer& q = parameters.GetPrime2();
const Integer& d = parameters.GetPrivateExponent();
const Integer& e = parameters.GetPublicExponent();
cout << "RSA Parameters:" << endl;
cout << " n: " << std::hex << n << endl; //n=p*q
cout << " p: " << std::hex << p << endl; //p
cout << " q: " << std::hex << q << endl; //q
cout << " d: " << std::hex << d << endl;
cout << " e: " << std::hex << e << endl; //e預設是17,原因不明
cout << endl;
//生成公鑰和私鑰
RSA::PrivateKey privateKey( parameters );//私鑰用於加密(n,d)
RSA::PublicKey publicKey( parameters ); //公鑰用於解密(n,e)
//預設使用ASN.1 DER編碼
//SavePrivateKey("rsa-private.key", privateKey);
//SavePublicKey("rsa-public.key", publicKey);
////////////////////////////////////////////////
//輸出privateKey
//ByteQueue queue;
//privateKey.Save(queue);
////////////////////////////////////////////////
//儲存private_key到char[]中,
//注意char[]長度要足夠
//注意這裡privateKey不僅包括n和d
//是使用ASN.1 DER編碼的字元陣列
//char private_key_der_string[1024];
//size_t size = queue.MaxRetrievable();
//queue.Get((byte*)private_key_der_string,size);
//for(int i=0;i<1024;i++)
// cout << std::hex << ((int)private_key_der_string[i]&0xff) << " ";
////////////////////////////////////////////////
//儲存private_key到string中,
//注意要預留string size
//string private_key_der_string;
//size_t size = queue.MaxRetrievable();
//if(size)
//{
// private_key_der_string.resize(size);
// queue.Get((byte*)private_key_der_string.data(),
// private_key_der_string.size());
//}
//for(auto it = private_key_der_string.begin();
// it!=private_key_der_string.end(); it++)
// cout<< std::hex << (*it & 0xff); //16進位制輸出char自動轉int,需要截斷位元組
//cout << endl << endl;
////////////////////////////////////////////////
//StringSource引數:(原始字串, 長度, 變換方式filter)
//使用filter將char[]轉存為base64編碼
//string bs64_private_key;
//StringSource ss(private_key_der_string, true,
// new Base64Encoder( //base64編碼器
// new StringSink(bs64_private_key) //儲存到bs64_private_key
// )
// );
//cout << bs64_private_key << endl;
////////////////////////////////////////////////
//儲存base64 key到檔案
SaveBase64PrivateKey("rsa-base64-private.key", privateKey);
SaveBase64PublicKey("rsa-base64-public.key", publicKey);
//////////////////////////////////////////////////
//=================分割線=======================//
//////////////////////////////////////////////////
////////////////////////////////////////////////
//讀取bs64儲存的privateKey和publicKey
//RSA::PrivateKey private_Key;
RSA::PublicKey public_Key;
//LoadBase64PrivateKey("rsa-base64-private.key", private_Key);
LoadBase64PublicKey("rsa-base64-public.key", public_Key);
string text= "你好世界", encrypted_text, decrypted_text;
////////////////////////////////////////////////
// 公鑰加密
// 這裡為了驗證有效性
// 直接使用生成的publicKey
RSAES_OAEP_SHA_Encryptor encryptor( public_Key );
StringSource( text, true,
new PK_EncryptorFilter( rng, encryptor,
new StringSink( encrypted_text )
)
);
// 私鑰解密
RSAES_OAEP_SHA_Decryptor decryptor( privateKey );
StringSource( encrypted_text, true,
new PK_DecryptorFilter( rng, decryptor,
new StringSink( decrypted_text )
)
);
cout << decrypted_text << endl;
//assert( text == decrypted_text );
}
catch( CryptoPP::Exception& e )
{
cerr << "Caught Exception..." << endl;
cerr << e.what() << endl;
}
system("pause");
return 0;
}
void Save(const string& filename, const BufferedTransformation& bt)
{
FileSink file(filename.c_str());
bt.CopyTo(file);
file.MessageEnd();
}
void SaveBase64(const string& filename, const BufferedTransformation& bt)
{
Base64Encoder encoder;
bt.CopyTo(encoder);
encoder.MessageEnd();
Save(filename, encoder);
}
void SavePrivateKey(const string& filename, const PrivateKey& key)
{
ByteQueue queue;
key.Save(queue);
Save(filename, queue);
}
void SavePublicKey(const string& filename, const PublicKey& key)
{
ByteQueue queue;
key.Save(queue);
Save(filename, queue);
}
void SaveBase64PrivateKey(const string& filename, const PrivateKey& key)
{
ByteQueue queue;
key.Save(queue);
SaveBase64(filename, queue);
}
void SaveBase64PublicKey(const string& filename, const PublicKey& key)
{
ByteQueue queue;
key.Save(queue);
SaveBase64(filename, queue);
}
void LoadPrivateKey(const string& filename, PrivateKey& key)
{
ByteQueue queue;
Load(filename, queue);
key.Load(queue);
}
void LoadPublicKey(const string& filename, PublicKey& key)
{
ByteQueue queue;
Load(filename, queue);
key.Load(queue);
}
void Load(const string& filename, BufferedTransformation& bt)
{
FileSource file(filename.c_str(), true /*pumpAll*/);
file.TransferTo(bt);
bt.MessageEnd();
}
void LoadBase64(const string& filename, BufferedTransformation& bt)
{
Base64Decoder decoder;
Load(filename,decoder);
decoder.CopyTo(bt);
bt.MessageEnd();
}
void LoadBase64PrivateKey(const string& filename, PrivateKey& key)
{
ByteQueue queue;
LoadBase64(filename, queue);
key.Load(queue);
}
void LoadBase64PublicKey(const string& filename, PublicKey& key)
{
ByteQueue queue;
LoadBase64(filename, queue);
key.Load(queue);
}
注意:
下面是生成的一組祕鑰值,30開頭的值是金鑰的儲存形式。檢視原始碼,可以發現其內部是使用預設使用ASN.1 DER編碼進行持久化。
利用dumpans1,可以對DER編碼進行解釋。圖片中的紅框部分為金鑰種的N值部分。
數字簽名
#include "stdafx.h"
#include "rsa.h"
using CryptoPP::RSA;
using CryptoPP::RSASS;
using CryptoPP::InvertibleRSAFunction;
#include "pssr.h"
using CryptoPP::PSS;
#include "sha.h"
using CryptoPP::SHA1;
#include "files.h"
using CryptoPP::FileSink;
using CryptoPP::FileSource;
#include "filters.h"
using CryptoPP::SignerFilter;
using CryptoPP::SignatureVerificationFilter;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::Integer;
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;
#include "SecBlock.h"
using CryptoPP::SecByteBlock;
#include <string>
using std::string;
#include <iostream>
using std::cout;
using std::endl;
#include <sha.h>
using CryptoPP::SHA;
int main(int argc, char* argv[])
{
try
{
////////////////////////////////////////////////
// 偽隨機數生成器
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize( rng, 1024 );
///////////////////////////////////////
// 生成私鑰和公鑰
RSA::PrivateKey privateKey( parameters ); //(n,e)
RSA::PublicKey publicKey( parameters ); //(n,d)
// 傳送訊息
string message = "Hello,world!";
string signature;
// 生成摘要
SHA hash;
//byte digest[SHA::DIGESTSIZE];
string digest;
digest.resize(SHA::DIGESTSIZE);
hash.CalculateDigest((byte*)digest.data(),(byte*)message.data(),message.length());
// 輸出摘要
for(auto it = digest.begin(); it!=digest.end(); it++)
cout<< std::hex << (*it & 0xff); //16進位制輸出char自動轉int,需要截斷位元組
cout << endl << endl;
////////////////////////////////////////////////
// 利用私鑰進行簽名
RSASS<PSS, SHA1>::Signer signer( privateKey );
StringSource( digest, true,
new SignerFilter( rng, signer,
new StringSink( signature )
) // SignerFilter
); // StringSource
// 輸出摘要加密結果
for(auto it = signature.begin(); it!=signature.end(); it++)
cout<< std::hex << (*it & 0xff); //16進位制輸出char自動轉int,需要截斷位元組
cout << endl << endl;
////////////////////////////////////////////////
// 利用公鑰校驗與恢復
RSASS<PSS, SHA1>::Verifier verifier( publicKey );
StringSource( digest+signature, true,
new SignatureVerificationFilter(
verifier, NULL,
SignatureVerificationFilter::THROW_EXCEPTION
) // SignatureVerificationFilter
); // StringSource
} //SignatureVerificationFilter::THROW_EXCEPTION 驗證失敗丟擲異常
catch( CryptoPP::Exception& e ) {
std::cerr << "Error: " << e.what() << std::endl;
}
system("pause");
return 0;
}