1. 程式人生 > >Cocos2d-x資料加密解密詳解

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