给梦一个奔跑的方向!
PDF Print E-mail
User Rating: / 0
PoorBest 
Written by xlingfairy
Tuesday, 08 December 2009 21:09
研究了几天 飞信 协议,就是为了写个轻巧的飞信客户端,没有别的目的,就只为实现自发短信就可以了。
网上提供的有PHP版的,Python 版的,Java 版的,都是基于 nathan 先生的飞信协议分析 
 
这个协议是基于 Fetion 2006 的,Fetion 2008 对登陆部分做了更改,加入了 Sha1 加密算法,(2006版的是MD5加密法)。
如今的互联网,可以称为垃圾处理场,大批垃圾的转贴,让你轻易的陷入搜索的烦恼中。搞不清楚 Fetion 2008 的登陆部分的 response 计算方法,我在原地徘徊了两天,现在,我把整理出来的该部分分享出来,让徘徊你的,少走一点弯路:
1, cnonce 随机,想什么就什么。
--------------------------------------
    private void GetCnonce() {
        Random rnd = new Random();
        using (MD5 md5 = MD5.Create()) {
            cnonce = Encoding.UTF8.GetString(md5.ComputeHash( Encoding.UTF8.GetBytes(rnd.Next(65535).ToString()) ));
        }
    }
 
2, nonce 如果不明白,请参考 nathan 分析的协议
-------------------------------------
 
    public void GetNonce() { 
        TcpRequest tc = TcpRequest.GetInstance(sipcProxyServer, sipcProxyPort, false);
        StringBuilder sb = new StringBuilder();
        sb.Append("R fetion.com.cn SIP-C/2.0\r\n");
        sb.Append(string.Format("F: {0}\r\n", sid));
        sb.Append(string.Format("I: {0}\r\n", ++i));
        sb.Append("Q: 1 R\r\n");
        sb.Append("L: 307\r\n\r\n");
        sb.Append(@"<args><device type=""PC"" version=""2009111301"" client-version=""3.5.1170"" /><caps value=""simple-im;im-session;temp-group;personal-group;im-relay"" /><events value=""contact;permission;system-message;personal-group;compact"" /><user-info attributes=""all"" /><presence><basic value=""400"" desc="""" /></presence></args>");
        tc.Write(sb.ToString());
        sb = tc.GetResponse();
        Regex reg = new Regex(@"nonce=""(?<nonce>[^""]*)""");
        Match ma = reg.Match(sb.ToString());
        nonce = ma.Groups["nonce"].Value;
    }
 
 
3, Bin2Hex
----------------------------------------
    public string Bin2Hex(byte[] binary) {
        StringBuilder builder = new StringBuilder();
        foreach (byte num in binary) {
            if (num > 15) {
                builder.AppendFormat("{0:X}", num);
            } else {
                builder.AppendFormat("0{0:X}", num);/////// 大于 15 就多加个 0
            }
        }
        return builder.ToString();
    }
 
一开始,我就在这个地方犯了个错,因为大于 15 的我没有加前置 0,错误函数如下:
    //private string Bin2Hex(byte[] bytes) {
    //    StringBuilder sb = new StringBuilder();
    //    foreach (byte b in bytes) {
    //        sb.Append(Convert.ToString(b, 16));//没有判断是否大于 15
    //    }
    //    return sb.ToString();
    //}
 
 
 
4, Hex2Bin
------------------------------------
 
    public byte[] Hex2Bin(string hex) {
        if ((hex == null) || (hex.Length < 1)) {
            return new byte[0];
        }
        int num = hex.Length / 2;
        byte[] buffer = new byte[num];
        num *= 2;
        for (int i = 0; i < num; i++) {
            int num3 = int.Parse(hex.Substring(i, 2), NumberStyles.HexNumber);
            buffer[i / 2] = (byte)num3;
            i++;
        }
        return buffer;
    }
 
 
5, Md52Hex
---------------------------------
    private string Md52Hex(byte[] bytes) { 
        using(MD5 md5 = MD5.Create()){
            return Bin2Hex(md5.ComputeHash(bytes));
        }
    }
 
 
6, 密码加密部分,密码是先加密的,后面有关密码操作的部分,都是加密后的密码
-------------------------------------
    public string DoHashPassword(byte[] password) {
        byte[] bytes = BitConverter.GetBytes(Environment.TickCount);
        return DoHashPassword(password, bytes);
    }
 
    public string DoHashPassword(string pwd) {
        char[] chars = pwd.ToCharArray();
        return DoHashPassword(Encoding.UTF8.GetBytes(chars));
    }
 
    public string DoHashPassword(byte[] password, byte[] b0) {
        using (SHA1 sha = SHA1.Create()) {
            byte[] src = sha.ComputeHash(password);
            for (int i = 0; i < password.Length; i++) {
                password[i] = 0;
            }
            byte[] dst = new byte[b0.Length + src.Length];
            Buffer.BlockCopy(b0, 0, dst, 0, b0.Length);
            Buffer.BlockCopy(src, 0, dst, b0.Length, src.Length);
            byte[] buffer3 = sha.ComputeHash(dst);
            byte[] buffer4 = new byte[b0.Length + buffer3.Length];
            Buffer.BlockCopy(b0, 0, buffer4, 0, b0.Length);
            Buffer.BlockCopy(buffer3, 0, buffer4, b0.Length, buffer3.Length);
            return Bin2Hex(buffer4);
        }
    }
 
6, 计算 response
------------------------------------
    public byte[] GetKey() {
        byte[] bytes = Encoding.UTF8.GetBytes(sid + ":" + domain + ":"); // sip:{sid}@{domain};p={p}
        byte[] src = Hex2Bin(encPwd.Substring(8));// encPwd 就加密后的密码
        byte[] dst = new byte[bytes.Length + src.Length];
        Buffer.BlockCopy(bytes, 0, dst, 0, bytes.Length);
        Buffer.BlockCopy(src, 0, dst, bytes.Length, src.Length);
        return Sha1(dst);
    }
 
    private string ComputeH1(byte[] key) {
        string s = string.Format(":{0}:{1}", nonce, cnonce);
        byte[] bytes = Encoding.UTF8.GetBytes(s);
        byte[] array = new byte[key.Length + bytes.Length];
        key.CopyTo(array, 0);
        Array.Copy(bytes, 0, array, key.Length, bytes.Length);
        return Md52Hex(array);
    }
 
    private string ComputeH2() {
        string s = string.Format("REGISTER:{0}", sid);
        return Md52Hex(Encoding.UTF8.GetBytes(s));
    }
 
    private string ComputeResponse(string h1, string h2) {
        string s = string.Format("{0}:{1}:{2}", h1, nonce, h2);
        return Md52Hex(Encoding.UTF8.GetBytes(s));
    }
 
就提供上面这些。
另外,用 Tcp 发送的 SIP 协议,其中的空格需要注意,如:
T: sip:xxx@fetion.com.cn;p=xxx
如果把 T: 和 sip:...之间的那个空格去掉的话,是得不到正确结果的。。。
亲身体会。。。
 

Add comment


Security code
Refresh

Popular Contents

Recommend

Site Info

Members : 1
Content : 100
Web Links : 7
Content View Hits : 56183

Links