C#中QQ协议TEA加密解密代码
网上找到的qq协议的TEA加密和解密说明如下: 也有很多源代码,但是感觉上代码比较复杂,不容易看。我用C#重写了下。
* QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节
* 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块
* f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是:
* crypt = f(plain ^ preCrypt) ^ prePlain
* 所以,从crypt得到plain的过程自然是
* plain = d(crypt ^ prePlain) ^ preCrypt
* 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数
* 填充的字节数与原始明文长度有关,填充的方法是:
*
* ------- 消息填充算法 -----------
* a = (明文长度 + 10) mod 8
* if(a 不等于 0) a = 8 - a;
* b = 随机数 & 0xF8 | a; 这个的作用是把a的值保存了下来
* plain[0] = b; 然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
* plain[1 至 a+2] = 随机数 & 0xFF; 这里用随机数填充明文的第1到第a+2个字节
* plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文
* plain[a+3+明文长度, 最后] = 0; 在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
* ------- 消息填充算法 ------------
| 1 /**//// <summary> 2 /// 加密解密QQ消息包的工具类. 3 /// </summary> 4 public static class QQCrypter 5 { 6 private static void code(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key) 7 { 8 if (outPos > 0) 9 { 10 for (int i = 0; i < 8; i++) 11 { 12 In[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]); 13 } 14 } 15 uint[] formattedKey = FormatKey(key); 16 uint y = ConvertByteArrayToUInt(In, outOffset + outPos); 17 uint z = ConvertByteArrayToUInt(In, outOffset + outPos + 4); 18 uint sum = 0; 19 uint delta = 0x9e3779b9; 20 uint n = 16; 21 22 while (n-- > 0) 23 { 24 sum += delta; 25 y += ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]); 26 z += ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]); 27 } 28 Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4); 29 Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4); 30 if (inPos > 0) 31 { 32 for (int i = 0; i < 8; i++) 33 { 34 Out[outOffset + outPos + i] = (byte)(Out[outOffset + outPos + i] ^ In[inOffset + inPos + i - 8]); 35 } 36 } 37 } 38 39 private static void decode(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key) 40 { 41 if (outPos > 0) 42 { 43 for (int i = 0; i < 8; i++) 44 { 45 Out[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]); 46 } 47 } 48 else 49 { 50 Array.Copy(In, inOffset, Out, outOffset, 8); 51 } 52 uint[] formattedKey = FormatKey(key); 53 uint y = ConvertByteArrayToUInt(Out, outOffset + outPos); 54 uint z = ConvertByteArrayToUInt(Out, outOffset + outPos + 4); 55 uint sum = 0xE3779B90; 56 uint delta = 0x9e3779b9; 57 uint n = 16; 58 59 while (n-- > 0) 60 { 61 z -= ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]); 62 y -= ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]); 63 sum -= delta; 64 } 65 Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4); 66 Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4); 67 } 68 69 /**//// <summary> 70 /// 解密 71 /// </summary> 72 /// <param name="In">密文</param> 73 /// <param name="offset">密文开始的位置</param> 74 /// <param name="len">密文长度</param> 75 /// <param name="key">密钥</param> 76 /// <returns>返回明文</returns> 77 public static byte[] Decrypt(byte[] In, int offset, int len, byte[] key) 78 { 79 // 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况 80 if ((len % 8 != 0) || (len < 16)) 81 { 82 return null; 83 } 84 byte[] Out = new byte[len]; 85 for (int i = 0; i < len; i += 8) 86 { 87 decode(In, offset, i, Out, 0, i, key); 88 } 89 for (int i = 8; i < len; i++) 90 { 91 Out[i] = (byte)(Out[i] ^ In[offset + i - 8]); 92 } 93 int pos = Out[0] & 0x07; 94 len = len - pos - 10; 95 byte[] res = new byte[len]; 96 Array.Copy(Out, pos + 3, res, 0, len); 97 return res; 98 } 99 100 public static byte[] Encrypt(byte[] In, int offset, int len, byte[] key) 101 { 102 // 计算头部填充字节数 103 int pos = (len + 10) % 8; 104 if (pos != 0) 105 { 106 pos = 8 - pos; 107 } 108 byte[] plain = new byte[len + pos + 10]; 109 plain[0] = (byte)((Rnd.Next() & 0xF8) | pos); 110 for (int i = 1; i < pos + 3; i++) 111 { 112 plain[i] = (byte)(Rnd.Next() & 0xFF); 113 } 114 Array.Copy(In, 0, plain, pos + 3, len); 115 for (int i = pos + 3 + len; i < plain.Length; i++) 116 { 117 plain[i] = 0x0; 118 } 119 // 定义输出流 120 byte[] outer = new byte[len + pos + 10]; 121 for (int i = 0; i < outer.Length; i += 8) 122 { 123 code(plain, 0, i, outer, 0, i, key); 124 } 125 return outer; 126 } 127 128 private static uint[] FormatKey(byte[] key) 129 { 130 if (key.Length == 0) 131 { 132 throw new ArgumentException("Key must be between 1 and 16 characters in length"); 133 } 134 byte[] refineKey = new byte[16]; 135 if (key.Length < 16) 136 { 137 Array.Copy(key, 0, refineKey, 0, key.Length); 138 for (int k = key.Length; k < 16; k++) 139 { 140 refineKey[k] = 0x20; 141 } 142 } 143 else 144 { 145 Array.Copy(key, 0, refineKey, 0, 16); 146 } 147 uint[] formattedKey = new uint[4]; 148 int j = 0; 149 for (int i = 0; i < refineKey.Length; i += 4) 150 { 151 formattedKey[j++] = ConvertByteArrayToUInt(refineKey, i); 152 } 153 return formattedKey; 154 } 155 156 private static byte[] ConvertUIntToByteArray(uint v) 157 { 158 byte[] result = new byte[4]; 159 result[0] = (byte)((v >> 24) & 0xFF); 160 result[1] = (byte)((v >> 16) & 0xFF); 161 result[2] = (byte)((v >> 8) & 0xFF); 162 result[3] = (byte)((v >> 0) & 0xFF); 163 return result; 164 } 165 166 private static uint ConvertByteArrayToUInt(byte[] v, int offset) 167 { 168 if (offset + 4 > v.Length) 169 { 170 return 0; 171 } 172 uint output; 173 output = (uint)(v[offset] << 24); 174 output |= (uint)(v[offset + 1] << 16); 175 output |= (uint)(v[offset + 2] << 8); 176 output |= (uint)(v[offset + 3] << 0); 177 return output; 178 } 179 180 } 181 |
浙公网安备 33010602011771号