47

I've been investigating a bit about Java String encryption techniques and unfortunately I haven't find any good tutorial how to hash String with SHA-512 in Java; I read a few blogs about MD5 and Base64, but they are not as secure as I'd like to (actually, Base64 is not an encryption technique), so I prefer SHA-512.

9ilsdx 9rvj 0lo
  • 7,955
  • 10
  • 38
  • 77
stack man
  • 2,303
  • 9
  • 34
  • 54
  • 2
    SHA2 is not an encryption technique either. It is a family of cryptographical secure hash functions, it can be used for message digest, not encryption (at least no in normal mode). You need to describe a bit more what you want to do. Other than that yur MD5 code should work with other hash algrithms as well, just exchange the algorithm name. The output of all of them is a binary byte[] array, so you might want to use base64 or binhex to make the result ascii only. – eckes Oct 12 '15 at 16:09
  • 3
    SHA-512 is not an encryption algorithm either. Actually, it's designed specifically to make it impossible to "decrypt" the digest produced by this hashing algorithm. What do you want to achieve? – JB Nizet Oct 12 '15 at 16:09
  • 1
    A java String contains valid Unicode. Getting the bytes in some charset (like UTF-8) and then encrypting them yields bytes that cannot be correctly translated to a String (for decrypting), certainly not with UTF-8 which requires correct sequences. Hence often Base64 is added to translate encrypted bytes into ASCII. – Joop Eggen Oct 12 '15 at 16:11
  • @JBNizet question updated with the goal I am trying to achieve. Thank you all for trying to help guys. – stack man Oct 12 '15 at 16:13
  • 3
    There is no cryptographic algorithm that allows doing what you want. Either you want encryption (but you shouldn't if the goal is to store passwords), and you need a key to encrypt/decrypt. Or you want a salt and a digest function (and a hashing function is what you want, although SHA-512 is considered too weak for that usage). The principle of salt + digest is that you can check if a submitted password is correct by resalting and redigesting it, and then check that the result is identical to the one stored in database. But it's impossible to reverse the result and get the original password. – JB Nizet Oct 12 '15 at 16:18
  • 3
    Use PBKDF2WithHmacSHA1 (which comes with the JDK) or BCrypt. But first, learn the principles of crypto. – JB Nizet Oct 12 '15 at 16:19
  • 1
    Possible duplicate of [How can I hash a password in Java?](http://stackoverflow.com/questions/2860943/how-can-i-hash-a-password-in-java) – Artjom B. Oct 12 '15 at 16:49

7 Answers7

80

you can use this for SHA-512 (Not a good choice for password hashing).

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public String get_SHA_512_SecurePassword(String passwordToHash, String salt){
    String generatedPassword = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update(salt.getBytes(StandardCharsets.UTF_8));
        byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for(int i=0; i< bytes.length ;i++){
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        generatedPassword = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return generatedPassword;
}
A. Sinha
  • 2,666
  • 3
  • 26
  • 45
  • That's exactly what I was looking for my friend, thanks a lot. – stack man Oct 12 '15 at 16:17
  • does not take character encoding into account, this has potential to break when you switch platform – artbristol Oct 12 '15 at 16:29
  • @artbristol please elaborate, what are the threats and how it can be minimized – A. Sinha Oct 12 '15 at 16:43
  • any time you turn a string into bytes, you need to specify an encoding. E.g. you should have `passwordToHash.getBytes("UTF-8")` – artbristol Oct 13 '15 at 08:00
  • 10
    @stackpepe - This should not be used to hash passwords, the SHA*-family is too fast and can be brute-forced too easily. With a good GPU you can calculate about [4 Giga SHA512 per second](http://hashcat.net/oclhashcat/#performance), that why one should switch to BCrypt, PBKDF2 or SCrypt. – martinstoeckli Oct 13 '15 at 11:50
  • @martinstoeckli Sir but we generally say dat we cud use a salt value to protect against dictionary attack...........can it also be compromised ? – A. Sinha Oct 13 '15 at 11:59
  • 5
    @Abhi - A salt prevents that an attacker can build one single rainbow-table to get all passwords at once, instead (s)he has to build a rainbow-table for each salt, what makes the attack unpracticable. The salt doesn't help at all against brute-forcing (often dictionary attack) a single password though. If brute-forcing a whole english dictionary only takes a fraction of a millisecond, then we need an algorithm with a cost factor, which defines how much time is necessary to calculate a single hash (BCrypt, PBKDF2, SCrypt). – martinstoeckli Oct 13 '15 at 13:31
  • 5
    Besides that the Salt is input as an UTF-8 String (while it is usually defined in bytes), the length is not checked, the exception handling isn't well defined (to the point that the code doesn't compile), the hexadecimal routine is hard to read and included in the method, Java naming conventions are not adhered to, bad coding practice by first assigning null when not needed...just a bad demo on how to use SHA-512. I guess the main reason for the upvotes is that this question comes up **first** in google for "SHA-512 Java" search term. – Maarten Bodewes Dec 29 '16 at 10:33
  • 2
    @MaartenBodewes People need to put some efforts from their side as well to get a better output. The code that I have provided is not for copy paste but is just a hint that can surely be improved as per requirement. – A. Sinha Jan 04 '17 at 06:39
  • 2
    @Abhi: yet that is exactly what people will do: copy and paste. – Martijn Pieters Apr 11 '17 at 07:13
  • 1
    Sorry, but that's a downvote. Like TheGreatContini answers below (and martinstoeckli mentions in a comment) simply hashing a password is not good enough. Salting first is a bit better, but still not enough, you're working with passwords! Really, the only good answer to "how do I hash a password with SHA512" is "don't use SHA512 but something better like Argon2". – ayke Aug 15 '18 at 22:07
  • @ayke People are free to use whatever they want depending upon their wish, may be someone could use bcrypt, others might like Argon2 etc. but since the question was regarding the implementation of SHA-512, I just provided that. With all due respect, I must say that my post is not to suggest the right approach (one should explore it at their own) but just to show the right implementation of the question asked. :) – A. Sinha Aug 16 '18 at 06:21
  • Of course people are free to use what they want. But giving them a false sense of security is not good. SHA-512 should not be used on it's own for password hashing except for rare cases where compatibility is needed with legacy system. The question indicates this is not the case. – ayke Aug 17 '18 at 22:10
27

Please stop using hash functions to encode passwords! They do not provide the protection you need. Instead, you should be using an algorithm like PBKDF2, bcrypt, or scrypt.

References:

TheGreatContini
  • 6,429
  • 2
  • 27
  • 37
  • 10
    I support your answer, but commonly those algorithms are called hash functions too (PBKDF even uses SHA for example). It is difficult enough to explain the difference between encryption and hashing, so the advice not to use hash functions may be a bit confusing. The slowness makes the difference. – martinstoeckli Oct 13 '15 at 11:55
  • I entirely agree that the terminology is confusing! I have my only complaints in Section 1.4: https://eprint.iacr.org/2015/387.pdf . Terminology matters! – TheGreatContini Oct 13 '15 at 22:00
  • @martinstoeckli Reading this back, this is not a correct remark. PBKDF2 is a **key derivation function** or **password hashing** function. That it *uses* a secure hash doesn't make it a secure hash in itself. There are many PBKDF's / password hashes that do not use secure hashes internally. – Maarten Bodewes Dec 28 '16 at 00:32
  • 2
    @MaartenBodewes - Maybe the best expression for appropriate algorithms is **password hashing function**. What I meant is, that it sounds confusing when we say: don't use hash functions, use password hash functions. If we do that, we should explain what makes the difference, and the important point in this case is the "cost factor", or more generally, that we can control the necessary time to calculate a hash. – martinstoeckli Dec 28 '16 at 19:35
  • It doesn't answer the question. – 9ilsdx 9rvj 0lo Aug 28 '18 at 14:14
  • I think this summarises the confusion: [When Hashing isn't Hashing](https://adamcaudill.com/2016/05/23/when-hashing-isnt-hashing/). – TheGreatContini Jan 30 '19 at 20:46
  • 1
    I agree, this doesn't answer the question at all. People looking for `SHA-512` Java code examples (regardless of WHY they need this) find a non-solution. Perhaps in the future you could 1. Offer a solution. 2. Offer the BETTER solution as a side-note. – tresf Nov 27 '19 at 18:23
14

Using Guava:

Hashing.sha512().hashString(s, StandardCharsets.UTF_8).toString()
TOUDIdel
  • 1,322
  • 14
  • 22
3

Use Apache Commons Crypt, it features SHA-512 based crypt() functions that generate salted hashes that are even compatible to libc's crypt and thus usable in PHP/Perl/Python/C and most databases, too.

https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/Crypt.html#Crypt%28%29

lathspell
  • 3,040
  • 1
  • 30
  • 49
2

you could use this to hash a password in java if you want to.

public static boolean isHashMatch(String password, // the password you want to check.
                                  String saltedHash, // the salted hash you want to check your password against.
                                  String hashAlgorithm, // the algorithm you want to use.
                                  String delimiter) throws NoSuchAlgorithmException // the delimiter that has been used to delimit the salt and the hash.
{
    // get the salt from the salted hash and decode it into a byte[].
    byte[] salt = Base64.getDecoder()
                        .decode(saltedHash.split(delimiter)[0]);
    // compute a new salted hash based on the provided password and salt.
    String pw_saltedHash = computeSaltedBase64Hash(password, 
                                                   salt,
                                                   hashAlgorithm,
                                                   delimiter);
    // check if the provided salted hash matches the salted hash we computed from the password and salt.
    return saltedHash.equals(pw_saltedHash);
}

public static String computeSaltedBase64Hash(String password, // the password you want to hash
                                             String hashAlgorithm, // the algorithm you want to use.
                                             String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
    // compute the salted hash with a random salt.
    return computeSaltedBase64Hash(password, null, hashAlgorithm, delimiter);
}

public static String computeSaltedBase64Hash(String password, // the password you want to hash
                                             byte[] salt, // the salt you want to use (uses random salt if null).
                                             String hashAlgorithm, // the algorithm you want to use.
                                             String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
    // transform the password string into a byte[]. we have to do this to work with it later.
    byte[] passwordBytes = password.getBytes();
    byte[] saltBytes;

    if(salt != null)
    {
        saltBytes = salt;
    }
        else
        {
            // if null has been provided as salt parameter create a new random salt.
            saltBytes = new byte[64];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(saltBytes);              
        }

    // MessageDigest converts our password and salt into a hash. 
    MessageDigest messageDigest = MessageDigest.getInstance(hashAlgorithm);
    // concatenate the salt byte[] and the password byte[].
    byte[] saltAndPassword = concatArrays(saltBytes, passwordBytes);
    // create the hash from our concatenated byte[].
    byte[] saltedHash = messageDigest.digest(saltAndPassword);
    // get java's base64 encoder for encoding.
    Encoder base64Encoder = Base64.getEncoder();
    // create a StringBuilder to build the result.
    StringBuilder result = new StringBuilder();

    result.append(base64Encoder.encodeToString(saltBytes)) // base64-encode the salt and append it.
          .append(delimiter) // append the delimiter (watch out! don't use regex expressions as delimiter if you plan to use String.split() to isolate the salt!)
          .append(base64Encoder.encodeToString(saltedHash)); // base64-encode the salted hash and append it.

    // return a salt and salted hash combo.
    return result.toString();
}

public static byte[] concatArrays(byte[]... arrays)
{   
    int concatLength = 0;
    // get the actual length of all arrays and add it so we know how long our concatenated array has to be.
    for(int i = 0; i< arrays.length; i++)
    {
        concatLength = concatLength + arrays[i].length;
    }
    // prepare our concatenated array which we're going to return later.
    byte[] concatArray = new byte[concatLength];
    // this index tells us where we write into our array.
    int index = 0;
    // concatenate the arrays.
    for(int i = 0; i < arrays.length; i++)
    {
        for(int j = 0; j < arrays[i].length; j++)
        {
            concatArray[index] = arrays[i][j];
            index++;
        }
    }
    // return the concatenated arrays.
    return concatArray;     
}
lolcat
  • 29
  • 3
2
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;

public String getHashSHA512(String StringToHash, String salt){
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            md.update(salt.getBytes(StandardCharsets.UTF_8));
            byte[] bytes = md.digest(StringToHash.getBytes(StandardCharsets.UTF_8));
            generatedPassword = Hex.encodeHexString(bytes);
        }
        catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        return generatedPassword;
    }

It's not recommended to use hash functions for passwords though, newer alogrithms like bcrypt, or scrypt exist

-1

With secure hashing combine 3 salt components (of 150 random characters each) to a individual user salt (user salt from the user database table, general salt in a database table (monthly change with cron job) and hide some salt in the application library). Align the for loop amount of the secure hash to your needs. See answer above for hashing method.

private static String generateSalt(int lenght){
    String abcCapitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String abcLowerCase = "abcdefghijklmnopqrstuvwxyz";
    String numbers = "01234567890123456789";
    String characters = "!@#$%^&*!@#$%%^^&*";

    String total = abcCapitals + abcLowerCase + numbers + characters;

    String response = "";

    char letters[] = new char[lenght];
    for (int i=0; i<lenght-1; i++){
        Random r = new Random();
        char letter = total.charAt(r.nextInt(total.length()));
        letters[i] = letter;
    }

    response = Arrays.toString(letters).replaceAll("\\s+","");
    response = response.replaceAll(",","");

    return response;
}

private static String getHash(String passwordToHash, String salt){
            String generatedPassword = null;
            try {
                 MessageDigest md = MessageDigest.getInstance("SHA-512");
                 md.update(salt.getBytes(StandardCharsets.UTF_8));
                 byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
                 StringBuilder sb = new StringBuilder();
                 for(int i=0; i< bytes.length ;i++){
                    sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
                 }
                 generatedPassword = sb.toString();
                } 
               catch (NoSuchAlgorithmException e){
                   System.out.println(e);
               }
            return generatedPassword;
        }

    public static String getSecureHash(String password, String salt){
        String hash = getHash(password, salt);
        for (int i=0; i<20000; i++){
            hash = getHash(password, hash);      
        }
        return hash;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String salt = generateSalt(150);
        String salt2 = generateSalt(150);
        String salt3 = generateSalt(150);
        String someString = "This is some string!";

        String hash = getSecureHash(someString, salt + salt2 + salt3);
        System.out.println(hash);
    }