本人还是在校学生,由于滤涉世未深,对互联网还心存敬畏,没能像当初分析2006飞信协议的大哥那样洒脱,出于各种顾虑,当初在写飞信登录协议相关的文章的时候,都没详细地阐述登录过程,那篇文章当初也只是做为自己业余爱好的一个总结,却没想会受到大家如此的关注,很多人留言或发邮件问我具体的身份验证过程及算法,我也无暇一一详细解答,后来想了想,互联网应该是一个开放的环境,大家对飞信的关注其实也是为了给更多人提供方便或者学习交流,我想我也无须吝啬,就在这里把详细地身份验证过程阐述一下,仅用于学习交流,希望大家把它用在正途。
另外,我本人也在用业余时间写了一个Linux环境下的 飞信,期待大家的加入,有兴趣的可以在这里留言。
具体身份验证流程我在下面这篇文章里面说得比较详细了,有需要的话可以先阅读一下这篇文章:
在里面有两个重要的过程:
1.密码的处理方式。
密码并不是以明文的方式进行加密的,而是用SHA1做了散列之后才进行加密的,散列的过程与2008略有不同,文字表达能力有限,就贴几个C函数吧:
InBlock.gif char* hash_password_v1( const unsigned char* b0 , int b0len
InBlock.gif                                                                     , const unsigned char* password , int psdlen)    
InBlock.gif{
InBlock.gif  unsigned char* dst = (unsigned char*)malloc(b0len + psdlen + 1);
InBlock.gif  unsigned char tmp[20];
InBlock.gif   char* res;
InBlock.gif  memset(tmp , 0 , sizeof(tmp));
InBlock.gif  memset(dst , 0 , b0len + psdlen + 1);
InBlock.gif  memcpy(dst , b0 , b0len);
InBlock.gif  memcpy(dst + b0len , password , psdlen);
InBlock.gif  SHA_CTX ctx;
InBlock.gif  SHA1_Init(&ctx);
InBlock.gif  SHA1_Update(&ctx , dst , b0len + psdlen );
InBlock.gif  SHA1_Final(tmp , &ctx);
InBlock.gif  free(dst);
InBlock.gif  res = hextostr(tmp , 20);
InBlock.gif   return res;
InBlock.gif}
InBlock.gif char* hash_password_v2( const char* userid , const char* passwordhex)    
InBlock.gif{
InBlock.gif   int id = atoi(userid);
InBlock.gif   char* res;
InBlock.gif  unsigned char* bid = (unsigned char*)(&id);
InBlock.gif  unsigned char ubid[4];
InBlock.gif   int bpsd_len;
InBlock.gif  unsigned char* bpsd = strtohex(passwordhex , &bpsd_len);
InBlock.gif  memcpy(ubid , bid , 4);
InBlock.gif  res = hash_password_v1(ubid , sizeof(id) , bpsd , bpsd_len);
InBlock.gif  free(bpsd);
InBlock.gif   return res;
InBlock.gif}
InBlock.gif char* hash_password_v4( const char* userid , const char* password)
InBlock.gif{
InBlock.gif   const char* domain = "fetion.com.cn:";
InBlock.gif   char *res , *dst;
InBlock.gif  unsigned char* udomain = (unsigned char*)malloc(strlen(domain));
InBlock.gif  unsigned char* upassword = (unsigned char*)malloc(strlen(password));
InBlock.gif  memset(udomain , 0 , strlen(domain));
InBlock.gif  memcpy(udomain , (unsigned char*)domain , strlen(domain));
InBlock.gif  memset(upassword , 0 , strlen(password));
InBlock.gif  memcpy(upassword , (unsigned char*)password , strlen(password));
InBlock.gif  res = hash_password_v1(udomain , strlen(domain) , upassword , strlen(password));
InBlock.gif  free(udomain);
InBlock.gif  free(upassword);
InBlock.gif   if(userid == NULL || strlen(userid) == 0)
InBlock.gif  {
InBlock.gif     return res;
InBlock.gif  }
InBlock.gif  dst = hash_password_v2(userid , res);
InBlock.gif  free(res);
InBlock.gif   return dst;
InBlock.gif}
第三个函数是用来对密码进行散列处理的函数,输入是userid和密码,这个函数返回一个40字节的16进制字符串。
另外还有两个函数是在unsigned char串和char串之间相互转换:
InBlock.gifunsigned char* strtohex( const char* in , int* len)    
InBlock.gif{
InBlock.gif  unsigned char* out = (unsigned char*)malloc(strlen( in)/2 );
InBlock.gif   int i = 0 , j = 0 , k = 0 ,length = 0;
InBlock.gif   char tmp[3] = { 0 };
InBlock.gif  memset( out , 0 , strlen( in) / 2);
InBlock.gif   while(i < ( int)strlen( in))
InBlock.gif  {
InBlock.gif    tmp[k++] = in[i++];
InBlock.gif    tmp[k] = '\0';
InBlock.gif     if(k == 2)
InBlock.gif    {
InBlock.gif       out[j++] = (unsigned char)strtol(tmp , ( char**)NULL , 16);
InBlock.gif      k = 0;
InBlock.gif      length ++;
InBlock.gif    }
InBlock.gif  }
InBlock.gif   if(len != NULL )
InBlock.gif    *len = length;
InBlock.gif   return out;
InBlock.gif}
InBlock.gif char* hextostr( const unsigned char* in , int len)    
InBlock.gif{
InBlock.gif   char* res = ( char*)malloc(len * 2 + 1);
InBlock.gif   int i = 0;
InBlock.gif  memset(res , 0 , len * 2 + 1);
InBlock.gif   while(i < len)
InBlock.gif  {
InBlock.gif    sprintf(res + i * 2 , "%02x" , in[i]);
InBlock.gif    i ++;
InBlock.gif  };
InBlock.gif  i = 0;
InBlock.gif   while(i < ( int)strlen(res))
InBlock.gif  {
InBlock.gif    res[i] = toupper(res[i]);
InBlock.gif    i ++;
InBlock.gif  };
InBlock.gif   return res;
InBlock.gif}
然后是计算response过程:
InBlock.gif /*这当然是不符合语法的,这样写大家应该就能明白*/
InBlock.gifunsigned char* response_tmp = (unsigned char*)nonce
InBlock.gif                    + strtohex(hashed_password) + strtohex(aedkey);
最后把response_tmp通过服务器发过来的公钥进行RSA加密后得到response,传回服务器就可以通过验证了。
具体的信令格式都是明文,大家自己抓包看就好了,我在这里也就不多说了。