TEA, QQ消息

差點吐血! 這破玩意搞了N天, 終於搞定了, 但是很遺憾的是, 還是不明就裏, 暫時先把工作進行下去吧...

因爲需要跟Java版的通信, Java加密, C#的再解密, 這樣就帶來很多問題, 不過最終還是OK了.

代碼如下(那個迭代過程實在是讓我頭疼啊, 看來算法還得加強!):

 

代碼
  1     /// <summary>
  2     /// 加密解密QQ消息包的工具類.
  3     /// </summary>
  4     public static class QQCrypter
  5     {
  6         private static Random Rnd = new Random();
  7         private static void code(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
  8         {
  9             if (outPos > 0)
 10             {
 11                 for (int i = 0; i < 8; i++)
 12                 {
 13                     In[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
 14                 }
 15             }
 16             uint[] formattedKey = FormatKey(key);
 17             uint y = ConvertByteArrayToUInt(In, outOffset + outPos);
 18             uint z = ConvertByteArrayToUInt(In, outOffset + outPos + 4);
 19             uint sum = 0;
 20             uint delta = 0x9e3779b9;
 21             uint n = 16;
 22 
 23             while (n-- > 0)
 24             {
 25                 sum += delta;
 26                 y += ((z << 4+ formattedKey[0]) ^ (z + sum) ^ ((z >> 5+ formattedKey[1]);
 27                 z += ((y << 4+ formattedKey[2]) ^ (y + sum) ^ ((y >> 5+ formattedKey[3]);
 28             }
 29             Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
 30             Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 44);
 31             if (inPos > 0)
 32             {
 33                 for (int i = 0; i < 8; i++)
 34                 {
 35                     Out[outOffset + outPos + i] = (byte)(Out[outOffset + outPos + i] ^ In[inOffset + inPos + i - 8]);
 36                 }
 37             }
 38         }
 39 
 40         private static void decode(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
 41         {
 42             if (outPos > 0)
 43             {
 44                 for (int i = 0; i < 8; i++)
 45                 {
 46                     Out[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
 47                 }
 48             }
 49             else
 50             {
 51                 Array.Copy(In, inOffset, Out, outOffset, 8);
 52             }
 53             uint[] formattedKey = FormatKey(key);
 54             uint y = ConvertByteArrayToUInt(Out, outOffset + outPos);
 55             uint z = ConvertByteArrayToUInt(Out, outOffset + outPos + 4);
 56             uint sum = 0xE3779B90;
 57             uint delta = 0x9e3779b9;
 58             uint n = 16;
 59 
 60             while (n-- > 0)
 61             {
 62                 z -= ((y << 4+ formattedKey[2]) ^ (y + sum) ^ ((y >> 5+ formattedKey[3]);
 63                 y -= ((z << 4+ formattedKey[0]) ^ (z + sum) ^ ((z >> 5+ formattedKey[1]);
 64                 sum -= delta;
 65             }
 66             Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
 67             Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 44);
 68         }
 69 
 70         /// <summary>
 71         /// 解密
 72          /// </summary>
 73         /// <param name="In">密文</param>
 74         /// <param name="offset">密文開始的位置</param>
 75         /// <param name="len">密文長度</param>
 76         /// <param name="key">密鑰</param>
 77         /// <returns>返回明文</returns>
 78         public static byte[] Decrypt(byte[] In, int offset, int len, byte[] key)
 79         {
 80             // 因爲QQ消息加密之後至少是16字節,並且肯定是8的倍數,這裏檢查這種情況
 81             if ((len % 8 != 0|| (len < 16))
 82             {
 83                 return null;
 84             }
 85             byte[] Out = new byte[len];
 86             for (int i = 0; i < len; i += 8)
 87             {
 88                 decode(In, offset, i, Out, 0, i, key);
 89             }
 90             for (int i = 8; i < len; i++)
 91             {
 92                 Out[i] = (byte)(Out[i] ^ In[offset + i - 8]);
 93             }
 94             int pos = Out[0& 0x07;
 95             len = len - pos - 10;
 96             byte[] res = new byte[len];
 97             Array.Copy(Out, pos + 3, res, 0, len);
 98             return res;
 99         }
100 
101         /// <summary>
102         /// 加密
103          /// </summary>
104         /// <param name="In">明文</param>
105         /// <param name="offset">明文開始的位置</param>
106         /// <param name="len">明文長度</param>
107         /// <param name="key">密鑰</param>
108         /// <returns>返回密文</returns>
109         public static byte[] Encrypt(byte[] In, int offset, int len, byte[] key)
110         {
111             // 計算頭部填充字節數
112             int pos = (len + 10% 8;
113             if (pos != 0)
114             {
115                 pos = 8 - pos;
116             }
117             byte[] plain = new byte[len + pos + 10];
118             plain[0= (byte)((Rnd.Next() & 0xF8| pos);
119             for (int i = 1; i < pos + 3; i++)
120             {
121                 plain[i] = (byte)(Rnd.Next() & 0xFF);
122             }
123             Array.Copy(In, 0, plain, pos + 3, len);
124             for (int i = pos + 3 + len; i < plain.Length; i++)
125             {
126                 plain[i] = 0x0;
127             }
128             // 定義輸出流
129               byte[] outer = new byte[len + pos + 10];
130             for (int i = 0; i < outer.Length; i += 8)
131             {
132                 code(plain, 0, i, outer, 0, i, key);
133             }
134             return outer;
135         }
136 
137         private static uint[] FormatKey(byte[] key)
138         {
139             if (key.Length == 0)
140             {
141                 throw new ArgumentException("Key must be between 1 and 16 characters in length");
142             }
143             byte[] refineKey = new byte[16];
144             if (key.Length < 16)
145             {
146                 Array.Copy(key, 0, refineKey, 0, key.Length);
147                 for (int k = key.Length; k < 16; k++)
148                 {
149                     refineKey[k] = 0x20;
150                 }
151             }
152             else
153             {
154                 Array.Copy(key, 0, refineKey, 016);
155             }
156             uint[] formattedKey = new uint[4];
157             int j = 0;
158             for (int i = 0; i < refineKey.Length; i += 4)
159             {
160                 formattedKey[j++= ConvertByteArrayToUInt(refineKey, i);
161             }
162             return formattedKey;
163         }
164 
165         private static byte[] ConvertUIntToByteArray(uint v)
166         {
167             byte[] result = new byte[4];
168             result[0= (byte)((v >> 24& 0xFF);
169             result[1= (byte)((v >> 16& 0xFF);
170             result[2= (byte)((v >> 8& 0xFF);
171             result[3= (byte)((v >> 0& 0xFF);
172             return result;
173         }
174 
175         private static uint ConvertByteArrayToUInt(byte[] v, int offset)
176         {
177             if (offset + 4 > v.Length)
178             {
179                 return 0;
180             }
181             uint output;
182             output = (uint)(v[offset] << 24);
183             output |= (uint)(v[offset + 1<< 16);
184             output |= (uint)(v[offset + 2<< 8);
185             output |= (uint)(v[offset + 3<< 0);
186             return output;
187         }
188     }

 

對外只留了兩個方法用於加密和解密, 有興趣的童鞋研究研究, 結果告訴我哦.

我找個兩個版本的, 上面那個是最終使用通過的, 還有一個版本的感覺明顯不對, 因爲加密的時候貌似沒有產生隨機數的樣子, 每次加密過後的密文都相同, 這我很想不通, 所以更放棄了對那段代碼的研究. 後來自己嘗試改寫Java版的, 無功而返.

下面附Java版的代碼, 帶部分註釋(網上找的).

 

代碼
  1 package mobi.bbase.ahome.utils;
  2 
  3 import java.io.ByteArrayOutputStream;
  4 import java.util.Random;
  5 
  6 
  7 /**
  8  * 加密解密QQ消息的工具類. QQ消息的加密算法是一個16次的迭代過程,並且是反饋的,每一個加密單元是8字節,輸出也是8字節,密鑰是16字節
  9  * 我們以prePlain表示前一個明文塊,plain表示當前明文塊,crypt表示當前明文塊加密得到的密文塊,preCrypt表示前一個密文塊
 10  * f表示加密算法,d表示解密算法 那麼從plain得到crypt的過程是: crypt = f(plain &circ; preCrypt) &circ;
 11  * prePlain 所以,從crypt得到plain的過程自然是 plain = d(crypt &circ; prePlain) &circ;
 12  * preCrypt 此外,算法有它的填充機制,其會在明文前和明文後分別填充一定的字節數,以保證明文長度是8字節的倍數
 13  * 填充的字節數與原始明文長度有關,填充的方法是:
 14  * 
 15  * <pre>
 16  * <code>
 17  * 
 18  *      ------- 消息填充算法 ----------- 
 19  *      a = (明文長度 + 10) mod 8
 20  *      if(a 不等於 0) a = 8 - a;
 21  *      b = 隨機數 &amp; 0xF8 | a;              這個的作用是把a的值保存了下來
 22  *      plain[0] = b;                   然後把b做爲明文的第0個字節,這樣第0個字節就保存了a的信息,這個信息在解密時就要用來找到真正明文的起始位置
 23  *      plain[1 至 a+2] = 隨機數 &amp; 0xFF;    這裏用隨機數填充明文的第1到第a+2個字節
 24  *      plain[a+3 至 a+3+明文長度-1] = 明文; 從a+3字節開始纔是真正的明文
 25  *      plain[a+3+明文長度, 最後] = 0;       在最後,填充0,填充到總長度爲8的整數爲止。到此爲止,結束了,這就是最後得到的要加密的明文內容
 26  *      ------- 消息填充算法 ------------
 27  *   
 28  * </code>
 29  * </pre>
 30  * 
 31  * @author luma
 32  * @author notXX
 33  */
 34 public class Crypter {
 35     // 指向當前的明文塊
 36     private byte[] plain;
 37     // 這指向前面一個明文塊
 38     private byte[] prePlain;
 39     // 輸出的密文或者明文
 40     private byte[] out;
 41     // 當前加密的密文位置和上一次加密的密文塊位置,他們相差8
 42     private int crypt, preCrypt;
 43     // 當前處理的加密解密塊的位置
 44     private int pos;     
 45     // 填充數
 46     private int padding;
 47     // 密鑰
 48     private byte[] key;
 49     // 用於加密時,表示當前是否是第一個8字節塊,因爲加密算法是反饋的
 50     // 但是最開始的8個字節沒有反饋可用,所有需要標明這種情況
 51     private boolean header = true;
 52     // 這個表示當前解密開始的位置,之所以要這麼一個變量是爲了避免當解密到最後時
 53     // 後面已經沒有數據,這時候就會出錯,這個變量就是用來判斷這種情況免得出錯
 54     private int contextStart;
 55     // 隨機數對象
 56     private static Random random = new Random();
 57     // 字節輸出流
 58 private ByteArrayOutputStream baos;
 59 /**
 60 * 構造函數
 61 */
 62 public Crypter() {
 63 baos = new ByteArrayOutputStream(8);
 64 }
 65 /**
 66 * 把字節數組從offset開始的len個字節轉換成一個unsigned int, 因爲java裏面沒有unsigned,所以unsigned
 67 * int使用long表示的, 如果len大於8,則認爲len等於8。如果len小於8,則高位填0 <br>
 68 * (edited by notxx) 改變了算法, 性能稍微好一點. 在我的機器上測試10000次, 原始算法花費18s, 這個算法花費12s.
 69 
 70 @param in
 71 *                   字節數組.
 72 @param offset
 73 *                   從哪裏開始轉換.
 74 @param len
 75 *                   轉換長度, 如果len超過8則忽略後面的
 76 @return
 77 */
 78 private static long getUnsignedInt(byte[] in, int offset, int len) {
 79 long ret = 0;
 80 int end = 0;
 81 if (len > 8)
 82 end = offset + 8;
 83 else
 84 end = offset + len;
 85 for (int i = offset; i < end; i++) {
 86 ret <<= 8;
 87 ret |= in[i] & 0xff;
 88 }
 89 return (ret & 0xffffffffl| (ret >>> 32);
 90 }
 91     
 92     /**
 93      * 解密
 94      * @param in 密文
 95      * @param offset 密文開始的位置
 96      * @param len 密文長度
 97      * @param k 密鑰
 98      * @return 明文
 99      */
100     public byte[] decrypt(byte[] in, int offset, int len, byte[] k) {
101     // 檢查密鑰
102     if(k == null)
103     return null;
104     
105         crypt = preCrypt = 0;
106         this.key = k;
107         int count;
108         byte[] m = new byte[offset + 8];
109         
110         // 因爲QQ消息加密之後至少是16字節,並且肯定是8的倍數,這裏檢查這種情況
111         if((len % 8 != 0|| (len < 16)) return null;
112         // 得到消息的頭部,關鍵是得到真正明文開始的位置,這個信息存在第一個字節裏面,所以其用解密得到的第一個字節與7做與
113         prePlain = decipher(in, offset);
114         pos = prePlain[0& 0x7;
115         // 得到真正明文的長度
116         count = len - pos - 10;
117         // 如果明文長度小於0,那肯定是出錯了,比如傳輸錯誤之類的,返回
118         if(count < 0return null;
119         
120         // 這個是臨時的preCrypt,和加密時第一個8字節塊沒有prePlain一樣,解密時
121         // 第一個8字節塊也沒有preCrypt,所有這裏建一個全0的
122         for(int i = offset; i < m.length; i++)
123             m[i] = 0;
124         // 通過了上面的代碼,密文應該是沒有問題了,我們分配輸出緩衝區
125         out = new byte[count];
126         // 設置preCrypt的位置等於0,注意目前的preCrypt位置是指向m的,因爲java沒有指針,所以我們在後面要控制當前密文buf的引用
127         preCrypt = 0;
128         // 當前的密文位置,爲什麼是8不是0呢?注意前面我們已經解密了頭部信息了,現在當然該8了
129         crypt = 8;
130         // 自然這個也是8
131         contextStart = 8;
132         // 加1,和加密算法是對應的
133         pos++;
134         
135         // 開始跳過頭部,如果在這個過程中滿了8字節,則解密下一塊
136         // 因爲是解密下一塊,所以我們有一個語句 m = in,下一塊當然有preCrypt了,我們不再用m了
137         // 但是如果不滿8,這說明了什麼?說明了頭8個字節的密文是包含了明文信息的,當然還是要用m把明文弄出來
138         // 所以,很顯然,滿了8的話,說明了頭8個字節的密文除了一個長度信息有用之外,其他都是無用的填充
139         padding = 1;
140         while(padding <= 2) {
141             if(pos < 8) {
142                 pos++;
143                 padding++;
144             }
145             if(pos == 8) {
146                 m = in;
147                 if(!decrypt8Bytes(in, offset, len)) return null;
148             }
149         }
150         
151         // 這裏是解密的重要階段,這個時候頭部的填充都已經跳過了,開始解密
152         // 注意如果上面一個while沒有滿8,這裏第一個if裏面用的就是原始的m,否則這個m就是in了
153         int i = 0;
154         while(count != 0) {
155             if(pos < 8) {
156                 out[i] = (byte)(m[offset + preCrypt + pos] ^ prePlain[pos]);
157                 i++;
158                 count--;
159                 pos++;
160             }
161             if(pos == 8) {
162                 m = in;
163                 preCrypt = crypt - 8;
164                 if(!decrypt8Bytes(in, offset, len)) 
165                     return null;
166             }
167         }
168         
169         // 最後的解密部分,上面一個while已經把明文都解出來了,就剩下尾部的填充了,應該全是0
170         // 所以這裏有檢查是否解密了之後是不是0,如果不是的話那肯定出錯了,返回null
171         for(padding = 1; padding < 8; padding++) {
172             if(pos < 8) {
173                 if((m[offset + preCrypt + pos] ^ prePlain[pos]) != 0)
174                     return null;
175                 pos++;
176             }
177             if(pos == 8) {
178                 m = in;
179                 preCrypt = crypt;
180                 if(!decrypt8Bytes(in, offset, len)) 
181                     return null;
182             }
183         }
184         return out;
185     }
186     
187     /**
188      * @param in
189      *            需要被解密的密文
190      * @param inLen
191      *            密文長度
192      * @param k
193      *            密鑰
194      * @return Message 已解密的消息
195      */
196     public byte[] decrypt(byte[] in, byte[] k) {   
197         return decrypt(in, 0, in.length, k);
198     }
199     
200     /**
201      * 加密
202      * @param in 明文字節數組
203      * @param offset 開始加密的偏移
204      * @param len 加密長度
205      * @param k 密鑰
206      * @return 密文字節數組
207      */
208     public byte[] encrypt(byte[] in, int offset, int len, byte[] k) {
209     // 檢查密鑰
210     if(k == null)
211     return in;
212     
213         plain = new byte[8];
214         prePlain = new byte[8];
215         pos = 1;           
216         padding = 0
217         crypt = preCrypt = 0;
218         this.key = k;
219         header = true;
220         
221         // 計算頭部填充字節數
222         pos = (len + 0x0A% 8;
223         if(pos != 0)
224             pos = 8 - pos;
225         // 計算輸出的密文長度
226         out = new byte[len + pos + 10];
227         // 這裏的操作把pos存到了plain的第一個字節裏面
228         // 0xF8後面三位是空的,正好留給pos,因爲pos是0到7的值,表示文本開始的字節位置
229         plain[0= (byte)((rand() & 0xF8| pos);
230         
231         // 這裏用隨機產生的數填充plain[1]到plain[pos]之間的內容
232         for(int i = 1; i <= pos; i++)
233             plain[i] = (byte)(rand() & 0xFF);
234         pos++;
235         // 這個就是prePlain,第一個8字節塊當然沒有prePlain,所以我們做一個全0的給第一個8字節塊
236         for(int i = 0; i < 8; i++)
237             prePlain[i] = 0x0;
238         
239         // 繼續填充2個字節的隨機數,這個過程中如果滿了8字節就加密之
240         padding = 1;
241         while(padding <= 2) {
242             if(pos < 8) {
243                 plain[pos++= (byte)(rand() & 0xFF);
244                 padding++;
245             }
246             if(pos == 8)
247                 encrypt8Bytes();
248         }
249         
250         // 頭部填充完了,這裏開始填真正的明文了,也是滿了8字節就加密,一直到明文讀完
251         int i = offset;
252         while(len > 0) {
253             if(pos < 8) {
254                 plain[pos++= in[i++];
255                 len--;
256             }
257             if(pos == 8)
258                 encrypt8Bytes();
259         }
260         
261         // 最後填上0,以保證是8字節的倍數
262         padding = 1;
263         while(padding <= 7) {
264             if(pos < 8) {
265                 plain[pos++= 0x0;
266                 padding++;
267             }
268             if(pos == 8)
269                 encrypt8Bytes();
270         }
271         
272         return out;
273     }
274 
275     /**
276      * @param in
277      *            需要加密的明文
278      * @param inLen
279      *            明文長度
280      * @param k
281      *            密鑰
282      * @return Message 密文
283      */
284     public byte[] encrypt(byte[] in, byte[] k) {
285         return encrypt(in, 0, in.length, k);
286     }
287 
288     /**
289      * 加密一個8字節塊
290      * 
291      * @param in
292      * 明文字節數組
293      * @return
294      * 密文字節數組
295      */
296     private byte[] encipher(byte[] in) {
297         // 迭代次數,16次
298         int loop = 0x10;
299         // 得到明文和密鑰的各個部分,注意java沒有無符號類型,所以爲了表示一個無符號的整數
300         // 我們用了long,這個long的前32位是全0的,我們通過這種方式模擬無符號整數,後面用到的long也都是一樣的
301         // 而且爲了保證前32位爲0,需要和0xFFFFFFFF做一下位與            
302         long y = getUnsignedInt(in, 04);
303         long z = getUnsignedInt(in, 44);
304         long a = getUnsignedInt(key, 04);
305         long b = getUnsignedInt(key, 44);
306         long c = getUnsignedInt(key, 84);
307         long d = getUnsignedInt(key, 124);
308         // 這是算法的一些控制變量,爲什麼delta是0x9E3779B9呢?
309         // 這個數是TEA算法的delta,實際是就是(sqr(5) - 1) * 2^31 (根號5,減1,再乘2的31次方)
310         long sum = 0;
311         long delta = 0x9E3779B9;
312         delta &= 0xFFFFFFFFL;
313 
314         // 開始迭代了,亂七八糟的,我也看不懂,反正和DES之類的差不多,都是這樣倒來倒去
315         while (loop-- > 0) {
316             sum += delta;
317             sum &= 0xFFFFFFFFL;
318             y += ((z << 4+ a) ^ (z + sum) ^ ((z >>> 5+ b);
319             y &= 0xFFFFFFFFL;
320             z += ((y << 4+ c) ^ (y + sum) ^ ((y >>> 5+ d);
321             z &= 0xFFFFFFFFL;
322         }
323 
324         // 最後,我們輸出密文,因爲我用的long,所以需要強制轉換一下變成int
325         baos.reset();
326         writeInt((int)y);
327         writeInt((int)z);
328         return baos.toByteArray();
329     }
330     
331     /**
332      * 解密從offset開始的8字節密文
333      * 
334      * @param in
335      * 密文字節數組
336      * @param offset
337      * 密文開始位置
338      * @return
339      * 明文
340      */
341     private byte[] decipher(byte[] in, int offset) {
342         // 迭代次數,16次
343         int loop = 0x10;
344         // 得到密文和密鑰的各個部分,注意java沒有無符號類型,所以爲了表示一個無符號的整數
345         // 我們用了long,這個long的前32位是全0的,我們通過這種方式模擬無符號整數,後面用到的long也都是一樣的
346         // 而且爲了保證前32位爲0,需要和0xFFFFFFFF做一下位與
347         long y = getUnsignedInt(in, offset, 4);
348         long z = getUnsignedInt(in, offset + 44);
349         long a = getUnsignedInt(key, 04);
350         long b = getUnsignedInt(key, 44);
351         long c = getUnsignedInt(key, 84);
352         long d = getUnsignedInt(key, 124);
353         // 算法的一些控制變量,sum在這裏也有數了,這個sum和迭代次數有關係
354         // 因爲delta是這麼多,所以sum如果是這麼多的話,迭代的時候減減減,減16次,最後
355         // 得到0。反正這就是爲了得到和加密時相反順序的控制變量,這樣才能解密呀~~
356         long sum = 0xE3779B90;
357         sum &= 0xFFFFFFFFL;
358         long delta = 0x9E3779B9;
359         delta &= 0xFFFFFFFFL;
360 
361         // 迭代開始了, @_@
362         while(loop-- > 0) {
363             z -= ((y << 4+ c) ^ (y + sum) ^ ((y >>> 5+ d);
364             z &= 0xFFFFFFFFL;
365             y -= ((z << 4+ a) ^ (z + sum) ^ ((z >>> 5+ b);
366             y &= 0xFFFFFFFFL;
367             sum -= delta;
368             sum &= 0xFFFFFFFFL;
369         }
370 
371         baos.reset();
372         writeInt((int)y);
373         writeInt((int)z);
374         return baos.toByteArray();
375     }
376     
377     /**
378      * 寫入一個整型到輸出流,高字節優先
379      * 
380      * @param t
381      */
382     private void writeInt(int t) {
383         baos.write(t >>> 24);
384         baos.write(t >>> 16);
385         baos.write(t >>> 8);
386         baos.write(t);
387     }
388     
389     /**
390      * 解密
391      * 
392      * @param in
393      * 密文
394      * @return
395      * 明文
396      */
397     private byte[] decipher(byte[] in) {
398         return decipher(in, 0);
399     }
400     
401     /**
402      * 加密8字節 
403      */
404     private void encrypt8Bytes() {
405         // 這部分完成我上面所說的 plain ^ preCrypt,注意這裏判斷了是不是第一個8字節塊,如果是的話,那個prePlain就當作preCrypt用
406         for(pos = 0; pos < 8; pos++) {
407             if(header) 
408             plain[pos] ^= prePlain[pos];
409             else
410             plain[pos] ^= out[preCrypt + pos];
411         }
412         // 這個完成我上面說的 f(plain ^ preCrypt)
413         byte[] crypted = encipher(plain);
414         // 這個沒什麼,就是拷貝一下,java不像c,所以我只好這麼幹,c就不用這一步了
415         System.arraycopy(crypted, 0, out, crypt, 8);
416         
417         // 這個完成了 f(plain ^ preCrypt) ^ prePlain,ok,下面拷貝一下就行了
418         for(pos = 0; pos < 8; pos++)
419             out[crypt + pos] ^= prePlain[pos];
420         System.arraycopy(plain, 0, prePlain, 08);
421         
422         // 完成了加密,現在是調整crypt,preCrypt等等東西的時候了
423         preCrypt = crypt;
424         crypt += 8;      
425         pos = 0;
426         header = false;            
427     }
428 
429     /**
430      * 解密8個字節
431      * 
432      * @param in
433      * 密文字節數組
434      * @param offset
435      * 從何處開始解密
436      * @param len
437      * 密文的長度
438      * @return
439      * true表示解密成功
440      */
441     private boolean decrypt8Bytes(byte[] in , int offset, int len) {
442         // 這裏第一步就是判斷後面還有沒有數據,沒有就返回,如果有,就執行 crypt ^ prePlain
443         for(pos = 0; pos < 8; pos++) {
444             if(contextStart + pos >= len) 
445                 return true;
446             prePlain[pos] ^= in[offset + crypt + pos];
447         }
448         
449         // 好,這裏執行到了 d(crypt ^ prePlain)
450         prePlain = decipher(prePlain);
451         if(prePlain == null)
452         return false;
453         
454         // 解密完成,最後一步好像沒做? 
455         // 這裏最後一步放到decrypt裏面去做了,因爲解密的步驟有點不太一樣
456         // 調整這些變量的值先
457         contextStart += 8;
458         crypt += 8;
459         pos = 0;
460         return true;
461     }
462     
463     /**
464      * 這是個隨機因子產生器,用來填充頭部的,如果爲了調試,可以用一個固定值
465      * 隨機因子可以使相同的明文每次加密出來的密文都不一樣
466      * 
467      * @return
468      * 隨機因子
469      */
470     private int rand() {
471         return random.nextInt();
472     }
473 }

貌似比較長啊...童鞋們努力吧~~

哎呀, 頭疼...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章