0

I am writing a program which concatenate a word R at the end of a password and then calculate the SHA-256 hash. Later on, adding the R word again at the end of the hex result and calculate the new hash with SHA256.

I want this to repeat for 100 times. Every time I want to print the hash.

So something like this, in pseudo code:

hash = SHA256(...(SHA256(SHA256(“password”||R)||R)||R)..)

I am currently testing my code by hashing 2 times:

   String R = "f@ghj!$g";
   hash = password.concat(R);

   MessageDigest md = MessageDigest.getInstance("SHA-256");
   digest = hash.getBytes(StandardCharsets.UTF_8);

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();

     hash = String.format("%064x", new BigInteger(1,digest)).concat(R);
     System.out.println(hash);

     digest = hash.getBytes(StandardCharsets.UTF_8);
   }

Lets forget this concatenation for a sec.

For example can't understand why the following two codes produce different results:

Code 1:

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();

   }

 hash = String.format("%064x", new BigInteger(1,digest));   
 System.out.println(hash);

Code 2:

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();
     //convert hash to string
     hash = String.format("%064x", new BigInteger(1,digest));
     //convert string again to bytes
     digest = hash.getBytes(StandardCharsets.UTF_8);
   }

 System.out.println(hash);

My question is: which is the right way to decode the hash (Byte[]) into hex String every time to concatenate the R word and encode again into bytes the right way?

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Codefather
  • 27
  • 1
  • 9

2 Answers2

1

In your first block of code the R is concatenated on every iteration, in the second code (now code fragment 1) it is only concatenated at the end, which explains the different results. This is referring to the code in the initial post.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
olivergregorius
  • 109
  • 1
  • 7
  • There is no concatenation going on in code fragment 1 or 2, so I don't get this answer. – Maarten Bodewes Mar 30 '19 at 18:04
  • You don't get it because the question has been edited that much that my answer doesn't make sense anymore! Check the initial revision. – olivergregorius Mar 30 '19 at 18:45
  • OK, fair enough. That first post didn't make too much sense, but your answer is correct if we don't consider the edits. BlackMam please try and post only after rereading your question, otherwise confusion like this may ensue. – Maarten Bodewes Mar 30 '19 at 19:34
1

Code fragment 1 is correct, but you need to add the print statement to it to get your expected output. For this however you need to use a true hex encoder / decoder, which - unhelpfully - is not delivered by default in java.util.


Here is a reworked example, without the concatenation, which I left out deliberately to leave you something to do.

The code uses a relatively slow but easy to remember and read toHex function. The BigInteger first needs to construct a BigInteger, which is wasteful and probably even slower. Although the code seems to work correctly for 32 byte hash values, I'd still consider the code hard to maintain.

public static byte[] printHexadecimalHashIterations(byte[] input, int iterations)
{
    var digest = input.clone();

    MessageDigest md;
    try
    {
        md = MessageDigest.getInstance("SHA-256");
    }
    catch (NoSuchAlgorithmException e)
    {
        throw new IllegalStateException("SHA-256 hash should be available", e);
    }

    for (int i = 0; i < iterations; i++)
    {
        md.update(digest);
        digest = md.digest();

        printDigest("Intermediate hash", digest);
    }

    printDigest("Final hash", digest);

    return digest;
}

public static void printDigest(String hashType, byte[] digest)
{
    var digestInHex = toHex(digest);
    System.out.printf("%s: %s%n", hashType, digestInHex);
}

public static String toHex(byte[] data)
{
    var sb = new StringBuilder(data.length * 2);
    for (int i = 0; i < data.length; i++)
    {
        sb.append(String.format("%02X", data[i]));
    }
    return sb.toString();
}

public static void main(String[] args)
{
    printHexadecimalHashIterations("password".getBytes(StandardCharsets.UTF_8), 2);
}

The main thing to take away from this is that data to (secure) hash functions consists of bytes (or octets if you prefer that name). The hexadecimal string is just a textual representation of these bytes. It is not identical to the data itself.

You should be able to differentiate between binary data and hexadecimals, which is just a representation of binary data. Don't ever call binary data "hex" as you do in the question: that's a red flag that you don't get the difference.

However, in your case you only need the hexadecimals to print them out to screen; you don't need to convert the digest byte array to hexadecimals at all; it remains available. So you can just go on with it.


In case you need to convert this textual representation back to bytes then you would need to perform hex decoding. Obviously you would again need a good method that does not involve BigInteger for that. There are plenty of libraries (Guava, Apache Commons, Bouncy Castle) that provide good hex encoders / decoders and good questions / answers on this on SO. The statement hash.getBytes(StandardCharsets.UTF_8) in code fragment 2 doesn't perform hexadecimal decoding, it performs character encoding.


As a final hint: the update methods allow streaming of data into the digest function. That means that you never actually have to concatenate anything to calculate the digest over the concatenation: you can just perform multiple calls to update instead.

Happy programming.


EDIT:

To perform your task I would do something like this:

final byte[] passwordBytes = "password".getBytes(StandardCharsets.UTF_8);
final byte[] rBytes = "f@ghj!$g".getBytes(StandardCharsets.UTF_8);

digest.update(passwordBytes);
digest.update(rBytes);
byte[] currentHash = digest.digest();

for (int i = 1; i < iterations; i++)
{
    digest.update(currentHash);
    digest.update(rBytes);
    currentHash = digest.digest();
}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • There is a small but possible chance that you actually need to concatenate a hexadecimal string with the `R` string and then need to character encode it. This isn't reflected by your pseudo code though. If this is the case none-the-less then the person that created the requirements / protocol doesn't understand encoding either. You should go back and explain that the protocol is bad. – Maarten Bodewes Mar 30 '19 at 13:57
  • I understood that i have to concatenate every time a hexadecimal string with the R but after your answer i am not sure anymore. – Codefather Mar 30 '19 at 18:09
  • Well, *if* this is the case then code fragment 2 could be correct. If doesn't make sense to hash encoded hexadecimals much. There would also be a difference in hashing upper and lowercase hex, to name just one issue. Note that *many many* programmers and even teachers say "hex" when binary or bytes is meant. Better ask for clarification if you suspect this is the case. – Maarten Bodewes Mar 30 '19 at 18:14
  • So you suggest to update on each iteration with the byte[] R ( md.update(R) )? In your code what is the "var" word? – Codefather Mar 30 '19 at 18:15
  • `var` is just a way to not have to type the class name, it declares a *local* variable which then needs to be assigned immediately to the compiler can confer the class. It has been introduced in Java 10 to make the language easier to write. So e.g. `digest` is a `byte[]` because that's what `input.clone()` returns. – Maarten Bodewes Mar 30 '19 at 18:19
  • OK, so you're learning anyway, so I showed in the answer what would be *my* answer to the question you were asked, hoping that this is what was meant. You'll again have to include the print statements etc. though. – Maarten Bodewes Mar 30 '19 at 18:29
  • Thank you again! You were really helpful. I think i had not understood well what update function does and this was also a problem. – Codefather Mar 30 '19 at 18:36