add password hashing and commands
This commit is contained in:
parent
1b61f085e3
commit
ec0a1aa5c3
9 changed files with 148 additions and 19 deletions
|
@ -67,8 +67,8 @@ public class GuiForm extends Gui implements ButtonCallback {
|
|||
GuiForm.this.labels[index].setText(name);
|
||||
}
|
||||
};
|
||||
this.inputs[z] = this.add((param & 0x80000000) != 0 ? new PasswordField(0, 50 * z, 300, 24, Math.min(param & 0xffff, 256), callback, (String)obj) :
|
||||
new Field(0, 50 * z, 300, 24, Math.min(param & 0xffff, 256), callback, (String)obj));
|
||||
this.inputs[z] = this.add((param & 0x80000000) != 0 ? new PasswordField(0, 50 * z, 300, 24, Math.min(param & 0xffff, 1024), callback, (String)obj) :
|
||||
new Field(0, 50 * z, 300, 24, Math.min(param & 0xffff, 1024), callback, (String)obj));
|
||||
}
|
||||
}
|
||||
this.add(new NavButton(0, 50 * (this.inputs.length + 1), 148, 24, null, "Abbrechen"));
|
||||
|
|
|
@ -39,7 +39,7 @@ public class CPacketForm implements Packet<IPlayer>
|
|||
obj = buf.readVarInt();
|
||||
break;
|
||||
default:
|
||||
obj = buf.readString(256);
|
||||
obj = buf.readString(1024);
|
||||
break;
|
||||
}
|
||||
this.data[z] = obj;
|
||||
|
|
|
@ -47,7 +47,7 @@ public class RPacketServerConfig implements Packet<IClientLoginHandler> {
|
|||
}
|
||||
|
||||
public boolean isAuthenticating() {
|
||||
return this.passwordAuth;
|
||||
return this.requiresAuth;
|
||||
}
|
||||
|
||||
public boolean canUsePassword() {
|
||||
|
|
|
@ -10,9 +10,11 @@ import java.security.MessageDigest;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.EncodedKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
@ -24,7 +26,9 @@ import javax.crypto.IllegalBlockSizeException;
|
|||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import common.log.Log;
|
||||
|
@ -189,6 +193,7 @@ public class EncryptUtil {
|
|||
|
||||
private static final long CRC24_INIT = 0xB704CEL;
|
||||
private static final long CRC24_POLY = 0x1864CFBL;
|
||||
private static final byte[] CLIENT_SALT = "ItIsSaltySalt2765666 tftffs ##89n!.le98udMeowHAsh44m;+*3m9DI9Sqn".getBytes();
|
||||
|
||||
private static byte[] crc24(byte[] data) {
|
||||
long crc = CRC24_INIT;
|
||||
|
@ -203,4 +208,23 @@ public class EncryptUtil {
|
|||
int value = (int)(crc & 0xFFFFFFL);
|
||||
return new byte[] {(byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), (byte)(value & 0xff)};
|
||||
}
|
||||
|
||||
public static byte[] hashPassword(String password, byte[] salt) {
|
||||
try {
|
||||
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
return factory.generateSecret(spec).getEncoded();
|
||||
}
|
||||
catch(NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
Log.SYSTEM.error(e, "Konnte Passwort-Prüfwert nicht errechnen");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Pair<byte[], byte[]> hashPassword(String password) {
|
||||
SecureRandom rand = new SecureRandom();
|
||||
byte[] salt = new byte[64];
|
||||
rand.nextBytes(salt);
|
||||
return new Pair<byte[], byte[]>(hashPassword(password, salt), salt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue