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


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


    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版,两个彻底兼容。
app

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版: ui

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是公开的方法,使用起来很是方便。 this

相关文章
相关标签/搜索