【加密】Cocos2d-x PNG图片资源加密(修改版)


【说明】

这篇文章是对上一篇 【Cocos2d-x PNG图片资源加密】的补充和扩展,那篇文章转自【旧时尘安】的博客,文中已经对原理和使用讲解的很清晰,这里只是根据我自己的使用情况做一些小小的功能扩展,也是自己做个整理,以便日后使用。如有侵权,请联系删除。


【链接】

原文地址:http://www.cnblogs.com/zhangpanyi/p/4560297.html

原始工程:https://github.com/zhangpanyi/EncryptPNG


【使用】

修改后的使用有所调整,原文的使用更简洁,这里主要是依照我个人的习惯作出的调整。我在代码中添加了设置密钥和扩展名的接口。

1. 在 cocos 目录下新建文件夹 ext ,将 CCAES.cpp、CCAES.h、CCDecryptImage.cpp、CCDecryptImage.h 拷贝到其中。

2. 在Xcode项目中引用 ext 目录。

注:这里可能会报错,为此我耽误了半天,结果居然是Xcode没有自动引用导致的,我已记录到 【这里】

3. Android项目需要修改 cocos/Android.mk 文件,将两个cpp文件添加进去即可。

4. 在 CCImage 中调用解密代码,方法与原文一样,这里略有修改,代码见附录。

注:我将对 CCDecryptImage.h 的引用放在了 CCImage.h ,是为了在项目中设置密钥时不需要再引用此头文件。

5. 在项目中设置密钥和扩展名,我是在 AppDelegate.cpp 中设置。

注:如果修改扩展名,需要加密端也做修改,保证两边扩展名一致。

// 设置密钥
const ext::aes_key key = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
ext::DecryptImageConfig(key, ".epng");

【代码】

CCImage.cpp

bool Image::initWithImageFile(const std::string& path)
{
    ......

    if (!data.isNull())
    {
        // 图片文件解密
        if (ext::AnalyzeExtension(path)[1] == ext::TARGET_EXTENSION)
		{
			auto image_data = ext::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)
{
    ......

    if (!data.isNull())
    {
        // 图片文件解密
        if (ext::AnalyzeExtension(fullpath)[1] == ext::TARGET_EXTENSION)
		{
            auto image_data = ext::DecryptImage(fullpath, data);
			ret = initWithImageData(&image_data[0], image_data.size());
		}
		else
		{
			ret = initWithImageData(data.getBytes(), data.getSize());
		}
    }

    return ret;
}

<span style="font-family: Arial, Helvetica, sans-serif;">CCDecryptImage.h</span>

#ifndef __CC_DECRYPT_IMAGE_H__
#define __CC_DECRYPT_IMAGE_H__

#include <array>
#include <vector>
#include "CCData.h"
#include "CCAES.h"

namespace ext
{
    /* 解密扩展名 */
    static std::string TARGET_EXTENSION = ".epng";
    
    /* 解密密钥 */
    static aes_key SECRET_KEY = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 };
    
    /**
     * 设置解密秘钥
     * @param key   秘钥(AES的16位秘钥)
     * @param exten 文件扩展名(需要解密的文件扩展名,如".png")
     */
    void DecryptImageConfig(const aes_key &key, const std::string &exten = TARGET_EXTENSION);
    
	/**
	 * 解密图片文件(在CCImage中调用)
	 * @param filename 文件名称
	 * @param data 文件数据
	 */
    std::vector<unsigned char> DecryptImage(const std::string &filename, cocos2d::Data &data);
    
    /**
     * 分解文件名的扩展名(在CCImage中调用)
     * @param file_path 文件名
     */
    std::array<std::string, 2> AnalyzeExtension(const std::string &file_path);
    
}

#endif

CCDecryptImage.cpp
#include "CCDecryptImage.h"

#include <sstream>
#include "ccMacros.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include <WinSock.h>
#pragma comment(lib, "ws2_32.lib")
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <netinet/in.h>
#endif

namespace ext
{
	/* CRC码长度 */
	static const uint32_t CRC_SIZE = 4;

	/* 文件头部 */
	static const unsigned char HEAD_DATA[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };

	/* IEND CRC码 */
	static const unsigned char IEND_DATA[] = { 0xae, 0x42, 0x60, 0x82 };

	/* 数据块头部(用于验证解密是否成功) */
	static const unsigned char BLOCK_HEAD[] = { 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x50, 0x4e, 0x47 };

#pragma pack(push, 1)

	struct Block
	{
		char name[4];
		uint32_t pos;
		uint32_t size;
	};

	struct IHDRBlock
	{
		Block block;
		char data[13 + CRC_SIZE];
	};

#pragma pack(pop)

    /* 解析文件扩展名 */
	std::array<std::string, 2> AnalyzeExtension(const std::string &file_path)
	{
		std::string::size_type pos = file_path.rfind('.');
		std::array<std::string, 2> text;
		if (std::string::npos != pos)
		{
			text[1] = file_path.substr(pos);
			text[0] = file_path.substr(0, pos);
		}
		else
		{
			text[0] = file_path;
		}
		return text;
	}

	template <int _Value, typename _Stream>
	std::array<char, _Value> ReadSome(_Stream &stream)
	{
		std::array<char, _Value> buffer;
		for (unsigned int i = 0; i < _Value; ++i) buffer[i] = stream.get();
		return buffer;
	}

    /* 解密块 */
	void DecryptBlock(std::stringstream &ss, const aes_key &key)
	{
		const std::streamoff contents_size = ss.tellp() - ss.tellg();
		const uint32_t block_size = (uint32_t)(contents_size + AES_BLOCK_SIZE - contents_size % AES_BLOCK_SIZE);
		std::vector<uint8_t> buffer;
		buffer.resize(block_size);
		for (uint32_t i = 0; i < contents_size; ++i) buffer[i] = ss.get();
		AES::DecryptData(&buffer[0], block_size, key);
		ss.seekg(0); ss.seekp(0);
		for (uint32_t i = 0; i < block_size; ++i) ss.put(buffer[i]);
	}

    /* 解密图片文件 */
    std::vector<unsigned char> DecryptImage(const std::string &filename, cocos2d::Data &data)
	{
		CCAssert(!data.isNull(), "data is null!");

		// 获取数据块信息位置
		const uint32_t block_start_pos = ntohl(*reinterpret_cast<uint32_t *>(data.getBytes() + data.getSize() - sizeof(uint32_t)));

		// 获取数据块信息
		std::stringstream block_info;
		for (uint32_t i = block_start_pos; i < data.getSize() - sizeof(uint32_t); ++i)
		{
			block_info.put(*(data.getBytes() + i));
		}

		// 解密数据块信息
		DecryptBlock(block_info, SECRET_KEY);

		// 验证数据块信息是否解密成功
		auto block_head = ReadSome<sizeof(BLOCK_HEAD)>(block_info);
		for (unsigned int i = 0; i < block_head.size(); ++i)
		{
			if (block_head[i] != BLOCK_HEAD[i])
			{
				CCAssert(false, "the key is wrong!");
			}
		}

		// 写入文件头信息
		std::vector<unsigned char> image_data;
		image_data.reserve(data.getSize());
		for (auto ch : HEAD_DATA) image_data.push_back(ch);

		// 写入数据块信息
		while (true)
		{
			Block block;
			memcpy(&block, &(ReadSome<sizeof(Block)>(block_info)[0]), sizeof(Block));
			if (block_info.eof())
			{
				CCAssert(false, "");
				CCLOG("the %s file format error!", filename.c_str());
			}

			// 写入数据块长度和名称
			char size_buffer[sizeof(block.size)];
			memcpy(size_buffer, &block.size, sizeof(size_buffer));
			for (auto ch : size_buffer) image_data.push_back(ch);
			for (auto ch : block.name) image_data.push_back(ch);

			block.pos = ntohl(block.pos);
			block.size = ntohl(block.size);

			char block_name[sizeof(block.name) + 1] = { 0 };
			memcpy(block_name, block.name, sizeof(block.name));
			if (strcmp(block_name, "IHDR") == 0)
			{
				IHDRBlock ihdr;
				memcpy(&ihdr, &block, sizeof(Block));
				memcpy(((char *)&ihdr) + sizeof(Block), &ReadSome<sizeof(IHDRBlock) - sizeof(Block)>(block_info)[0], sizeof(IHDRBlock) - sizeof(Block));
				for (auto ch : ihdr.data) image_data.push_back(ch);
			}
			else if (strcmp(block_name, "IEND") == 0)
			{
				for (auto ch : IEND_DATA) image_data.push_back(ch);
				CCLOG("decrypt %s success!", filename.c_str());
				break;
			}
			else
			{
				for (uint32_t i = 0; i < block.size + CRC_SIZE; ++i)
				{
					image_data.push_back(*(data.getBytes() + block.pos + i));
				}
			}
		}
		return image_data;
	}
    
    /* 配置解密秘钥和扩展名 */
    void DecryptImageConfig(const aes_key &key, const std::string &exten)
    {
        SECRET_KEY = key;
        TARGET_EXTENSION = exten;
    }
}

【附录】

修改版:http://pan.baidu.com/s/1bnCWEyN



猜你喜欢

转载自blog.csdn.net/ldpjay/article/details/46454999