RealmSession.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Threading.Tasks;
  5. using Helper;
  6. using Log;
  7. using Org.BouncyCastle.Crypto.Agreement.Srp;
  8. using Org.BouncyCastle.Crypto.Digests;
  9. using Org.BouncyCastle.Math;
  10. using Org.BouncyCastle.Security;
  11. using Org.BouncyCastle.Utilities.Encoders;
  12. using Robot.Protos;
  13. namespace Robot
  14. {
  15. public class RealmSession: IDisposable
  16. {
  17. private readonly NetworkStream networkStream;
  18. private readonly RealmInfo realmInfo = new RealmInfo();
  19. public RealmSession(string host, ushort port)
  20. {
  21. Socket socket = ConnectSocket(host, port);
  22. this.networkStream = new NetworkStream(socket);
  23. }
  24. public void Dispose()
  25. {
  26. this.networkStream.Dispose();
  27. }
  28. public static Socket ConnectSocket(string host, ushort port)
  29. {
  30. IPHostEntry hostEntry = Dns.GetHostEntry(host);
  31. foreach (IPAddress address in hostEntry.AddressList)
  32. {
  33. var ipe = new IPEndPoint(address, port);
  34. var tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  35. tempSocket.Connect(ipe);
  36. if (!tempSocket.Connected)
  37. {
  38. continue;
  39. }
  40. return tempSocket;
  41. }
  42. Logger.Debug("socket is null, address: {0}:{1}", host, port);
  43. throw new SocketException(10000);
  44. }
  45. public async void SendMessage<T>(ushort opcode, T message)
  46. {
  47. byte[] protoBytes = ProtobufHelper.ToBytes(message);
  48. var neworkBytes = new byte[sizeof (int) + sizeof (ushort) + protoBytes.Length];
  49. int totalSize = sizeof (ushort) + protoBytes.Length;
  50. var totalSizeBytes = BitConverter.GetBytes(totalSize);
  51. totalSizeBytes.CopyTo(neworkBytes, 0);
  52. var opcodeBytes = BitConverter.GetBytes(opcode);
  53. opcodeBytes.CopyTo(neworkBytes, sizeof (int));
  54. protoBytes.CopyTo(neworkBytes, sizeof (int) + sizeof (ushort));
  55. await this.networkStream.WriteAsync(neworkBytes, 0, neworkBytes.Length);
  56. }
  57. public async Task<SMSG_Password_Protect_Type> Handle_CMSG_AuthLogonPermit_Response()
  58. {
  59. var result = await this.RecvMessage();
  60. ushort opcode = result.Item1;
  61. byte[] message = result.Item2;
  62. if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE)
  63. {
  64. Logger.Trace("opcode: {0}", opcode);
  65. throw new RealmException(string.Format("error opcode: {0}", opcode));
  66. }
  67. var smsgPasswordProtectType =
  68. ProtobufHelper.FromBytes<SMSG_Password_Protect_Type>(message);
  69. Logger.Trace("message: {0}", JsonHelper.ToString(smsgPasswordProtectType));
  70. if (smsgPasswordProtectType.Code != 200)
  71. {
  72. throw new RealmException(string.Format(
  73. "SMSG_Lock_For_Safe_Time: {0}",
  74. JsonHelper.ToString(smsgPasswordProtectType)));
  75. }
  76. return smsgPasswordProtectType;
  77. }
  78. public async Task<SMSG_Auth_Logon_Challenge_Response>
  79. Handle_SMSG_Auth_Logon_Challenge_Response()
  80. {
  81. var result = await this.RecvMessage();
  82. ushort opcode = result.Item1;
  83. byte[] message = result.Item2;
  84. if (opcode != MessageOpcode.SMSG_AUTH_LOGON_CHALLENGE_RESPONSE)
  85. {
  86. Logger.Trace("opcode: {0}", opcode);
  87. }
  88. var smsgAuthLogonChallengeResponse =
  89. ProtobufHelper.FromBytes<SMSG_Auth_Logon_Challenge_Response>(message);
  90. if (smsgAuthLogonChallengeResponse.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS)
  91. {
  92. Logger.Trace("error code: {0}", smsgAuthLogonChallengeResponse.ErrorCode);
  93. throw new RealmException(
  94. string.Format("SMSG_Auth_Logon_Challenge_Response ErrorCode: {0}",
  95. JsonHelper.ToString(smsgAuthLogonChallengeResponse)));
  96. }
  97. return smsgAuthLogonChallengeResponse;
  98. }
  99. public async Task<Tuple<ushort, byte[]>> RecvMessage()
  100. {
  101. int totalReadSize = 0;
  102. int needReadSize = sizeof (int);
  103. var packetBytes = new byte[needReadSize];
  104. while (totalReadSize != needReadSize)
  105. {
  106. int readSize = await this.networkStream.ReadAsync(
  107. packetBytes, totalReadSize, packetBytes.Length);
  108. if (readSize == 0)
  109. {
  110. return new Tuple<ushort, byte[]>(0, new byte[] { });
  111. }
  112. totalReadSize += readSize;
  113. }
  114. int packetSize = BitConverter.ToInt32(packetBytes, 0);
  115. // 读opcode和message
  116. totalReadSize = 0;
  117. needReadSize = packetSize;
  118. var contentBytes = new byte[needReadSize];
  119. while (totalReadSize != needReadSize)
  120. {
  121. int readSize = await this.networkStream.ReadAsync(
  122. contentBytes, totalReadSize, contentBytes.Length);
  123. if (readSize == 0)
  124. {
  125. return new Tuple<ushort, byte[]>(0, new byte[] { });
  126. }
  127. totalReadSize += readSize;
  128. }
  129. ushort opcode = BitConverter.ToUInt16(contentBytes, 0);
  130. var messageBytes = new byte[needReadSize - sizeof (ushort)];
  131. Array.Copy(contentBytes, sizeof (ushort), messageBytes, 0, messageBytes.Length);
  132. return new Tuple<ushort, byte[]>(opcode, messageBytes);
  133. }
  134. public async void Login(string account, string password)
  135. {
  136. byte[] passwordBytes = password.ToByteArray();
  137. var digest = new MD5Digest();
  138. var passwordMd5 = new byte[digest.GetDigestSize()];
  139. digest.BlockUpdate(passwordBytes, 0, passwordBytes.Length);
  140. digest.DoFinal(passwordMd5, 0);
  141. // 发送帐号和密码MD5
  142. var cmsgAuthLogonPermit = new CMSG_Auth_Logon_Permit
  143. {
  144. Account = account,
  145. PasswordMd5 = Hex.ToHexString(passwordMd5)
  146. };
  147. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PERMIT, cmsgAuthLogonPermit);
  148. await this.Handle_CMSG_AuthLogonPermit_Response();
  149. // 这个消息已经没有作用,只用来保持原有的代码流程
  150. var cmsgAuthLogonChallenge = new CMSG_Auth_Logon_Challenge();
  151. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_CHALLENGE, cmsgAuthLogonChallenge);
  152. var smsgAuthLogonChallengeResponse =
  153. await this.Handle_SMSG_Auth_Logon_Challenge_Response();
  154. // 以下是SRP6处理过程
  155. var random = new SecureRandom();
  156. var srp6Client = new Srp6Client();
  157. var n = new BigInteger(1, smsgAuthLogonChallengeResponse.N);
  158. var g = new BigInteger(1, smsgAuthLogonChallengeResponse.G);
  159. var s = new BigInteger(1, smsgAuthLogonChallengeResponse.S);
  160. var b = new BigInteger(1, smsgAuthLogonChallengeResponse.B);
  161. srp6Client.Init(n, g, new Sha1Digest(), random);
  162. BigInteger a = srp6Client.GenerateClientCredentials(
  163. s.ToByteArray(), account.ToByteArray(), password.ToByteArray());
  164. BigInteger clientS = srp6Client.CalculateSecret(b);
  165. // 计算M1
  166. var sha1Digest = new Sha1Digest();
  167. var kBytes = new byte[sha1Digest.GetDigestSize()];
  168. var clientSBytes = clientS.ToByteArray();
  169. sha1Digest.BlockUpdate(clientSBytes, 0, clientSBytes.Length);
  170. sha1Digest.DoFinal(kBytes, 0);
  171. sha1Digest.Reset();
  172. var m1 = new byte[sha1Digest.GetDigestSize()];
  173. var aBytes = a.ToByteArray();
  174. var bBytes = b.ToByteArray();
  175. sha1Digest.BlockUpdate(aBytes, 0, aBytes.Length);
  176. sha1Digest.BlockUpdate(bBytes, 0, bBytes.Length);
  177. sha1Digest.BlockUpdate(kBytes, 0, kBytes.Length);
  178. sha1Digest.DoFinal(m1, 0);
  179. var cmsgAuthLogonProof = new CMSG_Auth_Logon_Proof
  180. {
  181. A = a.ToByteArray(),
  182. M1 = clientS.ToByteArray()
  183. };
  184. this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PROOF, cmsgAuthLogonProof);
  185. }
  186. }
  187. }