`
810364804
  • 浏览: 782811 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

先介绍一下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是公开的方法,使用起来非常方便。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics