public static void EncryptionKeyRequest(MinecraftClient client, IPacket _packet) { var packet = (EncryptionKeyRequestPacket)_packet; var random = RandomNumberGenerator.Create(); client.SharedSecret = new byte[16]; random.GetBytes(client.SharedSecret); // Generate a secure AES key if (packet.ServerId != "-") // Online mode { // Authenticate with minecraft.net if (!client.Session.OnlineMode) throw new AuthenticationException("Server is in online mode, but client is in offline mode."); var data = Encoding.ASCII.GetBytes(packet.ServerId) .Concat(client.SharedSecret) .Concat(packet.PublicKey).ToArray(); var hash = Cryptography.JavaHexDigest(data); var webClient = new WebClient(); string result = webClient.DownloadString("http://session.minecraft.net/game/joinserver.jsp?user="******"&sessionId=" + Uri.EscapeUriString(client.Session.SessionId) + "&serverId=" + Uri.EscapeUriString(hash)); if (result != "OK") { // TODO } } var parser = new AsnKeyParser(packet.PublicKey); var key = parser.ParseRSAPublicKey(); // Encrypt shared secret and verification token var crypto = new RSACryptoServiceProvider(); crypto.ImportParameters(key); var encryptedSharedSecret = crypto.Encrypt(client.SharedSecret, false); var encryptedVerification = crypto.Encrypt(packet.VerificationToken, false); var response = new EncryptionKeyResponsePacket(encryptedSharedSecret, encryptedVerification); client.SendPacket(response); client.FlushPackets(); client.NetworkManager.BaseStream = new AesStream(client.NetworkStream, client.SharedSecret); }
private void InitializeEncryption(EncryptionKeyRequestPacket packet) { // We have to hijack the encryption here to be able to sniff the // connection. What we do is set up two unrelated crypto streams, // one for the server, one for the client. We actually act a bit // more like a real client or a real server in this particular // stage of the connection, because we generate a shared secret // as a client and a public key as a server, and liase with each // end of the connection without tipping them off to this. After // this is done, we wrap the connection in an AesStream and // everything works fine. // Interact with the server (acting as a client) // Generate our shared secret var secureRandom = RandomNumberGenerator.Create(); ServerSharedKey = new byte[16]; secureRandom.GetBytes(ServerSharedKey); // Parse the server public key var parser = new AsnKeyParser(packet.PublicKey); var key = parser.ParseRSAPublicKey(); // Encrypt shared secret and verification token var crypto = new RSACryptoServiceProvider(); crypto.ImportParameters(key); byte[] encryptedSharedSecret = crypto.Encrypt(ServerSharedKey, false); byte[] encryptedVerification = crypto.Encrypt(packet.VerificationToken, false); // Create an 0xFC response to give the server ServerEncryptionResponse = new EncryptionKeyResponsePacket { SharedSecret = encryptedSharedSecret, VerificationToken = encryptedVerification }; // Authenticate with minecraft.net if need be if (packet.ServerId != "-") { try { var session = Session.DoLogin(Settings.Username, Settings.Password); // Generate session hash byte[] hashData = Encoding.ASCII.GetBytes(packet.ServerId) .Concat(ServerSharedKey) .Concat(packet.PublicKey).ToArray(); var hash = Cryptography.JavaHexDigest(hashData); var webClient = new WebClient(); string result = webClient.DownloadString("http://session.minecraft.net/game/joinserver.jsp?user="******"&sessionId=" + Uri.EscapeUriString(session.SessionId) + "&serverId=" + Uri.EscapeUriString(hash)); if (result != "OK") Console.WriteLine("Warning: Unable to login as user " + Settings.Username + ": " + result); } catch (Exception e) { Console.WriteLine("Warning: Unable to login as user " + Settings.Username + ": " + e.Message); } } // Interact with the client (acting as a server) // Generate verification token ClientVerificationToken = new byte[4]; secureRandom.GetBytes(ClientVerificationToken); // Encode public key as an ASN X509 certificate var encodedKey = AsnKeyBuilder.PublicKeyToX509(ServerKey); if (Settings.AuthenticateClients) ClientAuthenticationHash = CreateHash(); else ClientAuthenticationHash = "-"; ClientEncryptionRequest = new EncryptionKeyRequestPacket { VerificationToken = ClientVerificationToken, ServerId = ClientAuthenticationHash, PublicKey = encodedKey.GetBytes() }; // Send the client our encryption details and await its response ClientEncryptionRequest.WritePacket(ClientStream); ClientStream.Flush(); }
private void FinializeServerEncryption(EncryptionKeyResponsePacket encryptionKeyResponsePacket) { // Here, we have all the details we need to initialize our // proxy<->server crypto stream. This happens *after* we have // already completed the crypto handshake with the client. // Wrap the server stream in a crypto stream ServerStream = new MinecraftStream(new BufferedStream(new AesStream(Server, ServerSharedKey))); Log.Write("Encrypted server connection established."); // Write the response. This is the first encrypted packet // sent to the client. The correct response is to send // an 0xFC EncryptionKeyResponse with both fields as empty // arrays. var response = new EncryptionKeyResponsePacket { SharedSecret = new byte[0], VerificationToken = new byte[0] }; response.WritePacket(ClientStream); ClientStream.Flush(); // Wrap the client stream in a crypto stream ClientStream = new MinecraftStream(new BufferedStream(new AesStream(Client, ClientSharedKey))); Log.Write("Encrypted client connection established."); // And now we're done with encryption and everything can // continue normally. }
private bool FinializeClientEncryption(EncryptionKeyResponsePacket encryptionKeyResponsePacket) { // Here, we need to prepare everything to enable client<->proxy // encryption, but we can't turn it on quite yet. // Decrypt shared secret ClientSharedKey = CryptoServiceProvider.Decrypt(encryptionKeyResponsePacket.SharedSecret, false); var verificationToken = CryptoServiceProvider.Decrypt(encryptionKeyResponsePacket.VerificationToken, false); // Check verification token for (int i = 0; i < verificationToken.Length; i++) { if (verificationToken[i] != ClientVerificationToken[i]) Log.Write("WARNING: Client verification token does not match!"); } if (Settings.AuthenticateClients) { // Do authentication // Create a hash for session verification AsnKeyBuilder.AsnMessage encodedKey = AsnKeyBuilder.PublicKeyToX509(ServerKey); byte[] shaData = Encoding.UTF8.GetBytes(ClientAuthenticationHash) .Concat(ClientSharedKey) .Concat(encodedKey.GetBytes()).ToArray(); string hash = Cryptography.JavaHexDigest(shaData); var client = new WebClient(); var result = client.DownloadString(string.Format("http://session.minecraft.net/game/checkserver.jsp?user={0}&serverId={1}", PlayerName, hash)); if (result != "YES") { Log.Write("Failed to authenticate " + PlayerName + "!"); new DisconnectPacket("Failed to authenticate!").WritePacket(ServerStream); new DisconnectPacket("Failed to authenticate!").WritePacket(ClientStream); ServerStream.Flush(); ClientStream.Flush(); return false; } } // Send unencrypted response ServerEncryptionResponse.WritePacket(ServerStream); ServerStream.Flush(); // We wait for the server to respond, then set up encryption // for both sides of the connection. return true; }
private bool FinializeClientEncryption(EncryptionKeyResponsePacket encryptionKeyResponsePacket) { // Here, we need to prepare everything to enable client<->proxy // encryption, but we can't turn it on quite yet. if (Settings.AuthenticateClients) { // Do authentication // Create a hash for session verification AsnKeyBuilder.AsnMessage encodedKey = AsnKeyBuilder.PublicKeyToX509(ServerKey); byte[] shaData = Encoding.UTF8.GetBytes(ClientAuthenticationHash) .Concat(ClientSharedKey) .Concat(encodedKey.GetBytes()).ToArray(); string hash = Cryptography.JavaHexDigest(shaData); var client = new WebClient(); var result = client.DownloadString(string.Format("http://session.minecraft.net/game/checkserver.jsp?user={0}&serverId={1}", PlayerName, hash)); if (result != "YES") { Log.Write("Failed to authenticate " + PlayerName + "!"); ServerStream.WritePacket(new DisconnectPacket("Failed to authenticate!"), Craft.Net.PacketDirection.Serverbound); ClientStream.WritePacket(new DisconnectPacket("Failed to authenticate!"), Craft.Net.PacketDirection.Clientbound); Server.Flush(); Client.Flush(); return false; } } // Send unencrypted response ServerStream.WritePacket(ServerEncryptionResponse, Craft.Net.PacketDirection.Serverbound); Server.Flush(); // We wait for the server to respond, then set up encryption // for both sides of the connection. ServerStream.BaseStream = new AesStream(Server, ServerSharedKey); return true; }