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 => 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(),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",value); #endif int ok = sqlite3_bind_text(_stmt_update,1,-1,sqlITE_TRANSIENT); ok |= sqlite3_bind_text(_stmt_update,2,value,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,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",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; }