文档章节

XXTEA加密算法 -- 高效加密解密 JAVA C# 兼容版

wyouflf
 wyouflf
发布于 2013/07/30 09:50
字数 1884
阅读 1K
收藏 5

行业解决方案、产品招募中!想赚钱就来传!>>>


    先介绍一下XXTEA算法的相关知识:


    TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,支持128位密码,与BlowFish一样TEA每次只能加密/解密8字节数据。TEA特点是速度快、效率高,实现也非常简单。由于针对TEA的攻击不断出现,所以TEA也发展出几个版本,分别是XTEA、Block TEA和XXTEA。


    TEA加密和解密时都使用一个常量值,这个常量值为0x9e3779b,这个值是近似黄金分割率,注意,有些编程人员为了避免在程序中直接出现"mov 变量,0x9e3779b",以免被破解者直接搜索0x9e3779b这个常数得知使用TEA算法,所以有时会使用"sub 变量,0x61C88647"代替"mov 变量,0x9e3779b",0x61C88647=-(0x9e3779b)。


    TEA算法每一次可以操作64bit(8byte),采用128bit(16byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮。 


    标准的16轮运算TEA,如果要改成标准的32轮运算TEA,只需修改code和decode中的n为32,并将decode中的delta左移4位改成左移5位即可。


    XTEA 跟 TEA 使用了相同的简单运算,但它采用了截然不同的顺序,为了阻止密钥表攻击,四个子密钥(在加密过程中,原 128 位的密钥被拆分为 4 个 32 位的子密钥)采用了一种不太正规的方式进行混合,但速度更慢了。在跟描述 XTEA 算法的同一份报告中,还介绍了另外一种被称为 Block TEA 算法的变种,它可以对 32 位大小任意倍数的变量块进行操作。该算法将 XTEA 轮循函数依次应用于块中的每个字,并且将它附加于它的邻字。该操作重复多少轮依赖于块的大小,但至少需要 6 轮。该方法的优势在于它无需操作模式(CBC,OFB,CFB 等),密钥可直接用于信息。


    对于长的信息它可能比 XTEA 更有效率。在 1998 年,Markku-Juhani Saarinen 给出了一个可有效攻击 Block TEA 算法的代码,但之后很快 David J. Wheeler 和 Roger M. Needham 就给出了 Block TEA 算法的修订版,这个算法被称为 XXTEA。


    XXTEA 使用跟 Block TEA 相似的结构,但在处理块中每个字时利用了相邻字。它利用一个更复杂的 MX 函数代替了 XTEA 轮循函数,MX 使用 2 个输入量。


    如果加密字符串长度不是 4 的整数倍,则这些实现的在加密后无法真正还原,还原以后的字符串实际上与原字符串不相等,而是后面多了一些 \0 的字符,或者少了一些 \0 的字符。原因在于 XXTEA 算法只定义了如何对 32 位的信息块数组(实际上是 32 位无符号整数数组)进行加密,而并没有定义如何来将字符串编码为这种数组。而现有的实现中在将字符串编码为整数数组时,都丢失了字符串长度信息,因此还原出现了问题。

原理图:



    下面是我的实现,使用这个实现你不用担心上面提到的无法还原的情况:

    有两个版本C#版和JAVA版,两个完全兼容。

C#版:


using System;
using System.Collections.Generic;
using System.Text;

namespace Lidroid.Utilities
{
    public static class XXTEA
    {
        public static string Encrypt(this string data, string key)
        {
            return TEAEncrypt(
                Encoding.UTF8.GetBytes(data.PadRight(MIN_LENGTH, SPECIAL_CHAR)).ToLongArray(),
                Encoding.UTF8.GetBytes(key.PadRight(MIN_LENGTH, SPECIAL_CHAR)).ToLongArray()).ToHexString();
        }

        public static string Decrypt(this string data, string key)
        {
            if (string.IsNullOrWhiteSpace(data)) { return data; }
            byte[] code = TEADecrypt(
                data.ToLongArray(),
                Encoding.UTF8.GetBytes(key.PadRight(MIN_LENGTH, SPECIAL_CHAR)).ToLongArray()).ToByteArray();
            return Encoding.UTF8.GetString(code, 0, code.Length);
        }


        private static long[] TEAEncrypt(long[] data, long[] key)
        {
            int n = data.Length;
            if (n < 1) { return data; }

            long z = data[data.Length - 1], y = data[0], sum = 0, e, p, q;
            q = 6 + 52 / n;
            while (q-- > 0)
            {
                sum += DELTA;
                e = (sum >> 2) & 3;
                for (p = 0; p < n - 1; p++)
                {
                    y = data[p + 1];
                    z = data[p] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z);
                }
                y = data[0];
                z = data[n - 1] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z);
            }

            return data;
        }

        private static long[] TEADecrypt(long[] data, long[] key)
        {
            int n = data.Length;
            if (n < 1) { return data; }

            long z = data[data.Length - 1], y = data[0], sum = 0, e, p, q;
            q = 6 + 52 / n;
            sum = q * DELTA;
            while (sum != 0)
            {
                e = (sum >> 2) & 3;
                for (p = n - 1; p > 0; p--)
                {
                    z = data[p - 1];
                    y = data[p] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z);
                }
                z = data[n - 1];
                y = data[0] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z);
                sum -= DELTA;
            }

            return data;
        }


        private static long[] ToLongArray(this byte[] data)
        {
            int n = (data.Length % 8 == 0 ? 0 : 1) + data.Length / 8;
            long[] result = new long[n];

            for (int i = 0; i < n - 1; i++)
            {
                result[i] = BitConverter.ToInt64(data, i * 8);
            }

            byte[] buffer = new byte[8];
            Array.Copy(data, (n - 1) * 8, buffer, 0, data.Length - (n - 1) * 8);
            result[n - 1] = BitConverter.ToInt64(buffer, 0);

            return result;
        }
        private static byte[] ToByteArray(this long[] data)
        {
            List<byte> result = new List<byte>(data.Length * 8);

            for (int i = 0; i < data.Length; i++)
            {
                result.AddRange(BitConverter.GetBytes(data[i]));
            }

            while (result[result.Count - 1] == SPECIAL_CHAR)
            {
                result.RemoveAt(result.Count - 1);
            }

            return result.ToArray();
        }

        private static string ToHexString(this long[] data)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("x2").PadLeft(16, '0'));
            }
            return sb.ToString();
        }
        private static long[] ToLongArray(this string data)
        {
            int len = data.Length / 16;
            long[] result = new long[len];
            for (int i = 0; i < len; i++)
            {
                result[i] = Convert.ToInt64(data.Substring(i * 16, 16), 16);
            }
            return result;
        }

        private const long DELTA = 0x9E3779B9;
        private const int MIN_LENGTH = 32;
        private const char SPECIAL_CHAR = '\0';
    }
}


JAVA版:

package Lidroid.Utilities;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/*
 * XXTEA 加密算法
 */
public class XXTEA {
	public static String Encrypt(String data, String key) {
		return ToHexString(TEAEncrypt(
				ToLongArray(PadRight(data, MIN_LENGTH).getBytes(
						Charset.forName("UTF8"))),
				ToLongArray(PadRight(key, MIN_LENGTH).getBytes(
						Charset.forName("UTF8")))));
	}

	public static String Decrypt(String data, String key) {
		if (data == null || data.length() < MIN_LENGTH) {
			return data;
		}
		byte[] code = ToByteArray(TEADecrypt(
				ToLongArray(data),
				ToLongArray(PadRight(key, MIN_LENGTH).getBytes(
						Charset.forName("UTF8")))));
		return new String(code, Charset.forName("UTF8"));
	}

	private static long[] TEAEncrypt(long[] data, long[] key) {
		int n = data.length;
		if (n < 1) {
			return data;
		}

		long z = data[data.length - 1], y = data[0], sum = 0, e, p, q;
		q = 6 + 52 / n;
		while (q-- > 0) {
			sum += DELTA;
			e = (sum >> 2) & 3;
			for (p = 0; p < n - 1; p++) {
				y = data[(int) (p + 1)];
				z = data[(int) p] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)
						^ (sum ^ y) + (key[(int) (p & 3 ^ e)] ^ z);
			}
			y = data[0];
			z = data[n - 1] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)
					^ (sum ^ y) + (key[(int) (p & 3 ^ e)] ^ z);
		}

		return data;
	}

	private static long[] TEADecrypt(long[] data, long[] key) {
		int n = data.length;
		if (n < 1) {
			return data;
		}

		long z = data[data.length - 1], y = data[0], sum = 0, e, p, q;
		q = 6 + 52 / n;
		sum = q * DELTA;
		while (sum != 0) {
			e = (sum >> 2) & 3;
			for (p = n - 1; p > 0; p--) {
				z = data[(int) (p - 1)];
				y = data[(int) p] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)
						^ (sum ^ y) + (key[(int) (p & 3 ^ e)] ^ z);
			}
			z = data[n - 1];
			y = data[0] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y)
					+ (key[(int) (p & 3 ^ e)] ^ z);
			sum -= DELTA;
		}

		return data;
	}

	private static long[] ToLongArray(byte[] data) {
		int n = (data.length % 8 == 0 ? 0 : 1) + data.length / 8;
		long[] result = new long[n];

		for (int i = 0; i < n - 1; i++) {
			result[i] = bytes2long(data, i * 8);
		}

		byte[] buffer = new byte[8];
		for (int i = 0, j = (n - 1) * 8; j < data.length; i++, j++) {
			buffer[i] = data[j];
		}
		result[n - 1] = bytes2long(buffer, 0);

		return result;
	}

	private static byte[] ToByteArray(long[] data) {
		List<Byte> result = new ArrayList<Byte>();

		for (int i = 0; i < data.length; i++) {
			byte[] bs = long2bytes(data[i]);
			for (int j = 0; j < 8; j++) {
				result.add(bs[j]);
			}
		}

		while (result.get(result.size() - 1) == SPECIAL_CHAR) {
			result.remove(result.size() - 1);
		}

		byte[] ret = new byte[result.size()];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = result.get(i);
		}
		return ret;
	}

	public static byte[] long2bytes(long num) {
		ByteBuffer buffer = ByteBuffer.allocate(8).order(
				ByteOrder.LITTLE_ENDIAN);
		buffer.putLong(num);
		return buffer.array();
	}

	public static long bytes2long(byte[] b, int index) {
		ByteBuffer buffer = ByteBuffer.allocate(8).order(
				ByteOrder.LITTLE_ENDIAN);
		buffer.put(b, index, 8);
		return buffer.getLong(0);
	}

	private static String ToHexString(long[] data) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < data.length; i++) {
			sb.append(PadLeft(Long.toHexString(data[i]), 16));
		}
		return sb.toString();
	}

	private static long[] ToLongArray(String data) {
		int len = data.length() / 16;
		long[] result = new long[len];
		for (int i = 0; i < len; i++) {
			result[i] = new BigInteger(data.substring(i * 16, i * 16 + 16), 16)
					.longValue();
		}
		return result;
	}

	private static String PadRight(String source, int length) {
		while (source.length() < length) {
			source += SPECIAL_CHAR;
		}
		return source;
	}

	private static String PadLeft(String source, int length) {
		while (source.length() < length) {
			source = '0' + source;
		}
		return source;
	}

	private static long DELTA = 2654435769L;
	private static int MIN_LENGTH = 32;
	private static char SPECIAL_CHAR = '\0';
}

两个版本XXTEA算法都只有Encrypt和Decrypt是公开的方法,使用起来非常方便。

wyouflf

wyouflf

粉丝 410
博文 12
码字总数 4665
作品 2
西安
私信 提问
加载中
此博客有 1 条评论,请先登录后再查看。
访问安全控制解决方案

本文是《轻量级 Java Web 框架架构设计》的系列博文。 今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到...

黄勇
2013/11/03
3.4K
6
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
5.9K
18
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
树莓派(Raspberry Pi):完美的家用服务器

自从树莓派发布后,所有在互联网上的网站为此激动人心的设备提供了很多有趣和具有挑战性的使用方法。虽然这些想法都很棒,但树莓派( RPi )最明显却又是最不吸引人的用处是:创建你的完美家用...

异次元
2013/11/09
5.8K
8
极速博客引擎--Gor

gor 是使用 golang 实现的类Ruhoh静态博客引擎(Ruhoh like),基本兼容ruhoh 1.x规范. 相当于与ruhoh的官方实现(ruby实现), 有以下优点: 速度完胜 -- 编译wendal.net近200篇博客,仅需要1秒 安装...

wendal
2013/01/20
3.8K
0

没有更多内容

加载失败,请刷新页面

加载更多

代理服务器和反向代理服务器之间有什么区别? - What's the difference between proxy server and reverse proxy server?

问题: 代理服务器和反向代理服务器有什么区别? 解决方案: 参考一: https://stackoom.com/question/wRc/代理服务器和反向代理服务器之间有什么区别 参考二: https://oldbug.net/q/wRc/W...

技术盛宴
50分钟前
16
0
第八讲:配置外界可以访问虚拟机里面的HDFS

本节通过配置实现外界访问虚拟机Centos6.4里面的HDFS。为后续的java读写HDFS做准备 步骤有: 1、修改主机Windos7的网络配置 2、修改虚拟机Centos6.4里面的网络配置 3、修改虚拟机Centos6.4里...

刘日辉
今天
26
0
OSChina 周四乱弹 —— 不劳而获的饭好吃么?好吃!非常好吃!

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 小小编辑推荐:《世界上不存在的歌 (2020重唱版)》- 陈奕迅 《世界上不存在的歌 (2020重唱版)》- 陈奕迅 手机党少年们想听歌,请使劲儿戳(这...

小小编辑
今天
31
2
从 GPU、TPU,到 Web 端、移动端,深度学习框架部署训练开始变简单

本文作者:o****0 早些时候的统计显示,今年3月,深度学习框架集中爆发。5月,有人发布可以直接在 iphone11上训练神经网络的开源项目。日前,百度开源国内首个可直接运行在 Web 端的深度学习...

百度开发者中心
昨天
16
0
如何从Git存储库中删除文件? - How can I delete a file from a Git repository?

问题: I have added a file named "file1.txt" to a Git repository. 我已将名为"file1.txt"的文件添加到Git存储库中。 After that, I committed it, added a couple of directories called......

富含淀粉
今天
31
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部