1. 程式人生 > >基於cyptopp庫的rsa加解密詳解

基於cyptopp庫的rsa加解密詳解

編譯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;
}

參考