Luma写的 我翻译的
using System;
using System.Collections.Generic;
using
System.Text;
using System.Net;
namespace Nutqq
{
public class Crypter
{
// 指向当前的明文块
private byte[] plain;
//
这指向前面一个明文块
private byte[] prePlain;
//
输出的密文或者明文
private byte[] outData;
//
当前加密的密文位置和上一次加密的密文块位置,他们相差8
private int crypt, preCrypt;
// 当前处理的加密解密块的位置
private int pos;
// 填充数
private int padding;
// 密钥
private byte[] key;
private bool header = true;
private int contextStart;
private static byte rand = 0x0;
public byte[] encrypt(byte[] inData, int offset, int len, byte[]
key)
{
plain = new byte[8];
prePlain =
new byte[8];
pos = 1;
padding = 0;
crypt = preCrypt = 0;
this.key = key;
header =
true;
// 计算头部填充字节数
pos = (len + 0x0A) %
8;
if (pos != 0)
pos = 8 - pos;
// 计算输出的密文长度
outData = new byte[len + pos + 10];
// 这里的操作把pos存到了plain的第一个字节里面
//
0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置
plain[0] =
(byte)((rand & 0xF8) | pos);
// 这里用随机产生的数填充plain[1]到plain[pos]之间的内容
for (int i
= 1; i <= pos; i++)
plain[i] = (byte)(rand &
0xFF);
pos++;
//
这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块
for (int i =
0; i < 8; i++)
prePlain[i] = (byte)(0x0);
// 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之
padding =
1;
while (padding <= 2)
{
if
(pos < 8)
{
plain[pos++] =
(byte)(rand & 0xFF);
padding++;
}
if (pos == 8)
encrypt8Bytes();
}
// 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完
int i2 =
offset;
while (len > 0)
{
if
(pos < 8)
{
plain[pos++] =
inData[i2++];
len--;
}
if (pos == 8)
encrypt8Bytes();
}
// 最后填上0,以保证是8字节的倍数
padding = 1;
while (padding <= 7)
{
if (pos <
8)
{
plain[pos++] =
(byte)(0x0);
padding++;
}
if (pos == 8)
encrypt8Bytes();
}
return outData;
}
private void encrypt8Bytes()
{
for (pos = 0; pos < 8; pos++)
{
if (header)
plain[pos] ^=
prePlain[pos];
else
plain[pos] ^=
outData[preCrypt + pos];
}
byte[] crypted = encipher(plain);
Array.Copy(crypted, 0, outData, crypt, 8);
for (pos = 0; pos < 8; pos++)
outData[crypt
+ pos] ^= prePlain[pos];
Array.Copy(plain, 0, prePlain, 0,
8);
preCrypt = crypt;
crypt += 8;
pos = 0;
header = false;
}
public byte[] encrypt(byte[] inData, byte[] key)
{
return encrypt(inData, 0, inData.Length, key);
}
private byte[] encipher(byte[] inData)
{
int loop = 0x10;
uint y2 =
BitConverter.ToUInt32(inData, 0);
uint y =(uint)(IPAddress
.NetworkToHostOrder ( y2)>>32);
uint z2 = BitConverter.ToUInt32(inData, 4);
uint z
= (uint)(IPAddress.NetworkToHostOrder(z2) >> 32);
uint a2 = BitConverter.ToUInt32(key, 0);
uint
a = (uint)(IPAddress.NetworkToHostOrder(a2) >> 32);
uint b2
= BitConverter.ToUInt32(key, 4);
uint b =
(uint)(IPAddress.NetworkToHostOrder(b2) >> 32);
uint c2 =
BitConverter.ToUInt32(key, 8);
uint c =
(uint)(IPAddress.NetworkToHostOrder(c2) >> 32);
uint d2 =
BitConverter.ToUInt32(key, 12);
uint d =
(uint)(IPAddress.NetworkToHostOrder(d2) >> 32);
uint sum =
0;
uint delta = 0x9E3779B9;
while (loop-- >
0)
{
sum += delta;
y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
}
byte[] ret = new byte[8];
ret[3] =
(byte)y;
ret[2] = (byte)(y >> 8);
ret[1] =
(byte)(y >> 16);
ret[0] = (byte)(y >>
24);
ret[7] = (byte)z;
ret[6] = (byte)(z >>
8);
ret[5] = (byte)(z >> 16);
ret[4] =
(byte)(z >> 24);
return ret;
}
public byte[] decrypt(byte[] inData, int offset, int len, byte[]
key)
{
crypt = preCrypt =
0;
this.key = key;
int count;
byte[] m = new byte[offset + 8];
if ((len % 8 != 0) || (len < 16))
return null;
prePlain = decipher(inData,
offset);
pos = prePlain[0] & 0x7;
//
得到真正明文的长度
count = len - pos - 10;
//
如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回
if (count <
0)
return null;
// 这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时
// 第一个8字节块也没有preCrypt,所有这里建一个全0的
for (int i = offset; i <
m.Length; i++)
m[i] = 0;
//
通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区
outData = new
byte[count];
//
设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用
preCrypt = 0;
//
当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了
crypt =
8;
// 自然这个也是8
contextStart = 8;
//
加1,和加密算法是对应的
pos++;
// 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
//
因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了
//
但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
//
所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充
padding =
1;
while (padding <= 2)
{
if
(pos < 8)
{
pos++;
padding++;
}
if (pos == 8)
{
m =
inData;
if (!decrypt8Bytes(inData, offset,
len))
return null;
}
}
// 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
//
注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了
int i2 =
0;
while (count != 0)
{
if (pos
< 8)
{
outData[i2] =
(byte)(m[offset + preCrypt + pos] ^ prePlain[pos]);
i2++;
count--;
pos++;
}
if (pos == 8)
{
m = inData;
preCrypt = crypt -
8;
if (!decrypt8Bytes(inData, offset,
len))
return null;
}
}
//
最后的解密部分,上面一个while已经把明文都解出来了,到了这里还剩下什么?对了,还剩下尾部的填充,应该全是0
//
所以这里有检查是否解密了之后是0,如果不是的话那肯定出错了,所以返回null
for (padding = 1; padding
< 8; padding++)
{
if (pos <
8)
{
if ((m[offset + preCrypt + pos] ^
prePlain[pos]) != 0)
return
null;
pos++;
}
if
(pos == 8)
{
m =
inData;
preCrypt = crypt;
if
(!decrypt8Bytes(inData, offset, len))
return
null;
}
}
return
outData;
}
private bool decrypt8Bytes(byte[] inData, int offset, int
len)
{
// 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^
prePlain
for (pos = 0; pos < 8; pos++)
{
if (contextStart + pos >= len)
return true;
prePlain[pos] ^= inData[offset + crypt +
pos];
}
// 好,这里执行到了 d(crypt ^ prePlain)
prePlain =
decipher(prePlain);
if (prePlain == null)
return false;
// 解密完成,wait,没完成哦,最后一步没做哦?
//
这里最后一步放到decrypt里面去做了,因为解密的步骤毕竟还是不太一样嘛
// 调整这些变量的值先
contextStart += 8;
crypt += 8;
pos =
0;
return true;
}
public byte[] decrypt(byte[] inData, byte[] key)
{
return decrypt(inData, 0, inData.Length, key);
}
private byte[] decipher(byte[] inData, int offset)
{
//int loop = 0x10;
//uint y =
BitConverter.ToUInt32(inData, 0);
//uint z =
BitConverter.ToUInt32(inData, 4);
//uint a =
BitConverter.ToUInt32(key, 0);
//uint b =
BitConverter.ToUInt32(key, 4);
//uint c =
BitConverter.ToUInt32(key, 8);
//uint d =
BitConverter.ToUInt32(key, 12);
//uint sum =
0xE3779B90;
//uint delta = 0x9E3779B9;
int loop =
0x10;
uint y2 = BitConverter.ToUInt32(inData,
offset);
uint y = (uint)(IPAddress.NetworkToHostOrder(y2)
>> 32);
uint z2 = BitConverter.ToUInt32(inData, offset+4);
uint z = (uint)(IPAddress.NetworkToHostOrder(z2) >> 32);
uint a2 = BitConverter.ToUInt32(key, 0);
uint
a = (uint)(IPAddress.NetworkToHostOrder(a2) >> 32);
uint b2
= BitConverter.ToUInt32(key, 4);
uint b =
(uint)(IPAddress.NetworkToHostOrder(b2) >> 32);
uint c2 =
BitConverter.ToUInt32(key, 8);
uint c =
(uint)(IPAddress.NetworkToHostOrder(c2) >> 32);
uint d2 =
BitConverter.ToUInt32(key, 12);
uint d =
(uint)(IPAddress.NetworkToHostOrder(d2) >> 32);
uint sum =
0xE3779B90;
uint delta = 0x9E3779B9;
while (loop-- > 0)
{
z -=
((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
y -=
((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
sum
-= delta;
}
byte[] ret = new byte[8];
ret[3] =
(byte)y;
ret[2] = (byte)(y >> 8);
ret[1] =
(byte)(y >> 16);
ret[0] = (byte)(y >>
24);
ret[7] = (byte)z;
ret[6] = (byte)(z >>
8);
ret[5] = (byte)(z >> 16);
ret[4] =
(byte)(z >> 24);
return ret;
}
/// <summary> 解密
///
///
</summary>
/// <param name="">in
///
密文
/// </param>
/// <returns>
///
明文
/// </returns>
private byte[] decipher(byte[]
inData)
{
return decipher(inData, 0);
}
/// <summary> 这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值
///
随机因子可以使相同的明文每次加密出来的密文都不一样
///
///
</summary>
/// <returns>
/// 随机因子
/// </returns>
static Crypter()
{
}
//public static int urshift(uint number, int
bits)
//{
// if (number >= 0)
//
return number >> bits;
// else
// return
(number >> bits) + (uint)(2 << ~bits);
//}
}
}
曾经想写一个.NET下的qq程序,名字都想好了叫NutQQ,后来因为忙毕业设计没有做下去,整个程序只实现了可以登录一个qq帐号并保持在线状态的功能(挂qq?)。昨天偶尔翻起看看还有没有有用的东西,找了半天没有一点可取之处,汗。