C#实现QQ程序的加密算法

QQ的加密算法的C#版本

 

不说废话了,看代码把 转载请注明出处,谢谢了!


Luma写的 我翻译

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace CrazyCoder.QQ
{



    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) &#124;&#124; (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> 解密
        /// by http://www.CrazyCoder.cn/
        /// </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)
        //{

//by  www.CrazyCoder.cn
        //    if (number >= 0)
        //        return number >> bits;
        //    else
        //        return (number >> bits) + (uint)(2 << ~bits);
        //}


    }
}

Tags:  rsa加密算法 md5加密算法 des加密算法 加密算法 qq加密算法

延伸阅读

最新评论

  1. QQ2009能用吗?
  2. Fdsfasdf

发表评论