using System; using System.Collections.Generic; using System.Net.Sockets; using System.Numerics; using System.Security.Cryptography; using System.Threading.Tasks; using Helper; using Log; namespace LoginClient { public class RealmSession: IDisposable { public int ID { get; set; } public IMessageChannel MessageChannel { get; set; } public RealmSession(int id, IMessageChannel messageChannel) { this.ID = id; this.MessageChannel = messageChannel; } public void Dispose() { this.MessageChannel.Dispose(); } public async Task Handle_CMSG_AuthLogonPermit_Response() { var result = await this.MessageChannel.RecvMessage(); ushort opcode = result.Item1; byte[] message = result.Item2; if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE) { throw new LoginException(string.Format( "session: {0}, opcode: {1}", this.ID, opcode)); } var smsgPasswordProtectType = ProtobufHelper.FromBytes(message); if (smsgPasswordProtectType.Code != 200) { throw new LoginException(string.Format( "session: {0}, SMSG_Lock_For_Safe_Time: {1}", this.ID, JsonHelper.ToString(smsgPasswordProtectType))); } return smsgPasswordProtectType; } public async Task Handle_SMSG_Auth_Logon_Challenge_Response() { var result = await this.MessageChannel.RecvMessage(); ushort opcode = result.Item1; byte[] message = result.Item2; if (opcode != MessageOpcode.SMSG_AUTH_LOGON_CHALLENGE_RESPONSE) { Logger.Trace("opcode: {0}", opcode); throw new LoginException(string.Format( "session: {0}, opcode: {1}", this.ID, opcode)); } var smsgAuthLogonChallengeResponse = ProtobufHelper.FromBytes(message); if (smsgAuthLogonChallengeResponse.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS) { Logger.Trace("error code: {0}", smsgAuthLogonChallengeResponse.ErrorCode); throw new LoginException( string.Format("session: {0}, SMSG_Auth_Logon_Challenge_Response: {1}", this.ID, JsonHelper.ToString(smsgAuthLogonChallengeResponse))); } Logger.Debug("SMSG_Auth_Logon_Challenge_Response: \n{0}", JsonHelper.ToString(smsgAuthLogonChallengeResponse)); return smsgAuthLogonChallengeResponse; } public async Task Handle_SMSG_Auth_Logon_Proof_M2() { var result = await this.MessageChannel.RecvMessage(); ushort opcode = result.Item1; byte[] message = result.Item2; if (opcode != MessageOpcode.SMSG_AUTH_LOGON_PROOF_M2) { throw new LoginException(string.Format( "session: {0}, error opcode: {1}", this.ID, opcode)); } var smsgAuthLogonProofM2 = ProtobufHelper.FromBytes(message); if (smsgAuthLogonProofM2.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS) { throw new LoginException(string.Format( "session: {0}, SMSG_Auth_Logon_Proof_M2: {1}", this.ID, JsonHelper.ToString(smsgAuthLogonProofM2))); } return smsgAuthLogonProofM2; } public async Task Handle_SMSG_Realm_List() { var result = await this.MessageChannel.RecvMessage(); ushort opcode = result.Item1; byte[] message = result.Item2; if (opcode != MessageOpcode.SMSG_REALM_LIST) { throw new LoginException(string.Format( "session: {0}, error opcode: {1}", this.ID, opcode)); } var smsgRealmList = ProtobufHelper.FromBytes(message); return smsgRealmList; } public async Task> Login(string account, string password) { byte[] passwordBytes = password.ToByteArray(); MD5 md5 = MD5.Create(); byte[] passwordMd5 = md5.ComputeHash(passwordBytes); byte[] passwordMd5Hex = passwordMd5.ToHex().ToLower().ToByteArray(); // 发送帐号和密码MD5 var cmsgAuthLogonPermit = new CMSG_Auth_Logon_Permit { Account = account.ToByteArray(), PasswordMd5 = passwordMd5Hex }; Logger.Trace("session: {0}, account: {1}, password: {2}", this.ID, cmsgAuthLogonPermit.Account.ToStr(), cmsgAuthLogonPermit.PasswordMd5.ToHex()); this.MessageChannel.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PERMIT, cmsgAuthLogonPermit); await this.Handle_CMSG_AuthLogonPermit_Response(); // 这个消息已经没有作用,只用来保持原有的代码流程 var cmsgAuthLogonChallenge = new CMSG_Auth_Logon_Challenge(); this.MessageChannel.SendMessage( MessageOpcode.CMSG_AUTH_LOGON_CHALLENGE, cmsgAuthLogonChallenge); var smsgAuthLogonChallengeResponse = await this.Handle_SMSG_Auth_Logon_Challenge_Response(); Logger.Trace("session: {0}, SMSG_Auth_Logon_Challenge_Response OK", this.ID); // 以下是SRP6处理过程 var n = smsgAuthLogonChallengeResponse.N.ToUBigInteger(); var g = smsgAuthLogonChallengeResponse.G.ToUBigInteger(); var b = smsgAuthLogonChallengeResponse.B.ToUBigInteger(); var salt = smsgAuthLogonChallengeResponse.S.ToUBigInteger(); var srp6Client = new SRP6Client( new SHA1Managed(), n, g, b, salt, account.ToByteArray(), passwordMd5Hex); Logger.Debug("s: {0}\nN: {1}\nG: {2}\nB: {3}\nA: {4}\nS: {5}\nK: {6}\nm: {7}\na: {8}", srp6Client.Salt.ToUBigIntegerArray().ToHex(), srp6Client.N.ToUBigIntegerArray().ToHex(), srp6Client.G.ToUBigIntegerArray().ToHex(), srp6Client.B.ToUBigIntegerArray().ToHex(), srp6Client.A.ToUBigIntegerArray().ToHex(), srp6Client.S.ToUBigIntegerArray().ToHex(), srp6Client.K.ToUBigIntegerArray().ToHex(), srp6Client.M.ToUBigIntegerArray().ToHex(), srp6Client.SmallA.ToUBigIntegerArray().ToHex()); var cmsgAuthLogonProof = new CMSG_Auth_Logon_Proof { A = srp6Client.A.ToUBigIntegerArray(), M = srp6Client.M.ToUBigIntegerArray() }; this.MessageChannel.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PROOF, cmsgAuthLogonProof); await this.Handle_SMSG_Auth_Logon_Proof_M2(); Logger.Trace("session: {0}, SMSG_Auth_Logon_Proof_M2 OK", this.ID); // 请求realm list var cmsgRealmList = new CMSG_Realm_List(); this.MessageChannel.SendMessage(MessageOpcode.CMSG_REALM_LIST, cmsgRealmList); var smsgRealmList = await this.Handle_SMSG_Realm_List(); Logger.Trace("session: {0}, SMSG_Realm_List OK", this.ID); string address = smsgRealmList.GateList[0].Address.ToStr(); string[] split = address.Split(new[] { ':' }); if (split.Length != 2) { throw new LoginException( string.Format("session: {0}, gate address error, address: {1}", this.ID, address)); } string gateIP = split[0]; ushort gatePort = UInt16.Parse(split[1]); return Tuple.Create(gateIP, gatePort, srp6Client); } } }