先介紹一下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