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

相關文章
相關標籤/搜索