1. 程式人生 > >【加密】Cocos2d-x PNG圖片資源加密

【加密】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