views:

516

answers:

2

Hi there,

I've noticed that a lot of my Google searches took me here so i thought perhaps i could borrow your apt minds :)

I'm working on a One Time Password generator for a mobile device (as well as website to log in to) as part of my third year degree dissertation.

Using the org.bouncycastle.crypto.digests.MD5Digest library i am taking a byte array (from a string user input) then hashing it X number of times. This is also known as daisy chaining hash strings or lamports method of encryption.

My issue is that if the string is hashed once then it correctly hashes it, however if the new hash is hashed again the outcome is incorrect.

See code below:

private String generateHash(String OTP, int loopNum)
    {
      byte[] secretBytes = OTP.getBytes();

      for (int x = 0; x < loopNum; x++)
      {
          byte[] tempStore = new byte[16];
          tempStore = hash(secretBytes);
          secretBytes = tempStore;
      }

      return convertToHex(secretBytes);
   }

   public byte[] hash(byte[] secretBytes)
   {
        org.bouncycastle.crypto.digests.MD5Digest digest = new org.bouncycastle.crypto.digests.MD5Digest();

        digest.reset();

        // Update MD5 digest with user secret in byte format
        digest.update(secretBytes, 0, secretBytes.length);

        // get length of digest to initialise new md5 byte array
        int length = digest.getDigestSize();

        // create md5 byte array using length
        byte[] md5 = new byte[length];

        // calculate MD5 hash, using md5 byte array, 0 for buffer offset
        digest.doFinal(md5, 0);

        return md5;
   }


 private static String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        String Hex;
        String formattedHex;
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9))
                    buf.append((char) ('0'  +halfbyte));+
+                else+
+                    buf.append((char) ('a'+  (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }

        Hex = buf.toString();

        formattedHex = "\n"  +Hex.substring(0, 4)+  " "  +Hex.substring(4, 8)+  " "  +Hex.substring(8, 12)+  " "  +Hex.substring(12, 16)+  " "  +Hex.substring(16, 20)+  " "  +Hex.substring(20, 24)+  " "  +Hex.substring(24, 28)+  " " + Hex.substring(28, 32);
        return formattedHex;
    }

I think it is either;

  1. The digest does not return a correct byte array
  2. The Hex converter incorrectly converts this

Im testing using the secret of: A which has the following MD5 Outputs:

  1. 7fc56270e7a70fa81a5935b72eacbe29
  2. 8f28f2e7231860115d2a8cacba019dbe (this should be 4cbd6d53280de25e04712c7434a70642)

Many thanks for your help in advance :)

p.s. I'm checking it against a PHP md5 could this also be an issue?

+3  A: 

MD5, when applied to an input consisting in a single byte of value 0x41 (an 'A'), yields a 16-byte output which, when printed in hexadecimal, is 7fc56270e7a70fa81a5935b72eacbe29.

If you apply MD5 on those 16 bytes, you should get 8f28f2e7231860115d2a8cacba019dbe, and that's what you get.

Now, if you consider MD5 applied on a 32-byte string which is the ASCII encoding of the character string "7fc56270e7a70fa81a5935b72eacbe29", then this yields 4cbd6d53280de25e04712c7434a70642. So I think that your Java code is fine (for that) and that your confusion comes from how you give the input data to your PHP-based test code. You write 7fc562... and you think about it as "one byte of value 0x7f, then one byte of value 0xc5, then..." but the PHP code takes it as "one byte of value 0x37 (ASCII code for a '7'), then one byte of value 0x66 (ASCII code for an 'f'), then...".

On a Linux system, try this:

$ printf A | md5sum
7fc56270e7a70fa81a5935b72eacbe29  -
$ printf 7fc56270e7a70fa81a5935b72eacbe29 | md5sum
4cbd6d53280de25e04712c7434a70642  -
$ printf "\x7f\xc5\x62\x70\xe7\xa7\x0f\xa8\x1a\x59\x35\xb7\x2e\xac\xbe\x29" | md5sum
8f28f2e7231860115d2a8cacba019dbe  -

As side notes:

  • Be wary of OTP.getBytes(). It converts a string into bytes by using the locale-dependent charset. This will use UTF-8, UTF-16, ISO-8859-1,... depending on the system configuration, usually tied to the "system language". Your code will act differently on the same string, which is rarely a good idea. Instead, use OTP.getBytes("UTF-8") which will compute the same bytes regardless of the local configuration.
  • Your hash loop includes useless mantras. For instance, you allocate a 16-byte array, which you never use.
  • It is considered bad coding-style in Java to have a variable name beginning with an uppercase letter. If you plan on showing your code in a school context then you should rename Hex into hex.
  • When halfByte is obtained as the result of a "& 0x0F" then it necessarily contains a value between 0 and 15. The "0 <= halfByte" test is unnecessary.
Thomas Pornin
Bingo. The php md5() method only takes a string, not raw bytes.
Kevin
Firstly, thank you guys so much for your reply i really honestly appreciate it. With the 2 byte arrays, i understand i dont need two i was just trying to ensure that the byte array wasnt being overwritten incorrectly etc.Is there any way to get php to accept a byte array? I'm not sure how to go about fixing this.
Andy G
+1  A: 

Thank you all very much for your help, the problem was that my java md5 did not behave like a php MD5.

I found a solution to the problem which basically (in Java) take the byte array and convert it into a hex string and THEN get the bytes for this string which is then MD5'd rather than using the un hex'd byte array. See solutions below

See the following for the result: http://forums.sun.com/thread.jspa?forumID=9&amp;threadID=718781

static String byteArrayToHexString(byte byteValues[]) {
        byte singleChar = 0;
        if (byteValues == null || byteValues.length <= 0)
            return null;

        String entries[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "a", "b", "c", "d", "e", "f" };

        StringBuffer out = new StringBuffer(byteValues.length * 2);

        for (int i = 0; i < byteValues.length; i++) {
            singleChar = (byte) (byteValues[i] & 0xF0);
            singleChar = (byte) (singleChar >>> 4);
            // shift the bits down
            singleChar = (byte) (singleChar & 0x0F);
            out.append(entries[(int) singleChar]); 
            singleChar = (byte) (byteValues[i] & 0x0F); 
            out.append(entries[(int) singleChar]);
        }
        String rslt = new String(out);
        return rslt;
    }

Thanks a lot for all those who posted, cant thank you enough!

Andy Garbett