Cocos2d-x資料加密解密詳解
C++的Base64演算法實現
/* * base64.cpp * * Created on: 30/04/2011 * Author: nicholas */ #include "base64.h" #include <cctype> //#include <cstdint> #include <algorithm> namespace base64 { namespace { static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t encoded_size(size_t raw) { switch((raw*8)%6) { case 0: return (raw*8)/6; case 2: return ((raw*8)/6) + 3; case 4: return ((raw*8)/6) + 2; } return raw; } size_t decoded_size(size_t unpadded) { return (unpadded*6)/8; } int base64_index(std::string::value_type c) { if(c >= 'A' && c <= 'Z') return c-'A'; else if(c >= 'a' && c <= 'z') return c-'a' + 26; else if(c >= '0' && c <= '9') return c-'0' + 52; else if(c == '+') return 62; else if(c == '/') return 63; else return -1; } } encode_t::encode_t(std::string::size_type size) : state(zero), remainder(0) { encoded.reserve(encoded_size(size)); } /* State zero 8 bits input, zero remaining from last 6 bits consumed, 2 remaining => two State two 8 bits input, 2 remaining from last 4 bits consumed, 4 remaining => four State four 8 bits input, 4 remaining from last 2 bits consumed, 6 remaining 6 bits input, 6 remaining from last 6 bits consumed, 0 remaining => zero */ void encode_t::operator() (std::string::value_type c) { unsigned char value(0); switch(state) { case zero: value = (c & 0xfc) >> 2; remainder = c & 0x3; encoded.push_back(BASE64_CHARS[value]); state = two; break; case two: value = (remainder << 4) | ((c & 0xf0) >> 4); remainder = c & 0xf; encoded.push_back(BASE64_CHARS[value]); state = four; break; case four: value = (remainder << 2) | ((c & 0xc0) >> 6); remainder = c & 0x3f; encoded.push_back(BASE64_CHARS[value]); value = remainder; encoded.push_back(BASE64_CHARS[value]); state = zero; break; } } std::string encode_t::str() { unsigned char value(0); switch(state) { case zero: break; case two: value = remainder << 4; encoded.push_back(BASE64_CHARS[value]); encoded.push_back('='); encoded.push_back('='); state = zero; break; case four: value = remainder << 2; encoded.push_back(BASE64_CHARS[value]); encoded.push_back('='); state = zero; break; } return encoded; } decode_t::decode_t(std::string::size_type size) : state(zero), remainder(0) { decoded.reserve(decoded_size(size)); } /* State zero 6 bits input, zero remaining from last 6 bits consumed, zero remaining => six State six 6 bits input, 6 remaining from last write 1 byte, 4 remaining => four State four 6 bits input, 4 remaining from last write 1 byte, 2 remaining => two State two 6 bits input, 2 remaining from last write 1 byte, 0 remaining => zero */ void decode_t::operator() (std::string::value_type c) { unsigned char value(0); int index = base64_index(c); if(index == -1) return; switch(state) { case zero: remainder = index; state = six; break; case six: value = (remainder << 2) | ((index & 0x30) >> 4); remainder = index & 0xf; decoded.push_back(value); state = four; break; case four: value = (remainder << 4) | ((index & 0x3c) >> 2); remainder = index & 0x3; decoded.push_back(value); state = two; break; case two: value = (remainder << 6) | index; decoded.push_back(value); state = zero; break; } } std::string decode_t::str() const { return decoded; } std::string encode(const std::string& str) { return std::for_each(str.begin(), str.end(), encode_t(str.size())).str(); } std::string decode(const std::string& str) { size_t unpadded_size = str.size(); if(str.size() > 0 && str[str.size()-1] == '=') unpadded_size -= 1; if(str.size() > 1 && str[str.size()-2] == '=') unpadded_size -= 1; return std::for_each(str.begin(), str.end(), decode_t(unpadded_size)).str(); } }
/* * base64.h * * Created on: 30/04/2011 * Author: nicholas */ #ifndef BASE64_H_ #define BASE64_H_ #include <string> namespace base64 { class encode_t { private: enum { zero = 0, two, four } state; unsigned int remainder; std::string encoded; public: encode_t(std::string::size_type size); void operator() (std::string::value_type c); std::string str(); }; class decode_t { private: enum { zero = 0, six, four, two } state; unsigned int remainder; std::string decoded; public: decode_t(std::string::size_type size); void operator() (std::string::value_type c); std::string str() const; }; /* * Encode the given string @str into its base64 representation */ std::string encode(const std::string& str); /* * Decode the base64 encoded string @str */ std::string decode(const std::string& str); } #endif /* BASE64_H_ */
#include<iostream> #include"base64.h" using namespace std; int main () { string str = "abcdefghijklmn."; string out = base64::encode(str); cout<<"src:"<<str<<endl; cout<<"encode: "<<out<<endl; cout<<"decode: "<<base64::decode(out)<<endl; str = "ä¸ć."; out = base64::encode(str); cout<<endl<<"src:"<<str<<endl; cout<<"encode: "<< out <<endl; cout<<"decode: "<< base64::decode(out) <<endl; system("pause"); return 1; }
Android平臺下使用Base64演算法加密解密資料
import com.example.base64.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.Base64;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String src = "abcdefjhijklmn.";
// 加密資料
String encode = Base64.encodeToString(src.getBytes(), Base64.DEFAULT);
// 解密資料
String decode = new String(Base64.decode(encode, Base64.DEFAULT));
System.out.println("src:" + src);
System.out.println("encode:" + encode);
System.out.println("decode:" + decode);
}
}
資料庫資料項加密
Cocos2d-x中操作資料庫的實現都封裝在LocalStorage這個類中。使用的是sqlite3。
iOS、Win32平臺的加密
1.base64.h和base64.cpp新增專案Classes目錄下。
2.右鍵libExtensions專案,附加包含目錄,把base64庫所在目錄新增到包含目錄中,具體路徑根據自己專案結構而定3.修改localStorageSetItem方法,儲存資料時加密資料,這裡在win32平臺下忽略加密操作是因為一般win32平臺版本是用於內部測試的
void localStorageSetItem( const char *key, const char *value)
{
assert( _initialized );
// 加密資料
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
//CCLOG("key=%s src=%s", key, value);
std::string encodeStr = base64::encode(value);
value = encodeStr.c_str();
//CCLOG("key=%s encode=%s", key, value);
#endif
int ok = sqlite3_bind_text(_stmt_update, 1, key, -1, SQLITE_TRANSIENT);
ok |= sqlite3_bind_text(_stmt_update, 2, value, -1, SQLITE_TRANSIENT);
ok |= sqlite3_step(_stmt_update);
ok |= sqlite3_reset(_stmt_update);
if( ok != SQLITE_OK && ok != SQLITE_DONE)
printf("Error in localStorage.setItem()\n");
}
4.修改localStorageGetItem方法,獲取資料時解密資料
const char* localStorageGetItem( const char *key )
{
assert( _initialized );
int ok = sqlite3_reset(_stmt_select);
ok |= sqlite3_bind_text(_stmt_select, 1, key, -1, SQLITE_TRANSIENT);
ok |= sqlite3_step(_stmt_select);
const unsigned char *ret = sqlite3_column_text(_stmt_select, 0);
if( ok != SQLITE_OK && ok != SQLITE_DONE && ok != SQLITE_ROW)
printf("Error in localStorage.getItem()\n");
// 加密資料
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
//CCLOG("decode src=%s", ret);
if (ret)
{
std::string decodeStr = base64::decode((const char*)ret);
char* c = new char;
strcpy_s(c, decodeStr.size() + 1, decodeStr.c_str());
//CCLOG("key=%s decode=%s", key, c);
return c;
}
#endif
return (const char*)ret;
}
注意:c_str()方法返回的是string物件中保留的字串指標,該指標的生命週期是跟隨string物件的。也就是如果直接返回decodeStr.c_str(),返回的將是垃圾資料,因為decodeStr在函式結束後,已經被銷燬了,指標所指向的是垃圾資料。
這種資料庫加密方案存在以下幾個問題:
1.如果別人知道我所使用的加密演算法,然後通過程式計算出加密串,還是可以修改成功的。
2.資料庫還是可以用相關工具開啟,並檢視資料表。
3.每次讀寫資料時,增加了加密解密的步驟,降低效率。
對於Cocos2d-x中資料庫的加密有更好的解決辦法,就是使用wxsqlite3,點選檢視【整合wxSqlite3到Cocos2d-x】,這種資料庫加密的實現是在初始化資料庫的時候加密,執行時載入資料庫時候呼叫相關api解密,載入完成後資料的讀寫效率和未加密時一樣,相對比較高效。
Android平臺的加密
從LocalStorage.cpp中使用的巨集可以看出,這個實現在安卓平臺下是無法使用的。安卓平臺下的實現在:cocos2d-x-2.1.5\extensions\LocalStorage目錄下的LocalStorageAndroid.cpp中。
從localStrorageInit的實現可以看出,它是通過jni呼叫了java層中org/cocos2dx/lib/Cocos2dxLocalStorage的靜態方法init。
在打包安卓版Cocos2d-x遊戲時需要用到引擎的一個java庫,在cocos2d-x-2.1.5\cocos2dx\platform\android\java目錄下,Cocos2dxLocalStorage類就在這個庫中。
修改這個類的setItem方法,在儲存資料的時候加密public static void setItem(String key, String value) {
try {
// 加密資料
value = Base64.encodeToString(value.getBytes(), Base64.DEFAULT);
String sql = "replace into " + TABLE_NAME
+ "(key,value)values(?,?)";
mDatabase.execSQL(sql, new Object[] { key, value });
} catch (Exception e) {
e.printStackTrace();
}
}
修改這個類的getItem方法,在獲取資料的時候解密
public static String getItem(String key) {
String ret = null;
try {
String sql = "select value from " + TABLE_NAME + " where key=?";
Cursor c = mDatabase.rawQuery(sql, new String[] { key });
while (c.moveToNext()) {
// only return the first value
if (ret != null) {
Log.e(TAG, "The key contains more than one value.");
break;
}
ret = c.getString(c.getColumnIndex("value"));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
// 解密資料
if (ret != null) {
ret = new String(Base64.decode(ret, Base64.DEFAULT));
}
return ret == null ? "" : ret;
}