gpt4 book ai didi

java - 使用JPasswordField并使用getPassword +服务器登录过程处理密码

转载 作者:行者123 更新时间:2023-11-30 07:14:21 26 4
gpt4 key购买 nike

经过一天的漫长编程,我想发布一些对其他人有用的东西。

几天前,我想知道如何使用正确的过程处理JPasswordField的方法getPassword(),以将值传递给服务器并获得答案。

这是一个问题:

如何安全地从JPasswordField正确获取值并处理该值以使用服务器创建登录过程?

最佳答案

这是我达成的解决方案。

首先,我确定了足以达到我的目的的安全登录过程,我不想向服务器发送普通密码,也不想(显然)在数据库中存储普通密码。

首先要说的是,保护密码的好方法是永远不要以简单易懂的形式在网络上交换密码,这显然是因为可能存在“中间人”,简而言之就是有人以他们通往服务器的方式阅读您的消息。

密码需要进行哈希处理,这意味着密码会转换为很长的十六进制字符序列。哈希的好处是(希望)是单向的。您不能散列密码。

有很多算法可以做到这一点,我选择SHA256。

然后将密码散列,但是仅在我们之间,这还不够。如果黑客能够窃取该散列,则可以采用某些技术使他成功地对其进行“翻译”。只是为了在他的方程式中添加一个变量,并使他的生活更艰难,我们可以在对哈希值进行哈希处理之前在密码中添加盐。盐是一串字符串,它被添加到我们想要的密码的任何位置。这样可以避免基于字典和最常用密码的某种攻击。

但是,如果比我受过更好培训的黑客无法读取密码,我该怎么办?

答案很简单,我不必这样做。

但是要了解这一点,我们需要在“注册过程”中稍作停留,即将新用户添加到我的数据库中的那一刻。就是这个:


客户端要求服务器发送昵称进行注册。
服务器用令牌(密码的盐)来回答。
客户端对密码加盐,对其进行哈希处理,然后将其发送回服务器。
现在,服务器收到了一些无法读取的内容,因此没有安全问题,并将其与昵称和盐一起存储。加盐的哈希密码是“公共密码”。


因此,登录过程将如下所示:


客户端要求服务器登录
服务器用盐回答
客户端对密码进行盐析,然后对其进行哈希处理,然后将其发送回服务器。
服务器将共享机密与接收到的字符串进行比较。如果它们相等,则允许用户登录。


这应该很好,但是在这种情况下,如果黑客知道共享密钥可以毫无问题地访问服务器,因为这样做,我们只是更改了密码,使密码不可读,但仍然可以直接使用。

为了避免这种行为,我们只需要在我们的链中添加一个段落:


客户端要求服务器登录
服务器用盐和随机会话盐回答
客户端对密码加盐,然后对其进行哈希处理。在这一点上,它再次使散列盐化并重新散列。然后,它将哈希过的哈希过的哈希过的密码发送回服务器
服务器获取共享机密,将其与随机会话盐混合,然后进行哈希处理。如果两个字符串相等,则允许用户登录。


既然过程很清楚,我们有一个要解决的问题。如果我处理任何类型的String,它都可以在内存中保留很长时间,因此,如果我将密码放入String中,则可以很长时间以纯格式读取。这对我们来说不是那么好,但是我们并不是第一个考虑它的人,java确实创建了一种避免该密码持续存在的方法。解决方案是使用字符数组。这是因为即使阵列保留在内存中,其数据也会在内存中无序分布,并且很难重新创建原始密码。

重新发明热水?是的,只需在JPasswordField中使用getPassword()方法。

但这对于新手来说是相当困难的。我们得到一个char []数组,对于一个不是专家的人来说很奇怪。

我们首先想到的是将数组转换为纯字符串.......
但是,这正是我要避免的。所以我需要按原样处理数组。

然后,我们需要一种对密码进行加盐和哈希处理的方法,结果可能是这样的:

public static String digestSalted(String salt, char[] password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");

ArrayList<Byte> list = new ArrayList<Byte>();
for (int i = 0; i < password.length; i++) {
//String ch = String.valueOf(password[i]);
//byte[] b = ch.getBytes();
//for (int j = 0; j < b.length; j++) {
// list.add(b[j]);
//}
list.add((byte)password[i]);
}
byte[] saltInBytes = salt.getBytes();
byte[] toBeHashed = new byte[(saltInBytes.length + list.size())];
for (int i = 0; i < saltInBytes.length; i++) {
toBeHashed[i] = saltInBytes[i];
}
for (int i = saltInBytes.length; i < list.size() + saltInBytes.length; i++) {
toBeHashed[i] = list.get(i - saltInBytes.length);
}

md.update(toBeHashed);

byte byteData[] = md.digest();

StringBuffer hexString = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
String hex = Integer.toHexString(0xff & byteData[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();

}


该方法创建一个字节数组,该数组通过许多小字符串,然后附加盐。一旦加盐,它就会用SHA256哈希结果。

现在,返回值可以是字符串,因为它是散列的,并且没有安全性问题。

这为问题的第一部分提供了解决方案。

第二部分只是实现服务器与客户端之间的协议。

我只会在客户端中显示足够重要的代码以了解该过程。
我正在使用阻塞队列,当从套接字读取消息时,将消息放入该队列。这是代码:

public void login(String nickname, char[] password) {
if (cl == null) {
throw new RuntimeException();
}
long s = Sys.getTime();
cl.send("NICK " + nickname);
IncomingMessage reply = null;
try {
reply = this.mh.getMessage(); //The response to NICK msg
if (reply.getCommand().equalsIgnoreCase("LOGIN")) {
ArrayList<String> params = reply.getParams();
String accountSalt = params.get(0);
String randomSalt = params.get(1);
try {
String sharedSecret = SHAHash.digestSalted(accountSalt, password);
String saltedSharedSecret = SHAHash.digestSalted(randomSalt, sharedSecret);
if (saltedSharedSecret != null) {
cl.send("PASS " + saltedSharedSecret);
reply = this.mh.getMessage();
if (reply.getCommand().equalsIgnoreCase("WELCOME") && reply.getParams().get(0).equals(nickname)) {
// ************ LOG ************ //
LOG.config("Logged in.");
// ***************************** //
this.running = true;
this.loggedIn = true;
mh.startExecutor();
LOG.config("Time passed: " + (Sys.getTime() - s));
mh.startGame();
} else {
// ************ LOG ************ //
LOG.warning("A problem has occured while trying to login to the server.");
// ***************************** //
JOptionPane.showMessageDialog(null, "Error while logging to the server, shutting down.\n- ERROR 006 -");
System.exit(0);
}
}
} catch (NoSuchAlgorithmException e) {
// ************ LOG ************ //
LOG.warning("Error while SHA hashing the password, shutting down.");
// ***************************** //
JOptionPane.showMessageDialog(null, "Error while SHA hashing the password, shutting down.\n- ERROR 005 -");
System.exit(0);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}


现在,我们已经清楚了协议的工作原理,代码很容易理解,应该考虑的是 this.mh.getMessage()是一种阻塞方法,这意味着线程将等到队列中的某些内容可用后再尝试得到它。

这(几乎)是我解决问题的方式。让我知道答案是否有误或是否需要澄清。
我希望这对某人有用。有一个很好的编程

关于java - 使用JPasswordField并使用getPassword +服务器登录过程处理密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18501164/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com