add password hashing and commands

This commit is contained in:
Sen 2025-05-30 21:12:59 +02:00
parent 1b61f085e3
commit ec0a1aa5c3
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
9 changed files with 148 additions and 19 deletions

View file

@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
@ -854,7 +855,7 @@ public final class Server implements IThreadListener {
if(tag != null)
conn.readTags(tag);
if(Config.authenticate) {
if(conn.getPassword() == null && conn.getPubkey() == null) {
if(conn.getPasswordHash() == null && conn.getPubkey() == null) {
if(tag != null)
return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort";
if(!Config.register)
@ -870,11 +871,12 @@ public final class Server implements IThreadListener {
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);
conn.setPasswordHash(EncryptUtil.hashPassword(loginPass));
Log.NETWORK.info(loginUser + " registrierte sich mit Passwort");
}
}
else if(conn.getPubkey() != null ? !conn.getPubkey().equals(loginKey) : !conn.getPassword().equals(loginPass)) {
else if(conn.getPubkey() != null ? !conn.getPubkey().equals(loginKey) :
(loginPass == null || !MessageDigest.isEqual(EncryptUtil.hashPassword(loginPass, conn.getPasswordHash().second()), conn.getPasswordHash().first()))) {
return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort";
}
else {

View file

@ -25,6 +25,7 @@ import server.command.commands.CommandOfflinetp;
import server.command.commands.CommandPasswd;
import server.command.commands.CommandPlayers;
import server.command.commands.CommandPotion;
import server.command.commands.CommandPubkey;
import server.command.commands.CommandRegister;
import server.command.commands.CommandRemove;
import server.command.commands.CommandRevoke;
@ -280,6 +281,7 @@ public class CommandEnvironment {
this.registerExecutable(new CommandMessage());
this.registerExecutable(new CommandShutdown());
this.registerExecutable(new CommandPasswd());
this.registerExecutable(new CommandPubkey());
this.registerExecutable(new CommandPlayers());
this.registerExecutable(new CommandSave());
this.registerExecutable(new CommandRegister());

View file

@ -1,8 +1,11 @@
package server.command.commands;
import java.security.MessageDigest;
import common.color.TextColor;
import common.init.Config;
import common.network.IPlayer;
import common.util.EncryptUtil;
import server.command.Command;
import server.command.CommandEnvironment;
import server.command.Executor;
@ -25,6 +28,10 @@ public class CommandPasswd extends Command {
throw new RunException("Bei Verwendung als Spieler darf kein Passwort angegeben werden");
if(player.getAdmin() && player != exec)
throw new RunException("%s ist ein Admin", player.getUser());
if(player.getPubkey() != null && player == exec)
throw new RunException("Es darf kein Pubkey vorhanden sein, um diesen Befehl an sich selbst anzuwenden");
if(player.getPasswordHash() == null && player == exec)
throw new RunException("Es muss ein Passwort vorhanden sein, um diesen Befehl an sich selbst anzuwenden");
((Player)exec).displayForm(new Form() {
private Field checkField;
private Field passwordField;
@ -42,11 +49,11 @@ public class CommandPasswd extends Command {
protected void accept() {
Player plr = env.getServer().getPlayer(player.getUser());
if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec)) {
if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec) || (plr.getPasswordHash() == null && plr == exec) || (plr.getPubkey() != null && plr == exec)) {
exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten");
return;
}
if(this.checkField != null && !this.checkField.get().equals(plr.getPassword())) {
if(this.checkField != null && !MessageDigest.isEqual(EncryptUtil.hashPassword(this.checkField.get(), plr.getPasswordHash().second()), plr.getPasswordHash().first())) {
exec.logConsole(TextColor.RED + "Falsches Passwort eingegeben");
return;
}
@ -54,7 +61,8 @@ public class CommandPasswd extends Command {
exec.logConsole(TextColor.RED + "Passwörter stimmen nicht überein");
return;
}
plr.setPassword(this.passwordField.get());
plr.setPasswordHash(EncryptUtil.hashPassword(this.passwordField.get()));
plr.setPubkey(null);
exec.logConsole(TextColor.GREEN + "Passwort" + (plr != exec ? " für %s" : "") + " gesetzt", plr.getUser());
}
});
@ -62,7 +70,10 @@ public class CommandPasswd extends Command {
else if(exec.isConsole()) {
if(password == null)
throw new RunException("Bei Verwendung in der Konsole muss ein Passwort angegeben werden");
player.setPassword(password);
if(password.length() < 8)
throw new RunException("Das Passwort ist zu kurz, mindestens 8 Zeichen sind erforderlich");
player.setPasswordHash(EncryptUtil.hashPassword(password));
player.setPubkey(null);
exec.logConsole(TextColor.GREEN + "Passwort für %s gesetzt", player.getUser());
}
}

View file

@ -0,0 +1,87 @@
package server.command.commands;
import java.security.MessageDigest;
import java.security.PublicKey;
import common.color.TextColor;
import common.network.IPlayer;
import common.util.EncryptUtil;
import common.util.Pair;
import server.command.Command;
import server.command.CommandEnvironment;
import server.command.Executor;
import server.command.RunException;
import server.network.Player;
import server.util.Form;
public class CommandPubkey extends Command {
public CommandPubkey() {
super("pubkey");
this.addPlayer("player", true);
this.setParamsOptional();
this.addString("keySpec", false);
this.addString("keyData", false);
this.addString("keyHash", false);
this.addString("keyName", false);
}
public void exec(CommandEnvironment env, Executor exec, Player player, String keySpec, String keyData, String keyHash, String keyName) {
if(exec.isPlayer()) {
if(keySpec != null || keyData != null || keyHash != null || keyName != null)
throw new RunException("Bei Verwendung als Spieler darf kein Schlüssel angegeben werden");
if(player.getAdmin() && player != exec)
throw new RunException("%s ist ein Admin", player.getUser());
((Player)exec).displayForm(new Form() {
private Field checkField;
private Field keyField;
protected void init() {
this.checkField = player.getPasswordHash() == null || player != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, "");
this.keyField = this.addField("Neuer Pubkey", 1, 960, "");
}
public String getTitle() {
return "Schlüssel für " + player.getUser() + " ändern";
}
protected void accept() {
Player plr = env.getServer().getPlayer(player.getUser());
if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec)) {
exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten");
return;
}
if(this.checkField != null && plr.getPasswordHash() != null && !MessageDigest.isEqual(EncryptUtil.hashPassword(this.checkField.get(), plr.getPasswordHash().second()), plr.getPasswordHash().first())) {
exec.logConsole(TextColor.RED + "Falsches Passwort eingegeben");
return;
}
Pair<PublicKey, String> key;
try {
key = EncryptUtil.parseArmoredPubkey(this.keyField.get());
}
catch(IllegalArgumentException e) {
exec.logConsole(TextColor.RED + "Ungültiger Schlüssel");
return;
}
plr.setPasswordHash(null);
plr.setPubkey(key.first());
exec.logConsole(TextColor.GREEN + "Schlüssel" + (plr != exec ? " für %s" : "") + " gesetzt", plr.getUser());
}
});
}
else if(exec.isConsole()) {
if(keySpec == null || keyData == null || keyHash == null)
throw new RunException("Bei Verwendung in der Konsole muss ein Schlüssel angegeben werden");
Pair<PublicKey, String> key;
try {
key = EncryptUtil.parseArmoredPubkey(keySpec + " " + keyData + " " + keyHash + (keyName == null ? null : " " + keyName));
}
catch(IllegalArgumentException e) {
throw new RunException(e, "Ungültiger Schlüssel");
}
player.setPasswordHash(null);
player.setPubkey(key.first());
exec.logConsole(TextColor.GREEN + "Schlüssel für %s gesetzt", player.getUser());
}
}
}

View file

@ -115,6 +115,7 @@ import common.util.EncryptUtil;
import common.util.ExtMath;
import common.util.Facing;
import common.util.IntHashMap;
import common.util.Pair;
import common.util.PortalType;
import common.util.Position;
import common.util.Vec3i;
@ -172,7 +173,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
private boolean admin;
private int ping;
private boolean deleted;
private String password;
private Pair<byte[], byte[]> password;
private PublicKey pubkey;
private boolean profiling;
@ -343,11 +344,11 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
return this.isAdmin();
}
public void setPassword(String pass) {
this.password = pass;
public void setPasswordHash(Pair<byte[], byte[]> hash) {
this.password = hash;
}
public String getPassword() {
public Pair<byte[], byte[]> getPasswordHash() {
return this.password;
}
@ -628,8 +629,8 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
public void readTags(TagObject tag) {
this.admin = tag.getBool("admin");
if(tag.hasString("password"))
this.password = tag.getString("password");
if(tag.hasByteArray("passwordHash") && tag.hasByteArray("passwordSalt"))
this.password = new Pair<byte[], byte[]>(tag.getByteArray("passwordHash"), tag.getByteArray("passwordSalt"));
if(tag.hasByteArray("pubkey"))
this.pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("pubkey"));
this.selected = tag.getInt("selected");
@ -644,8 +645,10 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer
public void writeTags(TagObject tag) {
if(this.admin)
tag.setBool("admin", this.admin);
if(this.password != null)
tag.setString("password", this.password);
if(this.password != null) {
tag.setByteArray("passwordHash", this.password.first());
tag.setByteArray("passwordSalt", this.password.second());
}
if(this.pubkey != null)
tag.setByteArray("pubkey", this.pubkey.getEncoded());
if(!this.characters.isEmpty()) {