private static EncryptionKeyRequestPacket CreateEncryptionRequest(RemoteClient client, MinecraftServer server) { var verifyToken = new byte[4]; var csp = new RNGCryptoServiceProvider(); csp.GetBytes(verifyToken); client.VerificationToken = verifyToken; var encodedKey = AsnKeyBuilder.PublicKeyToX509(server.ServerKey); var request = new EncryptionKeyRequestPacket(client.ServerId, encodedKey.GetBytes(), verifyToken); return request; }
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(); }