关于Cocos2d-x数据加密解密
在Cocos2d-x中,操作数据库的实现都封装在LocalStorage类中,使用的是sqlite3。下面将分别介绍IOS、Win32平台以及Android平台的数据库数据项加密方法。
IOS、Win32平台的加密
步骤1:添加Base64库
将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[decodeStr.size() + 1];
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在函数结束后会被销毁,指针所指向的就是垃圾数据。
该加密方案存在的问题
- 如果他人知道所使用的加密算法,通过程序计算出加密串,仍可以对数据进行修改。
- 数据库仍可以使用相关工具打开并查看数据表。
- 每次读写数据时增加了加密解密步骤,会降低效率。
对于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;
}