RealmSession.cs 7.0 KB

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