C++实现对16进制字符串和字节数组的tea加密和解密算法

2020-01-13 17:51:55 浏览数 (1)

TEA(Tiny Encryption Algorithm) 是一种简单高效的加密算法,以加密解密速度快,实现简单著称。算法真的很简单,TEA算法每一次可以操作64-bit(8-byte),采用128-bit(16-byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮。 TEA 算法最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计的。该算法使用 128 位的密钥为 64 位的信息块进行加密,它需要进行 64 轮迭代,尽管作者认为 32 轮已经足够了。该算法使用了一个神秘常数δ作为倍数,它来源于黄金比率,以保证每一轮加密都不相同。但δ的精确值似乎并不重要,这里 TEA 把它定义为 δ=「(√5 - 1)231」(也就是程序中的 0×9E3779B9)。 下面是维基百科中个关于该算法的C语言描述的代码片段,如下:

代码语言:javascript复制
#include <stdint.h>

void encrypt (uint32_t v[2], uint32_t k[4]) {
    uint32_t v0=v[0], v1=v[1], sum=0, i;           /* set up */
    uint32_t delta=0x9E3779B9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i  ) {                         /* basic cycle start */
        sum  = delta;
        v0  = ((v1<<4)   k0) ^ (v1   sum) ^ ((v1>>5)   k1);
        v1  = ((v0<<4)   k2) ^ (v0   sum) ^ ((v0>>5)   k3);
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}

void decrypt (uint32_t v[2], uint32_t k[4]) {
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up; sum is 32*delta */
    uint32_t delta=0x9E3779B9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i  ) {                         /* basic cycle start */
        v1 -= ((v0<<4)   k2) ^ (v0   sum) ^ ((v0>>5)   k3);
        v0 -= ((v1<<4)   k0) ^ (v1   sum) ^ ((v1>>5)   k1);
        sum -= delta;
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}



/************************************************

The Tiny Encryption Algorithm (TEA) by 
David Wheeler and Roger Needham of the
Cambridge Computer Laboratory

**** ANSI C VERSION ****

Notes:

TEA is a Feistel cipher with XOR and
and addition as the non-linear mixing
functions. 

Takes 64 bits of data in v[0] and v[1].
Returns 64 bits of data in w[0] and w[1].
Takes 128 bits of key in k[0] - k[3].

TEA can be operated in any of the modes
of DES. Cipher Block Chaining is, for example,
simple to implement.

n is the number of iterations. 32 is ample,
16 is sufficient, as few as eight may be OK.
The algorithm achieves good dispersion after
six iterations. The iteration count can be
made variable if required.

Note this is optimised for 32-bit CPUs with
fast shift capabilities. It can very easily
be ported to assembly language on most CPUs.

delta is chosen to be the real part of (the
golden ratio Sqrt(5/4) - 1/2 ~ 0.618034
multiplied by 2^32). 

************************************************/

void encipher(unsigned long *const v,unsigned long *const w,
const unsigned long *const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0,delta=0x9E3779B9,
				a=k[0],b=k[1],c=k[2],d=k[3],n=32;

   while(n-->0)
      {
      sum  = delta;
      y  = (z<<4) a ^ z sum ^ (z>>5) b;
      z  = (y<<4) c ^ y sum ^ (y>>5) d;
      }

   w[0]=y; w[1]=z;
}

void decipher(unsigned long *const v,unsigned long *const w,
const unsigned long *const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0xC6EF3720,
				delta=0x9E3779B9,a=k[0],b=k[1],c=k[2],
				d=k[3],n=32;

   /* sum = delta<<5, in general sum = delta * n */

   while(n-->0)
      {
      z -= (y<<4) c ^ y sum ^ (y>>5) d;
      y -= (z<<4) a ^ z sum ^ (z>>5) b;
      sum -= delta;
      }
   
   w[0]=y; w[1]=z;
}

最近碰到一个Java项目的同事要求帮忙使用C 实现Tea加密和解密算法,生成对应的加密和解密DLL接口文件,然后供Java项目调用。应用场景主要是针对设备发送的16进制字节数组进行加密和解密,于是做了一个小例子做测试用。 一、tea算法的C 实现代码 1、首先声明一些头文件的定义文件type.h,代码如下:

代码语言:javascript复制
#ifndef _TYPE_H_
#define _TYPE_H_

// 自定义
typedef unsigned char		uint8;
typedef unsigned short		uint16;
typedef unsigned int		uint32;
#ifdef WIN32
typedef unsigned __int64	uint64;
typedef __int64	 int64;
#else
typedef unsigned long long	uint64;
typedef long long	int64;
#endif
typedef char	int8;
typedef short	int16;
typedef int		int32;

#include <string.h>

// 数组
#include <string>
#include <vector>

typedef std::string	String;
typedef std::vector<uint8>		Uint8Array;
typedef std::vector<uint16>		Uint16Array;
typedef std::vector<uint32>		Uint32Array;
typedef std::vector<uint64>		Uint64Array;
typedef std::vector<int8>		Int8Array;
typedef std::vector<int16>		Int16Array;
typedef std::vector<int32>		Int32Array;
typedef std::vector<int64>		Int64Array;
typedef std::vector<float>		Float32Array;
typedef std::vector<double>		Float64Array;
typedef std::vector<std::string>	StringArray;

#ifdef WIN32
#ifndef snprintf
#define snprintf sprintf_s
#endif
#endif

#endif

2、字符串工具处理类StringTools,为了简单起见直接采用一个func.h的头文件包含一个StringTools类,类中只包含static静态成员函数的实现,包括16进制与字节数组之间的相互转换,func.h代码如下:

代码语言:javascript复制
#ifndef _FUNC_H_
#define _FUNC_H_

#include "type.h"

#if _MSC_VER
#define snprintf _snprintf
#endif

// 字符串工具处理类
class StringTools
{
public:
	static inline StringArray split(const std::string& s, const std::string& sep, const bool compress = false)
	{
		StringArray out;
		int pos = 0;
		int index = -1;
		while ((index = s.find(sep, pos)) != s.npos)
		{
			if (index - pos == 0)
			{

			}
			// s.substr(pos, index - pos == 0 ? 1 : index - pos);
			std::string it = index - pos == 0 ? "" : s.substr(pos, index - pos);
			if (compress && it == "") // 压缩 index - pos == sep.size() && 
			{

			}
			else // 不用压缩
			{
				out.push_back(it);
			}

			pos = index   sep.size();
		}
		if (pos < (int)s.size())
		{
			out.push_back(s.substr(pos));
		}
		return out;
	}

	// 转换hex到字符串显示
	static inline String hex2str(const char* buff, const size_t buffsize, const char* sep = "", bool is_case = false) {
		String out;
		char ch[4];
		const char* fmt = is_case ? "x" : "X";
		for (size_t i = 0; i < buffsize; i  ) {
			sprintf(ch, fmt, buff[i] & 0xFF);
			if (out.empty()) {
				out = ch;
			}
			else {
				out  = sep;
				out  = ch;
			}
		}
		return out;
	}

	// 转换字符串显示到hex数组
	static inline String str2hex(const String& buff, const String& sep = "") {
		String out;
		size_t buffsize = buff.size();
		StringArray items;
		if (sep.empty() && buffsize % 2 == 0) {
			for (size_t i = 0; i < buffsize / 2; i  ) {
				items.emplace_back(buff.substr(i * 2, 2));
			}
		}
		else if (sep.size()) {
			items = split(buff, sep, true);
		}
		for (auto& i : items) {
			int ch;
			if (!to_int(ch, i, 16)) {
				return out;
			}
			out.push_back(ch & 0xff);
		}
		return out;
	}

	// 获取字符串中数字
	static inline bool to_int(int& _return, const std::string& buff, int base = 10, size_t offset = 0, size_t count = String::npos)
	{
		_return = 0;
		if (buff.empty())
			return false;
		try {
			_return = std::stoi(buff.substr(offset, count), 0, base);
		}
		catch (...) {
			return false;
		}
		return true;
	}
};

#endif

3、核心算法tea.h头文件的接口函数申明,如下:

代码语言:javascript复制
#ifndef _TEA_H_
#define _TEA_H_


#include "type.h"

/*******************************加密解密函数************************************/
// 加密核心函数
// v为需要加密的数据
// k为加密的密钥
// round为加密的轮数,和解密相对应
void tea_encode(int32 * v, const int32 * k, int round);

// 少于64为数据加密函数
void tea_encode_byte(char * v, const int32 * k, int32 p, const char* y);

void tea_decode_byte(char * v, const int32 * k, int32 p, const char* y);

// 解密核心函数
// v为需要解密的数据
// k为解密的密钥
// round为解密的轮数,和加密相对应
void tea_decode(int32 * v, const int32 * k, int round);


//加密数据分组运算
void tea_encode_buffer(char * in_buffer, uint32 in_size, const int32 * key,
	int32 cipherRemains, int round, const char* y);


//解密数据分组运算
void tea_decode_buffer(char * in_buffer, uint32 in_size, const int32 * key,
	int cipherRemains, int round, const char* y);

// 加密(传入的是16进制的字符串,输出的也是16进制的字符串)
int tea_encrypt_hexstr(char *buffer_in, int nlen, int32 key[], int round, const char* y);
 
// 解密(传入的是16进制的字符串,输出的也是16进制的字符串)
int tea_decrypt_hexstr(char *buffer_in, int nlen, int32 key[], int round, const char* y);

// 加密(传入的是16进制的字节数组,输出的也是16进制的字节数组)
int tea_encrypt_hexbytes(char *buffer_in, int nlen, int32 key[], int round, const char* y);

// 解密(传入的是16进制的字节数组,输出的也是16进制的字节数组)
int tea_decrypt_hexbytes(char *buffer_in, int nlen, int32 key[], int round, const char* y);

#endif

4、核心tea算法的接口函数的实现代码tea.cpp

代码语言:javascript复制
#include <string.h>

#include "tea.h"

#include <func.h>

/*******************************加密解密函数************************************/
// 加密核心函数
// v为需要加密的数据
// k为加密的密钥
// round为加密的轮数,和解密相对应
void tea_encode(int32 * v, const int32 * k, int round)
{
	uint32 y = 0, z = 0, sum = 0;
	uint32 delta = 0x9e3779b9;
	// uint8_t n = 2, *DataP1;
	uint8_t n = round, *DataP1;
	DataP1 = (uint8_t *)v;
	y = *DataP1;
	DataP1  ;
	y = y   ((uint32)*DataP1 << 8);
	DataP1  ;
	y = y   ((uint32)*DataP1 << 16);
	DataP1  ;
	y = y   ((uint32)*DataP1 << 24);

	DataP1  ;
	z = *DataP1;
	DataP1  ;
	z = z   ((uint32)*DataP1 << 8);
	DataP1  ;
	z = z   ((uint32)*DataP1 << 16);
	DataP1  ;
	z = z   ((uint32)*DataP1 << 24);

	while (n-- > 0)
	{
		sum  = delta;
		y  = ((z << 4)   k[0]) ^ (z   sum) ^ ((z >> 5)   k[1]);
		z  = ((y << 4)   k[2]) ^ (y   sum) ^ ((y >> 5)   k[3]);
	}
	DataP1 = (uint8_t *)v;
	*DataP1 = y;
	DataP1  ;
	*DataP1 = y >> 8;
	DataP1  ;
	*DataP1 = y >> 16;
	DataP1  ;
	*DataP1 = y >> 24;


	DataP1  ;
	*DataP1 = z;
	DataP1  ;
	*DataP1 = z >> 8;
	DataP1  ;
	*DataP1 = z >> 16;
	DataP1  ;
	*DataP1 = z >> 24;
}
/******************************************************************************/

// 少于64为数据加密函数
void tea_encode_byte(char * v, const int32 * k, int32 p, const char* y)
{
	//char y[] = "Wu&Tian"; //固定写的一个字符串,只要与解密相同即可,7字节长度
	if (y != NULL)
	{
		*v = *v ^ y[p] ^ (char)(k[p % 4] % 0xff);
	}
}

/******************************************************************************/
void tea_decode_byte(char * v, const int32 * k, int32 p, const char* y)
{
	//char y[] = "Wu&Tian";
	if (y != NULL)
	{
		*v = *v ^ (char)(k[p % 4] % 0xff) ^ y[p];
	}
}
/******************************************************************************/

// 解密核心函数
// v为需要解密的数据
// k为解密的密钥
// round为解密的轮数,和加密相对应
void tea_decode(int32 * v, const int32 * k, int round)
{
	uint32 y = 0, z = 0, sum = 0, delta = 0x9e3779b9;
	// uint8_t n = 2, *DataP1;
	uint8_t n = round, *DataP1;

	DataP1 = (uint8_t *)v;
	y = *DataP1;
	DataP1  ;
	y = y   ((uint32)*DataP1 << 8);
	DataP1  ;
	y = y   ((uint32)*DataP1 << 16);
	DataP1  ;
	y = y   ((uint32)*DataP1 << 24);

	DataP1  ;
	z = *DataP1;
	DataP1  ;
	z = z   ((uint32)*DataP1 << 8);
	DataP1  ;
	z = z   ((uint32)*DataP1 << 16);
	DataP1  ;
	z = z   ((uint32)*DataP1 << 24);

	sum = delta << 1;
	while (n-- > 0)
	{
		z -= ((y << 4)   k[2]) ^ (y   sum) ^ ((y >> 5)   k[3]);
		y -= ((z << 4)   k[0]) ^ (z   sum) ^ ((z >> 5)   k[1]);
		sum -= delta;
	}
	DataP1 = (uint8_t *)v;
	*DataP1 = y;
	DataP1  ;
	*DataP1 = y >> 8;
	DataP1  ;
	*DataP1 = y >> 16;
	DataP1  ;
	*DataP1 = y >> 24;


	DataP1  ;
	*DataP1 = z;
	DataP1  ;
	*DataP1 = z >> 8;
	DataP1  ;
	*DataP1 = z >> 16;
	DataP1  ;
	*DataP1 = z >> 24;
}
/******************************************************************************/

//加密数据分组运算
void tea_encode_buffer(char * in_buffer, uint32 in_size, const int32 * key,
	int32 cipherRemains, int round, const char* y)
{
	char * p;
	uint32 remain = in_size % 8;//计算出数据8字节整数倍之外的数据

	uint32 align_size = in_size - remain;

	for (p = in_buffer; p < in_buffer   align_size; p  = 8)
		tea_encode((int32 *)p, key, round);
	if (remain > 0 && cipherRemains)
		for (p = in_buffer   align_size; p < in_buffer   in_size; p  = 1)
			tea_encode_byte(p, key, --remain, y);
}
/******************************************************************************/

//解密数据分组运算
void tea_decode_buffer(char * in_buffer, uint32 in_size, const int32 * key,
	int cipherRemains, int round, const char* y)
{
	char * p;
	uint32 remain = in_size % 8;
	uint32 align_size = in_size - remain;

	for (p = in_buffer; p < in_buffer   align_size; p  = 8)
		tea_decode((int32 *)p, key, round);//8字节分组加密
	//处理超出8字节整数倍数据的加密运算
	if (remain > 0 && cipherRemains)
		for (p = in_buffer   align_size; p < in_buffer   in_size; p  = 1)
			tea_decode_byte(p, key, --remain, y);
}
// 加密(传入的是16进制的字符串,输出的也是16进制的字符串)
int tea_encrypt_hexstr(char *buffer_in, int nlen, int32 key[], int round, const char* y)
{
	if (buffer_in == NULL || nlen <= 0)
	{
		return -1;
	}

	// 先将16进制字符串转换成16进制字节数组
	string strHexTemp = Math::Tools::str2hex(buffer_in);

	// 加密数据块,加密后的数据存入pData中
	tea_encode_buffer((char*)strHexTemp.data(), strHexTemp.length(), key, 1, round, y);

	// 加密后将16进制字节数组转换成16进制字符串
	strHexTemp = Math::Tools::hex2str(strHexTemp.data(), strHexTemp.length());

	memcpy(buffer_in, strHexTemp.data(), strHexTemp.length());

	return 0;
}

// 解密(传入的是16进制的字符串,输出的也是16进制的字符串)
int tea_decrypt_hexstr(char *buffer_in, int nlen, int32 key[], int round, const char* y)
{
	if (buffer_in == NULL || nlen <= 0)
	{
		return -1;
	}

	// 先将16进制字符串转换成16进制字节数组
	string strHexTemp = Math::Tools::str2hex(buffer_in);

	// 解密数据块,解密后的数据存入pData中
	tea_decode_buffer((char*)strHexTemp.data(), strHexTemp.length(), key, 1, round, y);

	// 解密后将16进制字节数组转换成16进制字符串
	strHexTemp = Math::Tools::hex2str(strHexTemp.data(), strHexTemp.length());

	memcpy(buffer_in, strHexTemp.data(), strHexTemp.length());

	return 0;
}

// 加密(传入的是16进制的字节数组,输出的也是16进制的字节数组)
int tea_encrypt_hexbytes(char *buffer_in, int nlen, int32 key[], int round, const char* y)
{
	if (buffer_in == NULL || nlen <= 0)
	{
		return -1;
	}

	// 解密数据块,解密后的数据存入pData中
	tea_encode_buffer(buffer_in, nlen, key, 1, round, y);

	return 0;
}

// 解密(传入的是16进制的字节数组,输出的也是16进制的字节数组)
int tea_decrypt_hexbytes(char *buffer_in, int nlen, int32 key[], int round, const char* y)
{
	if (buffer_in == NULL || nlen <= 0)
	{
		return -1;
	}

	// 解密数据块,解密后的数据存入pData中
	tea_decode_buffer(buffer_in, nlen, key, 1, round, y);

	return 0;
}

5、相关接口的测试main.cpp

代码语言:javascript复制
#include <iostream>
#include <string>

#include "tea.h"
#include <func.h>

using namespace std;

int main(int argc, char* argv[])
{
	// 测试1: 输入的是16进制的字符串
	string strBuf = "E2DDFA5D00E0FF6880B0924100000000000000002D48190080A2190030000000000040004CFF000000000000000000000000000000000000750200000000";

	int keys[] = { 0x37777931, 0x73444148, 0x4148314F, 0x59484170 };

	// 加密
	tea_encrypt_hexstr((char*)strBuf.c_str(), strBuf.length(), keys, 2, "Wu&Tian");

	cout << "加密后的16进制数据为:" << strBuf << endl;

	// 解密
	tea_decrypt_hexstr((char*)strBuf.c_str(), strBuf.length(), keys, 2, "Wu&Tian");

	cout << "解密后的16进制数据为:" << strBuf << endl;

	cout << "-------------------------------------------------------------------------------------------------" << endl;

	// 测试数据2:输入的是16进制字节数组
	string strBuf2 = "E2DDFA5D00E0FF6880B0924100000000000000002D48190080A2190030000000000040004CFF000000000000000000000000000000000000750200000000";
	// 将16进制字符串转换成16进制字节数组
	string strHexTemp = Math::Tools::str2hex(strBuf2);
	// 对16进制的字节数组进行加密
	tea_encrypt_hexbytes((char*)strHexTemp.c_str(), strHexTemp.length(), keys, 2, "Wu&Tian");
	cout << "加密后的16进制字节数组为:" << strHexTemp << endl;

	tea_decrypt_hexbytes((char*)strHexTemp.c_str(), strHexTemp.length(), keys, 2, "Wu&Tian");

	cout << "解密后的16进制字节数组为:" << strHexTemp << endl;
	
	getchar();

	return 0;
}

二、使用VS2017运行测试结果如下图所示:

可以发现,当对16进制的字节数组E2DDFA5D00E0FF6880B0924100000000000000002D48190080A2190030000000000040004CFF000000000000000000000000000000000000750200000000 以及使用tea加密算法后的16进制字节数组AA12BC6447887E4012532708972000F88772F9859A947FEE16C4FE7C24E5D4F09B40181E6FD42110B93A8479F9261C2BB93A8479F9261C2B5532072C340E 进行打印时是乱码的,需要转换成16进制字符串才能正常显示的。

三、参考资料 1、Tiny Encryption Algorithm-wikipedia 2、TEA加密算法的C/C 实现 3、c 使用Tea算法进行加密解密

0 人点赞