views:

46

answers:

4

I've tried the following with MySQL UTF-8 and Latin-1, to no avail.

I hash my passwords (in this case toSecurify) using SHA-1 like so:

  if(toSecurify == null) {
   throw new Exception("toSecurifyString must not be null");
  }
  try {
   MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
   byte[] sha1HashBytes = new byte[40];
   messageDigest.update(toSecurify.getBytes(), 0, toSecurify.length());
   sha1HashBytes = messageDigest.digest();
   return new String(sha1HashBytes, "UTF-8");
  } catch(NoSuchAlgorithmException nsae) {
   throw new Exception("Hash algorithm not supported.");
  } catch(UnsupportedEncodingException uee) {
   throw new Exception("Encoding not supported.");
  }

Then I store this in the mysql database password column.

Now here's the tricky part I can query the db kind of like: Is there any record with

username=<insertUserName> and password = thatHashFunctionUpThere(<insertPassword>);

This works great.

But now, updating records looks something like this:

String userName = someJdbcStuffToGetUsername();
String password = someJdbcStuffToGetPassword();

update(userName, password);

The password has now changed! This corrupts the passwords. It's like on the way out (when querying for it) it gets corrupted, but never on the way in. I say this because inserts and queries work great, but when I get the value out then set it again, it corrupts it, so it must be on the way out.

Does anyone have any thoughts? Where on the way out should I look for encoding issues?

Thanks in advance guys!

A: 

Are you re-hashing the already hashed password on update by mistake?

Steve Claridge
That's a good suggestion Steve, but no, I'm not. :|
Jor
A: 

Going to repost this under a more concise question on stackoverflow. I've isolated the problem to a particular region, and the question will get more traction if phrased as such.

Jor
+1  A: 

There's a flaw in your code.

return new String(sha1HashBytes, "UTF-8"); 

You shouldn't be treating the bytes as characters. You should in fact convert every byte to a 2-character hexstring. E.g.

StringBuilder hex = new StringBuilder(sha1HashBytes.length * 2);
for (byte b : sha1HashBytes) {
    if ((b & 0xff) < 0x10) hex.append("0");
    hex.append(Integer.toHexString(b & 0xff));
}
return hex.toString();

But, better is to just use MySQL's own SHA1() function. On INSERT do:

String sql = "INSERT INTO user (username, password) VALUES (?, SHA1(?))";
// ...
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(username);
preparedStatement.setString(password); // This one should be unhashed!!
int affectedRows = preparedStatement.executeUpdate();
// ...

and on UPDATE:

String sql = "UPDATE user SET username = ?, password = SHA1(?) WHERE id = ?";
// ...
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(username);
preparedStatement.setString(password); // This one should be unhashed!!
preparedStatement.setLong(id);
int affectedRows = preparedStatement.executeUpdate();
// ...

and on SELECT:

String sql = "SELECT * FROM user WHERE username = ? AND password = SHA1(?)";
// ...
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(username);
preparedStatement.setString(password); // This one should be unhashed!!
resultSet = preparedStatement.executeQuery();
// ...

See also:

BalusC
A: 

Hey BalusC,

This is Jor posting as guest, as I cannot login right now to reply for some odd reason. I hope you can see this and answer this question.

  1. I cannot do it in SQL as I don't want to send unencrypted secure info across the wire to another host which will run the db. But that solution sounds great! I wish it could do it at the PreparedStatement level! :)

  2. Thank you for your suggested fix. It worked! But the more important question is why? Can you please elaborate in depth? I want to choose your answer as the correct one, but cannot log in now! :(

JorGuest
All your user accounts are unregistered. Such an account is by a long-living cookie tied to a *specific* PC and webbrowser. You cannot reuse the same account at a different PC and/or webbrowser. If you register your account (using OpenID), then you will be able to login with the same account from everywhere. I'd suggest to pick the same PC and webbrowser with which you've asked the question and then register the account. If you fail in recovering and registering your original account with which you asked the questions, send a mail to [email protected]. They'll help you further.
BalusC
As to why it works, well, the answer is simple: bytes are not the same as characters. You incorrectly treated them as characters while you have to convert them to characters first. A hexstring is a common way to represent bytes as "human readable" characters. You may find [this article](http://balusc.blogspot.com/2009/05/unicode-how-to-get-characters-right.html) useful to understand more about bytes vs characters (although it does not treat this particular issue, it will help to understand what is happening under the covers).
BalusC