public QQDataTransformer(String key) { this(key, "qq"); } public QQDataTransformer(String key, String name) { super(key, name); // TODO Auto-generated constructor stub } @Override public byte[] encode(byte[] data) throws TransformerException { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); byte[] keyBytes = md5.digest(StringTools.getUTF8Bytes(this.key)); swapBytes(keyBytes); reverseBits(keyBytes); byte[] enc = new QQCrypter().encrypt(data, keyBytes); enc = Base64.encode(enc); return enc; } catch (NoSuchAlgorithmException e) { throw new TransformerException(e); } catch (CodecException e) { throw new TransformerException(e); } } @Override public String encode(String data) throws TransformerException { return StringTools.getUTF8String(encode(StringTools.getUTF8Bytes(data))); } @Override public byte[] decode(byte[] data) throws TransformerException { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); byte[] keyBytes = md5.digest(StringTools.getUTF8Bytes(key)); swapBytes(keyBytes); reverseBits(keyBytes); byte[] dec = Base64.decode(data); byte[] rtn = new QQCrypter().decrypt(dec, keyBytes); return rtn; } catch (NoSuchAlgorithmException e) { throw new TransformerException(e); } catch (CodecException e) { throw new TransformerException(e); } } @Override public String decode(String data) throws TransformerException { return StringTools.getUTF8String(decode(StringTools.getUTF8Bytes(data))); } private static void swapBytes(byte[] b) { for (int i = 0; i < b.length; i += 2) { byte tmp = b[i]; b[i] = b[i + 1]; b[i + 1] = tmp; } } private static void reverseBits(byte[] b) { for (int i = 0; i < b.length; i++) b[i] ^= 0xFF; } static class QQCrypter { private byte[] plain; private byte[] prePlain; private byte[] out; private int crypt, preCrypt; private int pos; private int padding; private byte[] key; private boolean header = true; private int contextStart; private static Random random = new Random(); private ByteArrayOutputStream baos; public QQCrypter() { baos = new ByteArrayOutputStream(8); } private static long getUnsignedInt(byte[] in, int offset, int len) { long ret = 0; int end = 0; if (len > 8) end = offset + 8; else end = offset + len; for (int i = offset; i < end; i++) { ret <<= 8; ret |= in[i] & 0xff; } return (ret & 0xffffffffl) | (ret >>> 32); } public byte[] decrypt(byte[] in, int offset, int len, byte[] k) { if(k == null) return null; crypt = preCrypt = 0; this.key = k; byte[] m = new byte[offset + 8]; if((len % 8 != 0) || (len < 16)) return null; prePlain = decipher(in, offset); pos = prePlain[0] & 0x7; int count = len - pos - 10; if(count < 0) return null; for(int i = offset; i < m.length; i++) m[i] = 0; out = new byte[count]; preCrypt = 0; crypt = 8; contextStart = 8; pos++; padding = 1; while(padding <= 2) { if(pos < 8) { pos++; padding++; } if(pos == 8) { m = in; if(!decrypt8Bytes(in, offset, len)) return null; } } int i = 0; while(count != 0) { if(pos < 8) { out[i] = (byte)(m[offset + preCrypt + pos] ^ prePlain[pos]); i++; count--; pos++; } if(pos == 8) { m = in; preCrypt = crypt - 8; if(!decrypt8Bytes(in, offset, len)) return null; } } for(padding = 1; padding < 8; padding++) { if(pos < 8) { if((m[offset + preCrypt + pos] ^ prePlain[pos]) != 0) return null; pos++; } if(pos == 8) { m = in; preCrypt = crypt; if(!decrypt8Bytes(in, offset, len)) return null; } } return out; } public byte[] decrypt(byte[] in, byte[] k) { return decrypt(in, 0, in.length, k); } public byte[] encrypt(byte[] in, int offset, int len, byte[] k) { if(k == null) return in; plain = new byte[8]; prePlain = new byte[8]; pos = 1; padding = 0; crypt = preCrypt = 0; this.key = k; header = true; pos = (len + 0x0A) % 8; if(pos != 0) pos = 8 - pos; out = new byte[len + pos + 10]; plain[0] = (byte)((rand() & 0xF8) | pos); for(int i = 1; i <= pos; i++) plain[i] = (byte)(rand() & 0xFF); pos++; for(int i = 0; i < 8; i++) prePlain[i] = 0x0; padding = 1; while(padding <= 2) { if(pos < 8) { plain[pos++] = (byte)(rand() & 0xFF); padding++; } if(pos == 8) encrypt8Bytes(); } int i = offset; while(len > 0) { if(pos < 8) { plain[pos++] = in[i++]; len--; } if(pos == 8) encrypt8Bytes(); } padding = 1; while(padding <= 7) { if(pos < 8) { plain[pos++] = 0x0; padding++; } if(pos == 8) encrypt8Bytes(); } return out; } public byte[] encrypt(byte[] in, byte[] k) { return encrypt(in, 0, in.length, k); } private byte[] encipher(byte[] in) { int loop = 0x10; long y = getUnsignedInt(in, 0, 4); long z = getUnsignedInt(in, 4, 4); long a = getUnsignedInt(key, 0, 4); long b = getUnsignedInt(key, 4, 4); long c = getUnsignedInt(key, 8, 4); long d = getUnsignedInt(key, 12, 4); long sum = 0; long delta = 0x9E3779B9; delta &= 0xFFFFFFFFL; while (loop-- > 0) { sum += delta; sum &= 0xFFFFFFFFL; y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); y &= 0xFFFFFFFFL; z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); z &= 0xFFFFFFFFL; } baos.reset(); writeInt((int)y); writeInt((int)z); return baos.toByteArray(); } private byte[] decipher(byte[] in, int offset) { int loop = 0x10; long y = getUnsignedInt(in, offset, 4); long z = getUnsignedInt(in, offset + 4, 4); long a = getUnsignedInt(key, 0, 4); long b = getUnsignedInt(key, 4, 4); long c = getUnsignedInt(key, 8, 4); long d = getUnsignedInt(key, 12, 4); long sum = 0xE3779B90; sum &= 0xFFFFFFFFL; long delta = 0x9E3779B9; delta &= 0xFFFFFFFFL; while(loop-- > 0) { z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); z &= 0xFFFFFFFFL; y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); y &= 0xFFFFFFFFL; sum -= delta; sum &= 0xFFFFFFFFL; } baos.reset(); writeInt((int)y); writeInt((int)z); return baos.toByteArray(); } private void writeInt(int t) { baos.write(t >>> 24); baos.write(t >>> 16); baos.write(t >>> 8); baos.write(t); } private byte[] decipher(byte[] in) { return decipher(in, 0); } private void encrypt8Bytes() { for(pos = 0; pos < 8; pos++) { if(header) plain[pos] ^= prePlain[pos]; else plain[pos] ^= out[preCrypt + pos]; } byte[] crypted = encipher(plain); System.arraycopy(crypted, 0, out, crypt, 8); for(pos = 0; pos < 8; pos++) out[crypt + pos] ^= prePlain[pos]; System.arraycopy(plain, 0, prePlain, 0, 8); preCrypt = crypt; crypt += 8; pos = 0; header = false; } private boolean decrypt8Bytes(byte[] in , int offset, int len) { for(pos = 0; pos < 8; pos++) { if(contextStart + pos >= len) return true; prePlain[pos] ^= in[offset + crypt + pos]; } prePlain = decipher(prePlain); if(prePlain == null) return false; contextStart += 8; crypt += 8; pos = 0; return true; } private int rand() { return random.nextInt(); } }