TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,支持128位密码,与BlowFish一样TEA每次只能加密/解密8字节数据。TEA特点是速度快、效率高,实现也非常简单。由于针对TEA的***不断出现,所以TEA也发展出几个版本,分别是XTEA、Block TEA和XXTEA。

TEA加密和解密时都使用一个常量值,这个常量值为0x9e3779b,这个值是近似黄金分割率,注意,有些编程人员为了避免在程序中直接出现"mov 变量,0x9e3779b",以免被破解者直接搜索0x9e3779b这个常数得知使用TEA算法,所以有时会使用"sub 变量,0x61C88647"代替"mov 变量,0x9e3779b",0x61C88647=-(0x9e3779b)。

TEA算法每一次可以操作64bit(8byte),采用128bit(16byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮。

标准的16轮运算TEA,如果要改成标准的32轮运算TEA,只需修改code和decode中的n为32,并将decode中的delta左移4位改成左移5位即可。

C#的实现代码:

publicstaticclassTea { publicstaticbyte[]Encrypt(byte[]data,byte[]key) { byte[]dataBytes; if(data.Length%2==0) { datadataBytes=data; } else { dataBytes=newbyte[data.Length+1]; Array.Copy(data,0,dataBytes,0,data.Length); dataBytes[data.Length]=0x0; } byte[]result=newbyte[dataBytes.Length*4]; uint[]formattedKey=FormatKey(key); uint[]tempData=newuint[2]; for(inti=0;i<dataBytes.Length;i+=2) { tempData[0]=dataBytes[i]; tempData[1]=dataBytes[i+1]; code(tempData,formattedKey); Array.Copy(ConvertUIntToByteArray(tempData[0]),0,result,i*4,4); Array.Copy(ConvertUIntToByteArray(tempData[1]),0,result,i*4+4,4); } returnresult; } publicstaticbyte[]Decrypt(byte[]data,byte[]key) { uint[]formattedKey=FormatKey(key); intx=0; uint[]tempData=newuint[2]; byte[]dataBytes=newbyte[data.Length/8*2]; for(inti=0;i<data.Length;i+=8) { tempData[0]=ConvertByteArrayToUInt(data,i); tempData[1]=ConvertByteArrayToUInt(data,i+4); decode(tempData,formattedKey); dataBytes[x++]=(byte)tempData[0]; dataBytes[x++]=(byte)tempData[1]; } //修剪添加的空字符 if(dataBytes[dataBytes.Length-1]==0x0) { byte[]result=newbyte[dataBytes.Length-1]; Array.Copy(dataBytes,0,result,0,dataBytes.Length-1); } returndataBytes; } staticuint[]FormatKey(byte[]key) { if(key.Length==0) thrownewArgumentException("Keymustbebetween1and16charactersinlength"); byte[]refineKey=newbyte[16]; if(key.Length<16) { Array.Copy(key,0,refineKey,0,key.Length); for(intk=key.Length;k<16;k++) { refineKey[k]=0x20; } } else { Array.Copy(key,0,refineKey,0,16); } uint[]formattedKey=newuint[4]; intj=0; for(inti=0;i<refineKey.Length;i+=4) formattedKey[j++]=ConvertByteArrayToUInt(refineKey,i); returnformattedKey; } #regionTeaAlgorithm staticvoidcode(uint[]v,uint[]k) { uinty=v[0]; uintz=v[1]; uintsum=0; uintdelta=0x9e3779b9; uintn=16; while(n-->0) { sum+=delta; y+=(z<<4)+k[0]^z+sum^(z>>5)+k[1]; z+=(y<<4)+k[2]^y+sum^(y>>5)+k[3]; } v[0]=y; v[1]=z; } staticvoiddecode(uint[]v,uint[]k) { uintn=16; uintsum; uinty=v[0]; uintz=v[1]; uintdelta=0x9e3779b9; /* *由于进行16轮运算,所以将delta左移4位,减16次后刚好为0. */ sum=delta<<4; while(n-->0) { z-=(y<<4)+k[2]^y+sum^(y>>5)+k[3]; y-=(z<<4)+k[0]^z+sum^(z>>5)+k[1]; sum-=delta; } v[0]=y; v[1]=z; } #endregion privatestaticbyte[]ConvertUIntToByteArray(uintv) { byte[]result=newbyte[4]; result[0]=(byte)(v&0xFF); result[1]=(byte)((v>>8)&0xFF); result[2]=(byte)((v>>16)&0xFF); result[3]=(byte)((v>>24)&0xFF); returnresult; } privatestaticuintConvertByteArrayToUInt(byte[]v,intoffset) { if(offset+4>v.Length)return0; uintoutput; output=(uint)v[offset]; output|=(uint)(v[offset+1]<<8); output|=(uint)(v[offset+2]<<16); output|=(uint)(v[offset+3]<<24); returnoutput; } }

XTEA 跟 TEA 使用了相同的简单运算,但它采用了截然不同的顺序,为了阻止密钥表***,四个子密钥(在加密过程中,原 128 位的密钥被拆分为 4 个 32 位的子密钥)采用了一种不太正规的方式进行混合,但速度更慢了。在跟描述 XTEA 算法的同一份报告中,还介绍了另外一种被称为 Block TEA 算法的变种,它可以对 32 位大小任意倍数的变量块进行操作。该算法将 XTEA 轮循函数依次应用于块中的每个字,并且将它附加于它的邻字。该操作重复多少轮依赖于块的大小,但至少需要 6 轮。该方法的优势在于它无需操作模式(CBC,OFB,CFB 等),密钥可直接用于信息。对于长的信息它可能比 XTEA 更有效率。在 1998 年,Markku-Juhani Saarinen 给出了一个可有效*** Block TEA 算法的代码,但之后很快 David J. Wheeler 和 Roger M. Needham 就给出了 Block TEA 算法的修订版,这个算法被称为 XXTEA。XXTEA 使用跟 Block TEA 相似的结构,但在处理块中每个字时利用了相邻字。它利用一个更复杂的 MX 函数代替了 XTEA 轮循函数,MX 使用 2 个输入量。

如果加密字符串长度不是 4 的整数倍,则这些实现的在加密后无法真正还原,还原以后的字符串实际上与原字符串不相等,而是后面多了一些 \0 的字符,或者少了一些 \0 的字符。原因在于 XXTEA 算法只定义了如何对 32 位的信息块数组(实际上是 32 位无符号整数数组)进行加密,而并没有定义如何来将字符串编码为这种数组。而现有的实现中在将字符串编码为整数数组时,都丢失了字符串长度信息,因此还原出现了问题。C#的实现代码

usingSystem; classXXTEA { publicstaticByte[]Encrypt(Byte[]Data,Byte[]Key) { if(Data.Length==0) { returnData; } returnToByteArray(Encrypt(ToUInt32Array(Data,true),ToUInt32Array(Key,false)),false); } publicstaticByte[]Decrypt(Byte[]Data,Byte[]Key) { if(Data.Length==0) { returnData; } returnToByteArray(Decrypt(ToUInt32Array(Data,false),ToUInt32Array(Key,false)),true); } publicstaticUInt32[]Encrypt(UInt32[]v,UInt32[]k) { Int32n=v.Length-1; if(n<1) { returnv; } if(k.Length<4) { UInt32[]Key=newUInt32[4]; k.CopyTo(Key,0); k=Key; } UInt32z=v[n],y=v[0],delta=0x9E3779B9,sum=0,e; Int32p,q=6+52/(n+1); while(q-->0) { sum=unchecked(sum+delta); e=sum>>2&3; for(p=0;p<n;p++) { y=v[p+1]; z=unchecked(v[p]+=(z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z)); } y=v[0]; z=unchecked(v[n]+=(z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z)); } returnv; } publicstaticUInt32[]Decrypt(UInt32[]v,UInt32[]k) { Int32n=v.Length-1; if(n<1) { returnv; } if(k.Length<4) { UInt32[]Key=newUInt32[4]; k.CopyTo(Key,0); k=Key; } UInt32z=v[n],y=v[0],delta=0x9E3779B9,sum,e; Int32p,q=6+52/(n+1); sum=unchecked((UInt32)(q*delta)); while(sum!=0) { e=sum>>2&3; for(p=n;p>0;p--) { z=v[p-1]; y=unchecked(v[p]-=(z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z)); } z=v[n]; y=unchecked(v[0]-=(z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z)); sum=unchecked(sum-delta); } returnv; } privatestaticUInt32[]ToUInt32Array(Byte[]Data,BooleanIncludeLength) { Int32n=(((Data.Length&3)==0)?(Data.Length>>2):((Data.Length>>2)+1)); UInt32[]Result; if(IncludeLength) { Result=newUInt32[n+1]; Result[n]=(UInt32)Data.Length; } else { Result=newUInt32[n]; } n=Data.Length; for(Int32i=0;i<n;i++) { Result[i>>2]|=(UInt32)Data[i]<<((i&3)<<3); } returnResult; } privatestaticByte[]ToByteArray(UInt32[]Data,BooleanIncludeLength) { Int32n; if(IncludeLength) { n=(Int32)Data[Data.Length-1]; } else { n=Data.Length<<2; } Byte[]Result=newByte[n]; for(Int32i=0;i<n;i++) { Result[i]=(Byte)(Data[i>>2]>>((i&3)<<3)); } returnResult; } }