研究了几天 飞信 协议,就是为了写个轻巧的飞信客户端,没有别的目的,就只为实现自发短信就可以了。
网上提供的有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:...之间的那个空格去掉的话,是得不到正确结果的。。。
亲身体会。。。
| < Prev | Next > |
|---|



