使用openssl庫實現RSA、AES數據加密

     openssl是能夠很方便加密解密的庫,可使用它來對須要在網絡中傳輸的數據加密。可使用非對稱加密:公鑰加密,私鑰解密。openssl提供了對RSA的支持,但RSA存在計算效率低的問題,因此通常的作法是使用對稱密鑰加密數據,而後再把這個只在當前有效的臨時生成的對稱密鑰用非對稱密鑰的公鑰加密以後傳遞給目標方,目標方使用約定好的非對稱密鑰中的私鑰解開,獲得數據加密的密鑰,再進行數據解密,獲得數據,這種使用方式很常見,能夠認爲是對HTTPS的裁剪。對稱密鑰加密能夠選擇AES,比DES更優秀。
     openssl庫來自 http://www.openssl.org/,下載到openssl源碼以後,開始編譯:
      產生動態庫的作法
一、安裝ActivePerl
二、進入OpenSSL所在文件夾,運行:perl Configure VC-WIN32 --prefix=C:\openssl-dll
三、進入VC/BIN目錄,運行 VCVARS32.BAT 設置環境變量
四、返回OpenSSL目錄,運行 ms\do_ms
五、在OpenSSL目錄下執行編譯 nmake -f ms\ntdll.mak
六、把必要生成物拷貝到prefix定義的目錄中 nmake -f ms\ntdll.mak install
注意:能夠經過修改ntdll.mak文件中的CFLAG,肯定編譯MT、MD庫
      產生靜態庫的作法
一、安裝ActivePerl
二、perl configure VC-WIN32 --prefix=C:\openssl-lib
三、ms\do_ms.bat
四、nmake -f ms\nt.mak
五、nmake -f ms\nt.mak install
注意:能夠經過修改nt.mak文件中的CFLAG,肯定編譯MT、MD庫。重編的時候把生成物刪掉。
      RSA加解密須要先用openssl工具生成RSA公鑰和RSA私鑰。方法:
一、產生私鑰:openssl genrsa -out privkey.pem 1024;
二、根據私鑰產生公鑰:openssl rsa -in privkey.pem -pubout。
1024只是測試用,使用2048位才比較安全。
      RSA加密部分代碼demo
    std::string EncodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
    {
        if (strPemFileName.empty() || strData.empty())
        {
            assert(false);
            return "";
        }
        FILE* hPubKeyFile = NULL;
        if(fopen_s(&hPubKeyFile, strPemFileName.c_str(), "rb") || hPubKeyFile == NULL)
        {
            assert(false);
            return ""; 
        }
        std::string strRet;
        RSA* pRSAPublicKey = RSA_new();
        if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPublicKey, 0, 0) == NULL)
        {
            assert(false);
            return "";
        }

        int nLen = RSA_size(pRSAPublicKey);
        char* pEncode = new char[nLen + 1];
        int ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
        if (ret >= 0)
        {
            strRet = std::string(pEncode, ret);
        }
delete[] pEncode; RSA_free(pRSAPublicKey); fclose(hPubKeyFile); CRYPTO_cleanup_all_ex_data();
return strRet; }
      RSA解密部分代碼demo
    std::string DecodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
    {
        if (strPemFileName.empty() || strData.empty())
        {
            assert(false);
            return "";
        }
        FILE* hPriKeyFile = NULL;
        if(fopen_s(&hPriKeyFile, strPemFileName.c_str(),"rb") || hPriKeyFile == NULL)
        {
            assert(false);
            return "";
        }
        std::string strRet;
        RSA* pRSAPriKey = RSA_new();
        if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL)
        {
            assert(false);
            return "";
        }
        int nLen = RSA_size(pRSAPriKey);
        char* pDecode = new char[nLen+1];

        int ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPriKey, RSA_PKCS1_PADDING);
        if(ret >= 0)
        {
            strRet = std::string((char*)pDecode, ret);
        }
delete [] pDecode; RSA_free(pRSAPriKey); fclose(hPriKeyFile); CRYPTO_cleanup_all_ex_data();
return strRet; }
     RSA的API中當使用參數RSA_PKCS1_PADDING時,明文長度不能大於密文長度-11;當使用參數RSA_NO_PADDING時,明文長度須要正好是128。
      AES加密部分代碼
    std::string EncodeAES( const std::string& password, const std::string& data )
    {
        AES_KEY aes_key;
        if(AES_set_encrypt_key((const unsigned char*)password.c_str(), password.length() * 8, &aes_key) < 0)
        {
            assert(false);
            return "";
        }
        std::string strRet;
        std::string data_bak = data;
        unsigned int data_length = data_bak.length();
        int padding = 0;
        if (data_bak.length() % AES_BLOCK_SIZE > 0)
        {
            padding =  AES_BLOCK_SIZE - data_bak.length() % AES_BLOCK_SIZE;
        }
        data_length += padding;
        while (padding > 0)
        {
            data_bak += '\0';
            padding--;
        }
        for(unsigned int i = 0; i < data_length/AES_BLOCK_SIZE; i++)
        {
            std::string str16 = data_bak.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
            unsigned char out[AES_BLOCK_SIZE];
            ::memset(out, 0, AES_BLOCK_SIZE);
            AES_encrypt((const unsigned char*)str16.c_str(), out, &aes_key);
            strRet += std::string((const char*)out, AES_BLOCK_SIZE);
        }
        return strRet;
    }
     AES解密部分代碼
    std::string DecodeAES( const std::string& strPassword, const std::string& strData )
    {
        AES_KEY aes_key;
        if(AES_set_decrypt_key((const unsigned char*)strPassword.c_str(), strPassword.length() * 8, &aes_key) < 0)
        {
            assert(false);
            return "";
        }
        std::string strRet;
        for(unsigned int i = 0; i < strData.length()/AES_BLOCK_SIZE; i++)
        {
            std::string str16 = strData.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
            unsigned char out[AES_BLOCK_SIZE];
            ::memset(out, 0, AES_BLOCK_SIZE);
            AES_decrypt((const unsigned char*)str16.c_str(), out, &aes_key);
            strRet += std::string((const char*)out, AES_BLOCK_SIZE);
        }
        return strRet;
    }
     AES加密,塊大小必須爲128位(16字節),若是不是,則要補齊,密鑰長度能夠選擇128位、192位、256位。
 
     不一樣語言解密補充
  使用python解密的時候,public key可能要求是PKCS#1格式,而openssl是不支持的,openssl默認是x509格式的public key,爲此,若是要把上邊生成的public key提供給python使用,須要先從x509格式轉換爲PKCS#1格式。網絡上的資料顯示,php有一個api支持這種轉換,但我沒試過。因爲個人私鑰是2048位的,因此能夠很方便的實現x509轉PKCS#1,轉換是可逆的,說下PKCS#1轉x509的方法:首先刪除head和foot的「RSA」,而後在第二行開頭增長文本「MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A」,最後,對齊文本。若是私鑰不是2048的怎麼辦呢?可使用php的api轉一下了,或者到網上查找轉換的資料解決。 RSA key Formats。 2013.11.27.
  
 
 
一些資料:
 
明文長度限制:
 
AES加密例子:
 
編譯資料補充:
相關文章
相關標籤/搜索