openssl是可以很方便加密解密的庫,可以使用它來對需要在網絡中傳輸的數據加密。可以使用非對稱加密:公鑰加密,私鑰解密。openssl提供了對RSA的支持,但RSA存在計算效率低的問題,所以一般的做法是使用對稱密鑰加密數據,然后再把這個只在當前有效的臨時生成的對稱密鑰用非對稱密鑰的公鑰加密之后傳遞給目標方,目標方使用約定好的非對稱密鑰中的私鑰解開,得到數據加密的密鑰,再進行數據解密,得到數據,這種使用方式很常見,可以認為是對HTTPS的裁剪。對稱密鑰加密可以選擇AES,比DES更優秀。
openssl庫來自
http://www.openssl.org/,下載到openssl源碼之后,開始編譯:
產生動態庫的做法:
1、安裝ActivePerl
2、進入OpenSSL所在文件夾,運行:perl Configure VC-WIN32 --prefix=C:\openssl-dll
3、進入VC/BIN目錄,運行 VCVARS32.BAT 設置環境變量
4、返回OpenSSL目錄,運行 ms\do_ms
5、在OpenSSL目錄下執行編譯 nmake -f ms\ntdll.mak
6、把必要生成物拷貝到prefix定義的目錄中 nmake -f ms\ntdll.mak install
注意:可以通過修改ntdll.mak文件中的CFLAG,確定編譯MT、MD庫
產生靜態庫的做法:
1、安裝ActivePerl
2、perl configure VC-WIN32 --prefix=C:\openssl-lib
3、ms\do_ms.bat
4、nmake -f ms\nt.mak
5、nmake -f ms\nt.mak install
注意:可以通過修改nt.mak文件中的CFLAG,確定編譯MT、MD庫。重編的時候把生成物刪掉。
RSA加解密需要先用openssl工具生成RSA公鑰和RSA私鑰。方法:
1、產生私鑰:openssl genrsa -out privkey.pem 1024;
2、根據私鑰產生公鑰: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加密例子:
編譯資料補充: