TEA, QQ消息加密, 你搞死我了!

差点吐血! 这破玩意搞了N天, 终于搞定了, 但是很遗憾的是, 还是不明就里, 暂时先把工作进行下去吧...

因为需要跟Java版的通信, Java加密, C#的再解密, 这样就带来很多问题, 不过最终还是OK了.

代码如下(那个迭代过程实在是让我头疼啊, 看来算法还得加强!):

 

ExpandedBlockStart.gif 代码
  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  +   4 4 );
 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  +   4 4 );
 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,  0 16 );
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版的代码, 带部分注释(网上找的).

 

ExpandedBlockStart.gif 代码
  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  <   0 return   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,  0 4 );
303           long  z  =  getUnsignedInt(in,  4 4 );
304           long  a  =  getUnsignedInt(key,  0 4 );
305           long  b  =  getUnsignedInt(key,  4 4 );
306           long  c  =  getUnsignedInt(key,  8 4 );
307           long  d  =  getUnsignedInt(key,  12 4 );
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  +   4 4 );
349           long  a  =  getUnsignedInt(key,  0 4 );
350           long  b  =  getUnsignedInt(key,  4 4 );
351           long  c  =  getUnsignedInt(key,  8 4 );
352           long  d  =  getUnsignedInt(key,  12 4 );
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,  0 8 );
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  }

貌似比较长啊...童鞋们努力吧~~

哎呀, 头疼...

转载于:https://www.cnblogs.com/tonykent/archive/2010/05/06/1728614.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值