c# tea算法_[修改]C#版的TEA加密算法,我所发现的比较好的版本。

1 usingSystem;2 namespaceOverredQQ.QQCore3 {4 ///5 ///加密解密QQ消息的工具类. QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节6 ///我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块7 ///f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain ˆ preCrypt) ˆ8 ///prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt ˆ prePlain) ˆ9 ///preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数10 ///填充的字节数与原始明文长度有关,填充的方法是:11 ///12 ///13 ///14 ///------- 消息填充算法 -----------15 ///a = (明文长度 + 10) mod 816 ///if(a 不等于 0) a = 8 - a;17 ///b = 随机数 & 0xF8 | a;              这个的作用是把a的值保存了下来18 ///plain[0] = b;                       然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置19 ///plain[1 至 a+2] = 随机数 & 0xFF;    这里用随机数填充明文的第1到第a+2个字节20 ///plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文21 ///plain[a+3+明文长度, 最后] = 0;       在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容22 ///------- 消息填充算法 ------------23 ///24 ///25 ///26 ///27 ///28 ///29 ///30 ///31 ///32 ///33 ///34 ///overred35 ///36 37 publicstructCrypter38 {39 ///40 ///指向当前的明文块41 ///42 privatebyte[] plain;43 ///44 ///这指向前面一个明文块45 ///46 privatebyte[] prePlain;47 ///48 ///输出的密文或者明文49 ///50 privatebyte[] output;51 ///52 ///当前加密的密文位置和上一次加密的密文块位置,他们相差853 ///54 privateintcrypt, preCrypt;55 ///56 ///当前处理的加密解密块的位置57 ///58 privateintpos;59 ///60 ///填充数61 ///62 privateintpadding;63 ///64 ///密钥65 ///66 privatebyte[] key;67 ///68 ///用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的69 ///但是最开始的8个字节没有反馈可用,所有需要标明这种情况70 ///71 privateboolheader;72 ///73 ///这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时74 ///后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错75 ///76 privateintcontextStart;77 78 ///79 ///随机类80 ///81 privatestaticSystem.Random random_Renamed_Field;82 83 ///84 ///随机数对象85 ///86 privatestaticSystem.Random random;87 88 ///89 ///随机数对象90 ///91 publicstaticSystem.Random xRandom92 {93 get94 {95 if(random_Renamed_Field==null)96 random_Renamed_Field=newSystem.Random();97 returnrandom_Renamed_Field;98 }99 }100 101 102 publicstaticbyte[] ToBytes(uinta,uintb)103 {104 byte[] bytes=newbyte[8];105 bytes[0]=(byte)(a>>24);106 bytes[1]=(byte)(a>>16);107 bytes[2]=(byte)(a>>8);108 bytes[3]=(byte)a;109 bytes[4]=(byte)(b>>24);110 bytes[5]=(byte)(b>>16);111 bytes[6]=(byte)(b>>8);112 bytes[7]=(byte)b;113 returnbytes;114 }115 116 117 ///118 ///把字节数组从offset开始的len个字节转换成一个unsigned int, 因为C#里面有unsigned,所以unsigned119 ///int使用uint表示的。如果len大于4,则认为len等于4。如果len小于4,则高位填0
120 ///(edited by ) 改变了算法, 性能稍微好一点. 在我的机器上测试10000次, 原始算法花费18s, 这个算法花费12s.121 ///122 ///123 ///字节数组.124 ///125 ///126 ///从哪里开始转换.127 ///128 ///129 ///转换长度, 如果len超过8则忽略后面的130 ///131 ///132 ///133 publicstaticuintGetUInt(byte[] input,intoffset,intlen)134 {135 uintret=0;136 intend=(len>4)?(offset+4) : (offset+len);137 for(inti=offset; i需要被解密的密文146 ///密钥147 ///Message 已解密的消息148 publicstaticbyte[] Decrypt(byte[] input,byte[] key)149 {150 Crypter crypter=newCrypter();151 crypter.header=true;152 returncrypter.Decrypt0(input, key);153 }154 155 ///需要被解密的密文156 ///密钥157 ///Message 已解密的消息158 publicstaticbyte[] Decrypt(byte[] input,intoffset,intlen,byte[] key)159 {160 Crypter crypter=newCrypter();161 crypter.header=true;162 returncrypter.Decrypt0(input, offset, len, key);163 }164 165 ///需要加密的明文166 ///密钥167 ///Message 密文168 publicstaticbyte[] Encrypt(byte[] input,byte[] key)169 {170 Crypter crypter=newCrypter();171 crypter.header=true;172 returncrypter.Encrypt0(input, key);173 }174 175 ///需要加密的明文176 ///密钥177 ///Message 密文178 publicstaticbyte[] Encrypt(byte[] input,intoffset,intlen,byte[] key)179 {180 Crypter crypter=newCrypter();181 crypter.header=true;182 returncrypter.Encrypt0(input, offset, len, key);183 }184 185 ///186 ///抛出异常。187 ///188 ///异常信息189 privatestaticvoidthrowException(stringmessage)190 {191 thrownewCrypterException(message);192 }193 194 ///解密195 ///196 ///密文197 ///198 ///199 ///密文开始的位置200 ///201 ///202 ///密文长度203 ///204 ///205 ///密钥206 ///207 ///明文208 ///209 publicbyte[] Decrypt0(byte[] input,intoffset,intlen,byte[] key)210 {211 crypt=preCrypt=0;212 this.key=key;213 intcount;214 byte[] m=newbyte[offset+8];215 216 //因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况217 if((len%8!=0)||(len<16))218 throwException(@"len is not correct.");219 //得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与220 prePlain=Decipher(input, offset);221 pos=prePlain[0]&0x7;222 //得到真正明文的长度223 count=len-pos-10;224 //如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回225 if(count<0)226 throwException(@"count is not correct");227 228 //这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时229 //第一个8字节块也没有preCrypt,所有这里建一个全0的230 for(inti=offset; i306 ///需要被解密的密文307 ///308 ///309 ///密钥310 ///311 ///Message 已解密的消息312 ///313 publicbyte[] Decrypt0(byte[] input,byte[] key)314 {315 returnDecrypt(input,0, input.Length, key);316 }317 318 ///加密319 ///明文字节数组320 ///321 ///开始加密的偏移322 ///323 ///加密长度324 ///325 ///密钥326 ///327 ///密文字节数组328 ///329 publicbyte[] Encrypt0(byte[] input,intoffset,intlen,byte[] key)330 {331 plain=newbyte[8];332 prePlain=newbyte[8];333 pos=1;334 padding=0;335 crypt=preCrypt=0;336 this.key=key;337 header=true;338 339 //计算头部填充字节数340 pos=(len+0x0A)%8;341 if(pos!=0)342 pos=8-pos;343 //计算输出的密文长度344 output=newbyte[len+pos+10];345 //这里的操作把pos存到了plain的第一个字节里面346 //0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置347 intt1=0x7648354F;348 349 plain[0]=(byte)((t1&0xF8)|pos);350 351 //这里用随机产生的数填充plain[1]到plain[pos]之间的内容352 for(inti=1; i<=pos; i++)353 plain[i]=(byte)(t1&0xFF);354 pos++;355 //这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块356 for(inti=0; i<8; i++)357 prePlain[i]=(byte)(0x0);358 359 //继续填充2个字节的随机数,这个过程中如果满了8字节就加密之360 padding=1;361 while(padding<=2)362 {363 if(pos<8)364 {365 plain[pos++]=(byte)(t1&0xFF);366 padding++;367 }368 if(pos==8)369 Encrypt8Bytes();370 }371 372 //头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完373 inti2=offset;374 while(len>0)375 {376 if(pos<8)377 {378 plain[pos++]=input[i2++];379 len--;380 }381 if(pos==8)382 Encrypt8Bytes();383 }384 385 //最后填上0,以保证是8字节的倍数386 padding=1;387 while(padding<=7)388 {389 if(pos<8)390 {391 plain[pos++]=(byte)(0x0);392 padding++;393 }394 if(pos==8)395 Encrypt8Bytes();396 }397 398 returnoutput;399 }400 401 ///402 ///需要加密的明文403 ///404 ///405 ///密钥406 ///407 ///Message 密文408 ///409 publicbyte[] Encrypt0(byte[] input,byte[] key)410 {411 returnEncrypt(input,0, input.Length, key);412 }413 414 ///415 ///加密一个8字节块416 ///417 ///418 ///明文字节数组419 ///420 ///421 ///密文字节数组422 ///423 privatebyte[] Encipher(byte[] input)424 {425 if(key==null)426 {427 throwException(@"key is null.");428 }429 //迭代次数,16次430 intloop=0x10;431 //得到明文和密钥的各个部分,注意c#有无符号类型,所以为了表示一个无符号的整数432 //我们用了uint,这个uint的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的uint也都是一样的433 //而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与434 uinty=GetUInt(input,0,4);435 uintz=GetUInt(input,4,4);436 uinta=GetUInt(key,0,4);437 uintb=GetUInt(key,4,4);438 uintc=GetUInt(key,8,4);439 uintd=GetUInt(key,12,4);440 //这是算法的一些控制变量,为什么delta是0x9E3779B9呢?441 //这个数是TEA算法的delta,实际是就是sqr(5)-1 * 2^31442 uintsum=0;443 uintdelta=0x9E3779B9;444 //delta &= unchecked((int) 0xFFFFFFFFL);445 446 //开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去447 while(loop-->0)448 {449 sum+=delta;450 //sum &= unchecked((int) 0xFFFFFFFFL);451 y+=((z<<4)+a)^(z+sum)^(z>>5)+b;452 //y &= unchecked((int) 0xFFFFFFFFL);453 z+=((y<<4)+c)^(y+sum)^(y>>5)+d;454 //z &= unchecked((int) 0xFFFFFFFFL);455 }456 457 //最后,我们输出密文,因为我用的uint,所以需要强制转换一下变成int458 459 returnToBytes(y, z);460 }461 462 ///463 ///解密从offset开始的8字节密文464 ///465 ///466 ///密文字节数组467 ///468 ///469 ///密文开始位置470 ///471 ///472 ///明文473 ///474 privatebyte[] Decipher(byte[] input,intoffset)475 {476 if(key==null)477 {478 throwException(@"key is null.");479 }480 //迭代次数,16次481 intloop=0x10;482 //得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数483 //我们用了uint,这个uint的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的uint也都是一样的484 //而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与485 uinty=GetUInt(input, offset,4);486 uintz=GetUInt(input, offset+4,4);487 uinta=GetUInt(key,0,4);488 uintb=GetUInt(key,4,4);489 uintc=GetUInt(key,8,4);490 uintd=GetUInt(key,12,4);491 //算法的一些控制变量,为什么sum在这里也有数了呢,这个sum嘛就是和迭代次数有关系了492 //因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后493 //得到什么? Yeah,得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样494 //才能解密呀~~495 uintsum=0xE3779B90;496 //sum &= unchecked((int) 0xFFFFFFFFL);497 uintdelta=0x9E3779B9;498 //delta &= unchecked((int) 0xFFFFFFFFL);499 500 //迭代开始了, #_#501 while(loop-->0)502 {503 z-=((y<<4)+c)^(y+sum)^((y>>5)+d);504 //z &= unchecked((int) 0xFFFFFFFFL);505 y-=((z<<4)+a)^(z+sum)^((z>>5)+b);506 //y &= unchecked((int) 0xFFFFFFFFL);507 sum-=delta;508 //sum &= unchecked((int) 0xFFFFFFFFL);509 }510 511 //输出明文,注意要转成int512 513 returnToBytes(y, z);514 }515 516 ///517 ///解密518 ///519 ///520 ///密文521 ///522 ///523 ///明文524 ///525 privatebyte[] Decipher(byte[] input)526 {527 returnDecipher(input,0);528 }529 530 ///531 ///加密8字节532 ///533 privatevoidEncrypt8Bytes()534 {535 //这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用536 for(pos=0; pos<8; pos++)537 {538 if(header)539 plain[pos]^=prePlain[pos];540 else541 plain[pos]^=output[preCrypt+pos];542 }543 //这个完成到了我上面说的 f(plain ^ preCrypt)544 byte[] crypted=Encipher(plain);545 //这个没什么,就是拷贝一下,java不像c,所以我只好这么干,c就不用这一步了546 Array.Copy(crypted,0, output, crypt,8);547 548 //这个就是完成到了 f(plain ^ preCrypt) ^ prePlain,ok,完成了,下面拷贝一下就行了549 for(pos=0; pos<8; pos++)550 output[crypt+pos]^=prePlain[pos];551 Array.Copy(plain,0, prePlain,0,8);552 553 //完成了加密,现在是调整crypt,preCrypt等等东西的时候了554 preCrypt=crypt;555 crypt+=8;556 pos=0;557 header=false;558 }559 560 ///561 ///解密8个字节562 ///563 ///564 ///密文字节数组565 ///566 ///567 ///从何处开始解密568 ///569 ///570 ///密文的长度571 ///572 ///573 ///true表示解密成功574 ///575 privateboolDecrypt8Bytes(byte[] input,intoffset,intlen)576 {577 //这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain578 for(pos=0; pos<8; pos++)579 {580 if(contextStart+pos>=len)581 returntrue;582 prePlain[pos]^=input[offset+crypt+pos];583 }584 585 //好,这里执行到了 d(crypt ^ prePlain)586 prePlain=Decipher(prePlain);587 if(prePlain==null)588 returnfalse;589 590 //解密完成,wait,没完成哦,最后一步没做哦?591 //这里最后一步放到Decrypt里面去做了,因为解密的步骤毕竟还是不太一样嘛592 //调整这些变量的值先593 contextStart+=8;594 crypt+=8;595 pos=0;596 returntrue;597 }598 599 ///600 ///这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值。601 ///随机因子可以使相同的明文每次加密出来的密文都不一样。602 ///603 ///604 ///随机因子605 ///606 privateintRand()607 {608 returnxRandom.Next();609 }610 staticCrypter()611 {612 random=xRandom;613 }614 }615 616 ///617 ///加密/解密出错异常。618 ///619 publicclassCrypterException : Exception620 {621 publicCrypterException(stringmessage)622 :base(message)623 {624 }625 }626 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值