2016-02-13 57 views
1

我试着将密码安全地保存在我的数据库中。 我读过关于这个主题的内容,所有人都推荐使用salt并将其保存为密码。我把它放在mysql后,salt被改变了

于是我找出这个实现:

private static byte[] setSalt() throws NoSuchAlgorithmException { 
     SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 
     byte[] salt = new byte[16]; 
     sr.nextBytes(salt); 
     return salt; 
    } 

然后我试图将其保存为一个字节在MySQL

public static void test(byte[] salt) throws Exception{ 
    SQLconnect sql = new SQLconnect(); 
    Object[] prepString = {salt, "admin" }; 
    sql.runQuery("UPDATE orhalimi_cl_db.users set Salt = ? where Username = ?", 
      prepString,ACTION.UPDATE); 
} 

这里是更新:

preparedStatement = connect.prepareStatement(query); 
      for (int i = 0; i < prepString.length; i++) { 
       preparedStatement.setObject(i + 1, prepString[i]); 
      } 
preparedStatement.executeUpdate(); 

而且当我从DB中检索盐并检查equals时,这些再见是不同的。

我在我的数据库中将salt保存为binary(16)

这里是我如何得到它从DB:

List<HashMap<String, Object>> results; 
SQLconnect sql = new SQLconnect(); 
Object[] prepString = {"Salt", "admin" }; 
results = sql.runQuery("SELECT ? from orhalimi_cl_db.users where Username = ?", 
       prepString,ACTION.SELECT); 
     if (results.isEmpty()) { 
      System.out.println("empty"); 
      return null; 
     } 
return ((String) results.get(0).get("Salt")).getBytes(); 

我知道它的样子我已经给了很多不同部分的从我的代码, 但它看起来像我弄乱的字节保存或退出MySQL。所以我给了所有可能相关的信息。

在此先感谢。

答:

有趣的事情,我的问题是,我所有的SQL preparedStatement时使用?,而不是只对价值的事实。所以它看起来像SELECT ? from orhalimi_cl_db.users who Username = ?,转向SELECT 'Salt' from orhalimi_cl_db.users where Username = 'admin',答案总是盐。

无论如何,我将其转换为十六进制和Base64和容易得多,现在

+2

尝试保存为blob。另外,在附注中,我认为你需要用你的密码来加密盐。 – anaxin

+0

请提供'SHOW CREATE TABLE' –

回答

3

你试图写一个原始字节值到MySQL。由于字节值在Java中是带符号的8位整数,所以这注定会出错。 基本上有两种选择:a)使用BLOB数据类型或b)将字节值编码为Base64字符串。

如果是BLOB类型(这似乎是您尝试的),您需要读写二进制安全。这也意味着,您不能简单地将它从数据库中读取为String,然后期望获得的字节与您写入数据库的字节相同。你必须使用二进制流。

我宁愿选择第二个选项。它更安全,更简单。特别是因为你需要将你的密码字符串与盐一起散列。使用Base64编码,您可以简单地连接密码和salt,然后计算字符串上的散列值。

编辑: 下面的示例如何生成腌SHA512哈希:

public String getSalt() { 
    String saltStr = ""; 
    try { 
     final SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 
     final byte[] salt = new byte[16]; 
     sr.nextBytes(salt); 
     saltStr = Base64.getEncoder().encodeToString(salt); 
    } catch (NoSuchAlgorithmException ignored) { } 

    return saltStr; 
} 

public String hashPassword(String password, String salt) 
{ 
    try { 
     final MessageDigest md = MessageDigest.getInstance("SHA-512"); 

     // hash password and salt 
     md.update(password.getBytes()); 
     md.update(salt.getBytes()); 
     final byte[] hash = md.digest(); 

     // convert byte array to hex string 
     final StringBuffer sb = new StringBuffer(); 
     for (int i = 0; i < hash.length; ++i) { 
      sb.append(Integer.toString((hash[i] & 0xff) + 0x100, 16).substring(1)); 
     } 

     return sb.toString(); 
    } catch (NoSuchAlgorithmException ignored) { return ""; } 
} 

这样,你也可以使用其他任何字符串作为盐。但是,当然推荐使用安全的随机数生成器,如getSalt()

使用此方法的一大优势是,对Java的内部字节处理的依赖较少。使用生成的十六进制散列和Base64盐串,您可以轻松重新计算Java外部的散列值,例如直接在数据库内部进行计算。

+0

我尝试保存,根据盐进行计算并进行比较。这真的很有用。有趣的是,我的问题是,我在所有SQL准备语句中使用'?'而不仅仅是在值上。因此,它看起来像来自orhalimi_cl_db.users的'SELECT'Salt',其中Username ='admin'',答案总是盐。换句话说 - 我讨厌preparedStatement。 无论如何,你可以用base64和十六进制编码帮助我,所以我不需要这么做。谢谢 –

+1

The?只是价值的占位符,以确保它们被正确地消毒。它们不用于引用表或列名称。 –

1

这是一个X-Y问题,其中真正的问题是Is it difficult to store password hashes properly?。答案是肯定的。

我建议你在用户表中放置一个char(60)列并使用bcrypt

你有意重新发明轮子吗?也许这是一个学习练习,在这种情况下,去get'em老虎

+0

是的,它是学习练习,但我觉得我惹的祸。 –

相关问题