Java calculate hex representation of a SHA-1 digest of a String

JavaHashSha1

Java Problem Overview


I'm storing the user password on the db as a sha1 hash.

Unfortunately I'm getting strange answers.

I'm storing the string as this:

MessageDigest cript = MessageDigest.getInstance("SHA-1");
              cript.reset();
              cript.update(userPass.getBytes("utf8"));
              this.password = new String(cript.digest());

I wanted something like this -->

aff --> "0c05aa56405c447e6678b7f3127febde5c3a9238"

rather than

aff --> �V@\D~fx����:�8

Java Solutions


Solution 1 - Java

Using apache common codec library:

DigestUtils.sha1Hex("aff")

The result is 0c05aa56405c447e6678b7f3127febde5c3a9238

That's it :)

Solution 2 - Java

This is happening because cript.digest() returns a byte array, which you're trying to print out as a character String. You want to convert it to a printable Hex String.

Easy solution: Use Apache's commons-codec library:

String password = new String(Hex.encodeHex(cript.digest()),
                             CharSet.forName("UTF-8"));

Solution 3 - Java

One iteration of a hash algorithm is not secure. It's too fast. You need to perform key strengthening by iterating the hash many times.

Furthermore, you are not salting the password. This creates a vulnerability to pre-computed dictionaries, like "rainbow tables."

Instead of trying to roll your own code (or using some sketchy third-party bloatware) to do this correctly, you can use code built-in to the Java runtime. See this answer for details.

Once you have hashed the password correctly, you'll have a byte[]. An easy way to convert this to a hexadecimal String is with the BigInteger class:

String passwordHash = new BigInteger(1, cript.digest()).toString(16);

If you want to make sure that your string always has 40 characters, you may need to do some padding with zeroes on the left (you could do this with String.format().)

Solution 4 - Java

If you don't want to add any extra dependencies to your project, you could also use

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);

Solution 5 - Java

The crypt.digest() method returns a byte[]. This byte array is the correct SHA-1 sum, but crypto hashes are typically displayed to humans in hex form. Each byte in your hash will result in two hex digits.

To safely convert a byte to hex use this:

// %1$ == arg 1
// 02  == pad with 0's
// x   == convert to hex
String hex = String.format("%1$02x", byteValue);

This code snippet can be used for converting a char to hex:

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 
import java.io.*;

public class UnicodeFormatter  {

   static public String byteToHex(byte b) {
      // Returns hex String representation of byte b
      char hexDigit[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
      };
      char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
      return new String(array);
   }

   static public String charToHex(char c) {
      // Returns hex String representation of char c
      byte hi = (byte) (c >>> 8);
      byte lo = (byte) (c & 0xff);
      return byteToHex(hi) + byteToHex(lo);
   }
}

Note that working with bytes in Java is very error prone. I would double check everything and test some strange cases as well.

Also you should consider using something stronger than SHA-1. http://csrc.nist.gov/groups/ST/hash/statement.html

Solution 6 - Java

With Google Guava:

Maven:

<dependency>
   <artifactId>guava</artifactId>
   <groupId>com.google.guava</groupId>
   <version>14.0.1</version>
</dependency>

Sample:

HashCode hashCode = Hashing.sha1().newHasher()
   .putString(password, Charsets.UTF_8)
   .hash();            

String hash = BaseEncoding.base16().lowerCase().encode(hashCode.asBytes());

Solution 7 - Java

If you use Spring its quite simple:

MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");

Solution 8 - Java

There's more than just simple standard hash algorithms involved in storing passwords nonreversible.

  1. Do multiple rounds to make brute-force attacks slower
  2. Use a per-record "salt" as input to the hash algorithm besides the password to make dictionary attacks less feasible and avoid output collisions.
  3. Use "pepper", an application-configuration-setting as input to the hash algorithm to make a stolen database-dump with an unknown "pepper" useless.
  4. Pad the input to avoid weaknesses in some hash algorithms e.g. where you could append a character to the password without knowing the password, by modifying the hash.

For more info, see e.g.

You could also use a http://en.wikipedia.org/wiki/Password-authenticated_key_agreement method to avoid passing the password in cleartext to the server at all.

Solution 9 - Java

To use UTF-8, do this:

userPass.getBytes("UTF-8");

And to get a Base64 String from the digest, you can do something like this:

this.password = new BASE64Encoder().encode(cript.digest());

Since MessageDigest.digest() returns a byte array, you can convert it to String using Apache's Hex Encoding (simpler).

E.g.

this.password = Hex.encodeHexString(cript.digest());

Solution 10 - Java

digest() returns a byte array, which you're converting to a string using the default encoding. What you want to do is base64 encode it.

Solution 11 - Java

How about converting byte[] to base64 string?

    byte[] chkSumBytArr = digest.digest();
    BASE64Encoder encoder = new BASE64Encoder();
    String base64CheckSum = encoder.encode(chkSumBytArr);

Solution 12 - Java

you can use this code too(from crackstation.net):

private static String toHex(byte[] array)
{
    BigInteger bi = new BigInteger(1, array);
    String hex = bi.toString(16);
    int paddingLength = (array.length * 2) - hex.length();
    if(paddingLength > 0)
        return String.format("%0" + paddingLength + "d", 0) + hex;
    else
        return hex;
}

Solution 13 - Java

        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.reset();
        messageDigest.update(password.getBytes("UTF-8"));
        String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);

Solution 14 - Java

echo -n "aff" | sha1sum produce the correct output (echo inserts a newline by default)

Solution 15 - Java

You need to hex encode the result first. MessageDigest returns a "raw" hash, rather than a human readable one.

Edit:

@thejh provided a link to code which should work. Personally, I'd suggest using either Bouncycastle or Apache Commons Codec to do the job. Bouncycastle would be good if you want to do any other crypto-related operations.

Solution 16 - Java

In java 17 HexFormat is introduced.

It allows you to write:

var cript = MessageDigest.getInstance("sha1");
var hexformat = HexFormat.of();
this.password = hexformat.formatHex(cript.digest(userPass.getBytes(StandardCharsets.UTF_8)))

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarcos Roriz JuniorView Question on Stackoverflow
Solution 1 - JavaaltumanoView Answer on Stackoverflow
Solution 2 - JavaJason NicholsView Answer on Stackoverflow
Solution 3 - JavaericksonView Answer on Stackoverflow
Solution 4 - JavaManish SahniView Answer on Stackoverflow
Solution 5 - Javacyber-monkView Answer on Stackoverflow
Solution 6 - JavaThoView Answer on Stackoverflow
Solution 7 - JavaVedranView Answer on Stackoverflow
Solution 8 - JavaStefan LView Answer on Stackoverflow
Solution 9 - JavaBuhake SindiView Answer on Stackoverflow
Solution 10 - JavaPaulJWilliamsView Answer on Stackoverflow
Solution 11 - JavaMahesView Answer on Stackoverflow
Solution 12 - JavaMahdiView Answer on Stackoverflow
Solution 13 - JavaPankaj KumarView Answer on Stackoverflow
Solution 14 - JavaKrystalView Answer on Stackoverflow
Solution 15 - JavadevelopmentalinsanityView Answer on Stackoverflow
Solution 16 - JavaStevenView Answer on Stackoverflow