先介绍一下XXTEA算法的相关知识: java
若是加密字符串长度不是 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