add pubkey auth

This commit is contained in:
Sen 2025-05-29 03:04:44 +02:00
parent bdf67a89f7
commit 5a394749bf
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
24 changed files with 822 additions and 107 deletions

View file

@ -121,9 +121,9 @@ public final class Server implements IThreadListener {
private final List<Dimension> unload = Lists.<Dimension>newArrayList();
private final Map<String, Position> warps = Maps.<String, Position>newTreeMap();
private final CommandEnvironment scriptEnv = new CommandEnvironment(this);
private final KeyPair keyPair;
private final boolean debug;
private KeyPair keyPair;
private WorldServer space;
private ChannelFuture endpoint;
@ -169,7 +169,7 @@ public final class Server implements IThreadListener {
Log.flushLog();
}
public static void saveServerConfig(long time) {
public static void saveServerConfig(long time, Server server) {
TagObject data = new TagObject();
data.setLong("Time", time);
data.setLong("LastAccess", System.currentTimeMillis());
@ -182,6 +182,10 @@ public final class Server implements IThreadListener {
}
data.setObject("Config", cfg);
data.setObject("Universe", UniverseRegistry.toTags());
if(server != null) {
data.setByteArray("PrivateKey", server.getPrivateKey().getEncoded());
data.setByteArray("PublicKey", server.getPublicKey().getEncoded());
}
File nfile = new File("server.cdt.tmp");
File lfile = new File("server.cdt");
try {
@ -195,7 +199,7 @@ public final class Server implements IThreadListener {
}
}
public static long loadServerConfig() {
public long loadServerConfig() {
Config.clear();
UniverseRegistry.clear();
File file = new File("server.cdt");
@ -216,6 +220,12 @@ public final class Server implements IThreadListener {
Log.IO.info("Version: %s", version);
Log.IO.info("Weltzeit: %d Ticks / %d Sekunden", time, time / 20L);
Log.IO.info("Zuletzt geladen: %s", new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date(lastPlayed)));
if(tag.hasByteArray("PrivateKey") && tag.hasByteArray("PublicKey")) {
PrivateKey key = EncryptUtil.decodePrivateKey(tag.getByteArray("PrivateKey"));
PublicKey pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("PublicKey"));
if(key != null && pubkey != null)
this.keyPair = new KeyPair(pubkey, key);
}
return time;
}
catch(Exception e) {
@ -258,7 +268,6 @@ public final class Server implements IThreadListener {
}
}
}, "password");
this.keyPair = EncryptUtil.generateKeyPair();
}
public CommandEnvironment getScriptEnvironment() {
@ -283,7 +292,7 @@ public final class Server implements IThreadListener {
public void saveWorldInfo() {
if(!this.debug) {
saveServerConfig(this.space.getDayTime());
saveServerConfig(this.space.getDayTime(), this);
WorldServer.saveWarps(this.warps);
}
}
@ -452,7 +461,11 @@ public final class Server implements IThreadListener {
public void run(long time) {
if(!this.debug) {
Converter.convert();
long wtime = loadServerConfig();
long wtime = this.loadServerConfig();
if(this.keyPair == null) {
Log.SYSTEM.info("Generiere neues Schlüsselpaar");
this.keyPair = EncryptUtil.generateKeyPair();
}
// if(dtime == -1L) // {
// dtime = World.START_TIME;
//// Config.set("spawnDim", "1", null);
@ -485,6 +498,8 @@ public final class Server implements IThreadListener {
// }
}
else {
Log.SYSTEM.info("Generiere temporäres Schlüsselpaar");
this.keyPair = EncryptUtil.generateKeyPair();
Config.clear();
UniverseRegistry.clear();
Config.set("daylightCycle", "false", false);
@ -831,29 +846,41 @@ public final class Server implements IThreadListener {
radius > 0 ? 0.0f : Config.spawnPitch, world.dimension.getDimensionId());
}
public String addPlayer(NetConnection connection, String loginUser, String loginPass) {
public String addPlayer(NetConnection connection, String loginUser, String loginPass, PublicKey loginKey) {
TagObject tag = this.readPlayer(loginUser);
Player conn = new Player(this, connection, loginUser);
if(tag != null)
conn.readTags(tag);
if(Config.authenticate) {
if(conn.getPassword() == null && conn.getPubkey() == null) {
if(tag != null)
return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort";
if(!Config.register)
return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)";
if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit)
return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit);
if(loginKey != null) {
conn.setPubkey(loginKey);
Log.NETWORK.info(loginUser + " registrierte sich mit Pubkey");
}
else {
if(loginPass == null || loginPass.length() == 0)
return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)";
if(loginPass.length() < Config.minPassLength)
return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen";
conn.setPassword(loginPass);
Log.NETWORK.info(loginUser + " registrierte sich mit Passwort");
}
}
else if(conn.getPubkey() != null ? !conn.getPubkey().equals(loginKey) : !conn.getPassword().equals(loginPass)) {
return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort";
}
else {
Log.NETWORK.info(loginUser + " loggte sich mit " + (loginKey != null ? "Pubkey" : "Passwort") + " ein");
}
}
if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.isAdmin())
return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit);
if(conn.getPassword() == null) {
if(!Config.register)
return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)";
if(loginPass.length() == 0)
return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)";
if(loginPass.length() < Config.minPassLength)
return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen";
conn.setPassword(loginPass);
Log.NETWORK.info(loginUser + " registrierte sich mit Passwort");
}
else if(!conn.getPassword().equals(loginPass)) {
return "Falsches Passwort";
}
else {
Log.NETWORK.info(loginUser + " loggte sich mit Passwort ein");
}
if(Config.compression >= 0) {
connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {

View file

@ -1,6 +1,7 @@
package server.network;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Arrays;
@ -9,20 +10,28 @@ import javax.crypto.SecretKey;
import common.color.TextColor;
import common.init.Config;
import common.log.Log;
import common.net.util.concurrent.Future;
import common.net.util.concurrent.GenericFutureListener;
import common.network.ILoginHandler;
import common.network.IPlayer;
import common.network.NetConnection;
import common.network.NetHandler;
import common.packet.LPacketPasswordResponse;
import common.packet.LPacketChallenge;
import common.packet.LPacketPassword;
import common.packet.LPacketPubkey;
import common.packet.LPacketResponse;
import common.packet.LPacketStartEncrypt;
import common.packet.RPacketChallenge;
import common.packet.RPacketDisconnect;
import common.packet.RPacketRequestEncrypt;
import common.packet.RPacketResponse;
import common.packet.RPacketServerConfig;
import server.Server;
public class LoginHandler extends NetHandler implements ILoginHandler
{
private static enum LoginState {
INIT, ENCRYPT, PASSWORD, AUTHENTICATED, ACCEPTED;
INIT, ENCRYPT, PROOF, PASSWORD, CHALLENGE, AUTHENTICATED, ACCEPTED;
}
private static final SecureRandom TOKEN_RNG = new SecureRandom();
@ -35,6 +44,7 @@ public class LoginHandler extends NetHandler implements ILoginHandler
private String loginUser;
private String loginPass;
private byte[] loginToken;
private PublicKey loginKey;
public LoginHandler(Server server, NetConnection netManager)
{
@ -97,7 +107,7 @@ public class LoginHandler extends NetHandler implements ILoginHandler
//// player.netHandler.kick("Du hast dich von einen anderen Ort verbunden");
// }
// this.networkManager.sendPacket(new RPacketLoginSuccess());
String kick = this.server.addPlayer(this.netManager, this.loginUser, this.loginPass);
String kick = this.server.addPlayer(this.netManager, this.loginUser, this.loginPass, this.loginKey);
if(kick != null)
this.closeConnection(kick);
else
@ -107,10 +117,16 @@ public class LoginHandler extends NetHandler implements ILoginHandler
public void sendLoginPacket() {
if(this.state != LoginState.INIT)
throw new IllegalStateException("Unerwartetes Handshake-Paket");
this.state = LoginState.ENCRYPT;
this.loginToken = new byte[4];
TOKEN_RNG.nextBytes(this.loginToken);
this.netManager.sendPacket(new RPacketRequestEncrypt(this.server.getPublicKey(), this.loginToken));
if(Config.encrypt) {
this.state = LoginState.ENCRYPT;
this.loginToken = new byte[4];
TOKEN_RNG.nextBytes(this.loginToken);
this.netManager.sendPacket(new RPacketRequestEncrypt(this.server.getPublicKey(), this.loginToken));
}
else {
this.state = LoginState.PASSWORD;
this.netManager.sendPacket(new RPacketServerConfig(Config.accessRequired, Config.authenticate, Config.authenticate && Config.passwordAuth, false));
}
}
public void processEncryption(LPacketStartEncrypt packet) {
@ -121,26 +137,81 @@ public class LoginHandler extends NetHandler implements ILoginHandler
throw new IllegalStateException("Fehlerhaftes Token");
SecretKey key = packet.getKey(pkey);
this.netManager.startEncryption(key);
this.state = LoginState.PROOF;
}
public void processChallenge(LPacketChallenge packet) {
if(this.state != LoginState.PROOF)
throw new IllegalStateException("Unerwartetes Anforderungs-Paket");
this.state = LoginState.PASSWORD;
this.netManager.sendPacket(new RPacketResponse(packet.getToken(this.server.getPrivateKey())), new GenericFutureListener < Future <? super Void >> () {
public void operationComplete(Future <? super Void > u) throws Exception {
LoginHandler.this.netManager.sendPacket(new RPacketServerConfig(Config.accessRequired, Config.authenticate, Config.authenticate && Config.passwordAuth,
Config.authenticate && Config.pubkeyAuth));
}
});
}
private boolean checkAccess(String access) {
if(Config.accessRequired) {
if(Config.password.length() < 8) {
this.closeConnection("Es ist kein Zugangspasswort für diesen Server konfiguriert");
return false;
}
if(!Config.password.equals(access)) {
this.closeConnection("Falsches Zugangspasswort");
return false;
}
}
return true;
}
public void processPasswordResponse(LPacketPasswordResponse packetIn) {
public void processPassword(LPacketPassword packet) {
if(this.state != LoginState.PASSWORD)
throw new IllegalStateException("Unerwartetes Passwort-Paket");
this.loginUser = packetIn.getUser();
this.loginPass = packetIn.getPassword();
this.loginUser = packet.getUser();
if(this.loginUser.isEmpty() || !IPlayer.isValidUser(this.loginUser))
throw new IllegalStateException("Ungültiger Nutzername!");
// if(!this.checkConnect(packetIn.getAccess()))
// return;
if(Config.password.length() < 8) {
this.closeConnection("Es ist kein Zugangspasswort für diesen Server konfiguriert");
return;
if(!Config.passwordAuth && Config.authenticate) {
this.closeConnection("Dieser Server " + (Config.pubkeyAuth && Config.encrypt ? "benötigt einen öffentlichen Schlüssel zur Authentifizierung" : "hat keine Authentifizierungsmethode konfiguriert"));
return;
}
if(!Config.password.equals(packetIn.getAccess())) {
this.closeConnection("Falsches Zugangspasswort");
return;
if(!this.checkAccess(packet.getAccess()))
return;
if(Config.authenticate)
this.loginPass = packet.getPassword();
this.state = LoginState.AUTHENTICATED;
}
public void processPubkey(LPacketPubkey packet) {
if(this.state != LoginState.PASSWORD)
throw new IllegalStateException("Unerwartetes Pubkey-Paket");
this.loginUser = packet.getUser();
if(this.loginUser.isEmpty() || !IPlayer.isValidUser(this.loginUser))
throw new IllegalStateException("Ungültiger Nutzername!");
if((!Config.pubkeyAuth || !Config.encrypt) && Config.authenticate) {
this.closeConnection("Dieser Server " + (Config.passwordAuth ? "benötigt ein Passwort zur Authentifizierung" : "hat keine Authentifizierungsmethode konfiguriert"));
return;
}
if(!this.checkAccess(packet.getAccess()))
return;
if(Config.authenticate) {
this.loginKey = packet.getKey();
this.loginToken = new byte[32];
TOKEN_RNG.nextBytes(this.loginToken);
this.netManager.sendPacket(new RPacketChallenge(this.loginKey, this.loginToken));
this.state = LoginState.CHALLENGE;
}
else {
this.state = LoginState.AUTHENTICATED;
}
}
public void processResponse(LPacketResponse packet) {
if(this.state != LoginState.CHALLENGE)
throw new IllegalStateException("Unerwartetes Beweis-Paket");
if(!Arrays.equals(this.loginToken, packet.getToken()))
throw new IllegalStateException("Fehlerhaftes Beweis-Token");
this.state = LoginState.AUTHENTICATED;
}
}

View file

@ -1,5 +1,6 @@
package server.network;
import java.security.PublicKey;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@ -110,6 +111,7 @@ import common.tileentity.TileEntitySign;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.ChunkPos;
import common.util.EncryptUtil;
import common.util.ExtMath;
import common.util.Facing;
import common.util.IntHashMap;
@ -171,6 +173,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
private int ping;
private boolean deleted;
private String password;
private PublicKey pubkey;
private boolean profiling;
private int selectionDim = Integer.MIN_VALUE;
@ -347,6 +350,14 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
public String getPassword() {
return this.password;
}
public void setPubkey(PublicKey key) {
this.pubkey = key;
}
public PublicKey getPubkey() {
return this.pubkey;
}
@ -619,6 +630,8 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
this.admin = tag.getBool("admin");
if(tag.hasString("password"))
this.password = tag.getString("password");
if(tag.hasByteArray("pubkey"))
this.pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("pubkey"));
this.selected = tag.getInt("selected");
List<TagObject> list = tag.getList("characters");
for(int z = 0; z < list.size(); z++) {
@ -633,6 +646,8 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
tag.setBool("admin", this.admin);
if(this.password != null)
tag.setString("password", this.password);
if(this.pubkey != null)
tag.setByteArray("pubkey", this.pubkey.getEncoded());
if(!this.characters.isEmpty()) {
tag.setInt("selected", this.selected);
List<TagObject> list = Lists.newArrayList();

View file

@ -1308,7 +1308,7 @@ public abstract class Converter {
Config.set(rule.getValue(), rules.getString(rule.getKey()), false);
}
Log.IO.info("Speichere neue server.cdt ...");
Server.saveServerConfig(World.START_TIME);
Server.saveServerConfig(World.START_TIME, null);
Weather weather = tag.getByte("thundering") != 0 ? Weather.THUNDER : (tag.getByte("raining") != 0 ? Weather.RAIN : Weather.CLEAR);
if(weather != Weather.CLEAR) {
TagObject dataTag = new TagObject();