【加密】Cocos2d-x PNG圖片資源加密
實現原理
如果你對實現原理並不感興趣,只是想直接使用的話可以跳過這節。首先我假設讀者已經清楚PNG影象檔案結構了,如果沒有的話這裡建議可以看看《揭祕資料解密的關鍵技術》第5章,這章裡面專門對PNG影象格式進行了介紹。或者看看《PNG檔案格式詳解》這篇文章。
實現原理很簡單,只是將PNG檔案的檔案頭和每個資料塊的名稱給去掉,特殊的資料塊(如IHDR和IEND)連資料塊內容也一併給去掉。在這之後,已經找不到PNG的特徵資訊了。但是要怎麼解密呢?我將去掉的那些資訊按照一定格式儲存起來,然後進行AES加密,最後把加密後的資料追加到檔案尾,必須要金鑰正確的情況下才能解密成功。這樣做好處就是解密的效率高。一個檔案大概只需加密幾十位元組,而不必加密整個檔案,這將大大提升解密時的速度。
如何使用
首先說一下加密解密最簡單的方法。先去這裡下載我已經編譯好的加密和解密程式,如果無法執行你可以自己編譯原始碼來生成程式(必須要支援C++11的編譯器)。
要加密PNG圖片,就把 EncryptPNG.exe 檔案放到圖片所在的檔案目錄裡面執行,然後輸入金鑰就可以了。然後它就會自動加密所在目錄及其子目錄的所有PNG圖片,並在生成對應的 .epng 檔案。
如果想要驗證檔案是否能夠成功解密,只需開啟命令視窗,輸入 DecryptPNG.exe xxx.epng,然後輸入金鑰。如果金鑰正確的話就會生成一個 .png 檔案。這都很簡單吧?
在cocos2d-x 上的使用
在cocos2d-x上的使用更為簡單,就跟平時建立精靈的方法是一樣的。例如:
// add "HelloWorld" splash screen" auto sprite = Sprite::create("HelloWorld.epng"); // position the sprite on the center of the screen sprite->setPosition(Vec2(visibleSize / 2) + origin); // add the sprite as a child to this layer this->addChild(sprite);
就跟使用普通的PNG圖片一樣吧?不過在這之前你需要修改一下引擎程式碼。這裡以cocos2d-x 3.6 為例,首先下載這些檔案。然後把裡面的所有檔案拷貝到cocos2d\cocos\base 目錄下,在cocos2d\cocos\Android.mk的 LOCAL_SRC_FILES 裡面新增 base/CCAES.cpp 和 base/CCDecryptImage.cpp。最後還需要做一件事情,編輯cocos2d\cocos\platform\CCImage.cpp檔案,新增標頭檔案 #include "base/CCDecryptImage.h" ,然後修改 initWithImageFile 和 initWithImageFileThreadSafe 函式,將 if (!data.isNull()) 裡面的內容替換成下面的:
bool Image::initWithImageFile(const std::string& path)
{
bool ret = false;
_filePath = FileUtils::getInstance()->fullPathForFilename(path);
#ifdef EMSCRIPTEN
// Emscripten includes a re-implementation of SDL that uses HTML5 canvas
// operations underneath. Consequently, loading images via IMG_Load (an SDL
// API) will be a lot faster than running libpng et al as compiled with
// Emscripten.
SDL_Surface *iSurf = IMG_Load(fullPath.c_str());
int size = 4 * (iSurf->w * iSurf->h);
ret = initWithRawData((const unsigned char*)iSurf->pixels, size, iSurf->w, iSurf->h, 8, true);
unsigned int *tmp = (unsigned int *)_data;
int nrPixels = iSurf->w * iSurf->h;
for(int i = 0; i < nrPixels; i++)
{
unsigned char *p = _data + i * 4;
tmp[i] = CC_RGB_PREMULTIPLY_ALPHA( p[0], p[1], p[2], p[3] );
}
SDL_FreeSurface(iSurf);
#else
Data data = FileUtils::getInstance()->getDataFromFile(_filePath);
if (!data.isNull())
{
if (splitext(path)[1] == ".epng")
{
auto image_data = DecryptImage(path, data);
ret = initWithImageData(&image_data[0], image_data.size());
}
else
{
ret = initWithImageData(data.getBytes(), data.getSize());
}
}
#endif // EMSCRIPTEN
return ret;
}
bool Image::initWithImageFileThreadSafe(const std::string& fullpath)
{
bool ret = false;
_filePath = fullpath;
Data data = FileUtils::getInstance()->getDataFromFile(fullpath);
if (!data.isNull())
{
if (splitext(fullpath)[1] == ".epng")
{
auto image_data = DecryptImage(fullpath, data);
ret = initWithImageData(&image_data[0], image_data.size());
}
else
{
ret = initWithImageData(data.getBytes(), data.getSize());
}
}
return ret;
}
好了大功告成。金鑰在CCDecryptImage.cpp檔案 DEAULT_KEY 裡設定(預設是123456),現在可以執行最開始的那段程式碼試試能否成功解密了。
原始碼下載
C++實現的加密和解密的原始碼
參考文章:http://cn.cocos2d-x.org/tutorial/show?id=2908