使用JPasswordField并使用getPassword + server登录过程处理密码

时间:2013-08-29 01:59:35

标签: java swing jpasswordfield getpasswd

经过一天的长时间编程后,我想发布一些对别人有用的东西。

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

这就是问题:

如何以安全的方式从JPasswordField正确获取值并处理它以创建服务器的登录过程?

1 个答案:

答案 0 :(得分:4)

这是我达成的解决方案。

首先,我决定登录程序对我的目的来说足够安全,我不想向服务器发送一个普通密码而我不想存储一个普通密码(显然)在我的数据库中。

首先要说的是,保护密码的一个好方法是,它永远不会以简单易读的形式通过网络进行交换,这显然是因为中间有可能的"# 34;用少数几个词来说就是有人在去服务器的途中阅读你的消息。

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

有很多算法要做,我选择SHA256。

然后对密码进行哈希处理,但就在我们之间,这可能还不够。如果一个黑客能够窃取哈希,那么有一些技巧可以让他成功地进行翻译"它的。只是为了在他的等式中添加一个变量,并使他的生活更加艰难,我们可以在哈希之前为密码添加一个盐。 salt是一段字符串,添加到我们想要密码的任何位置。这可以避免基于字典和最常用密码的某种攻击。

但如果一个比我训练得更好的黑客无法读取密码,我怎么能

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

但要理解这一点,我们需要在注册程序中跳过一会儿"这是将新用户添加到我的数据库的时刻。就是这样:

  1. 客户要求服务器注册发送昵称。
  2. 服务器使用作为密码盐的令牌回答。
  3. 客户端将密码加密,将其哈希并将其发送回服务器。
  4. 服务器现在收到一些不可读的内容,因此没有安全问题,并使用昵称和salt存储它。盐渍哈希密码是"常见秘密"。
  5. 所以登录程序将是这样的:

    1. 客户要求服务器登录
    2. 服务器用salt回答
    3. 客户端将密码加密,然后将其哈希并将其发送回服务器。
    4. 服务器将共享密钥与收到的字符串进行比较。如果等于,则允许用户登录。
    5. 这应该很好,但在这种情况下,如果黑客知道共享密钥可以毫无问题地访问服务器,因为这样做我们只是更改了密码,不是可读的,但仍然可以直接使用。

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

      1. 客户要求服务器登录
      2. 服务器使用salt和随机session-salt
      3. 进行回答
      4. 客户端密码,哈希密码。在这一点上,它再次将哈希值重新哈希并重新哈希。然后它将哈希盐渍哈希密码发送回服务器
      5. 服务器获取共享密钥,将其与随机会话盐一起使用,然后对其进行哈希处理。如果两个字符串相等,则允许用户登录。
      6. 现在程序很明确,我们有一个问题需要解决。如果我处理任何类型的String,它可以在内存中保留很长时间,所以如果我将我的密码放在String中,它可以在普通形式下长时间可读。这对我们来说不是那么好,但我们不是第一个考虑它的,java确实创建了一种避免此密码持久存在的方法。解决方案是使用一组字符。这是因为即使数组持久存储在内存中,其数据也会在内存中没有顺序传播,并且很难重新创建原始密码。

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

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

        我们想到的第一件事就是将该数组转换为普通字符串....... 但这正是我想要避免的。所以我需要按原样处理数组。

        我们需要一个方法来对密码进行salt和hash,结果可以是:

        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();
        
        }
        

        此方法创建一个通过许多小字符串的字节数组,然后附加salt。盐渍后,它会使用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()是一种阻塞方法,这意味着线程会等到在尝试获取之前,队列中有一些东西可供使用。

        这(几乎)我如何解决我的问题。如果答案中有任何错误或者您需要澄清,请告诉我。 我希望这对某人有用。有一个很好的编程