3 Replies Latest reply: May 27, 2004 5:49 PM by 843810 Branched to a new discussion. RSS

    MessageDigest MD5 does not match up with PHP md5

    843810
      Hello Java Developers,

      Quite simply, I am generating an MD5 fingerprint from Java and wanting to verify it in PHP on a remote web site. No matter what I've tried and despite all my trouble-shooting, the results always come out differently.

      I understand that PHP's md5 function returns the result as a hex string. I've written my own Java toHexString function, but the result still comes out different (slightly different in fact). I will paste both my Java code and PHP code, let me know if you see anything wrong:

      Java Code:
           MessageDigest     md5 = MessageDigest.getInstance("MD5");
           Date          dt = Calendar.getInstance().getTime();
           Random          r = new Random (dt.getTime ());
           int          nRandom;
           String          s;
                     
           nRandom = sr.nextInt();
           s = (nRandom + ":" + nCode + ":" + (nRandom ^ 426473384));
           
           // I've also tried "US-ASCII" here
           md5.update (s.getBytes ("UTF-8")); 
      
           // Returns URL fingerprint and ingredients to be sent to PHP script
           return "fp=" + toHexString (md5.digest()) + "&r=" + nRandom;
      The toHexString function:
           private String toHexString (byte [] v)
           {
                StringBuffer     sb = new StringBuffer ();
                byte          n1, n2;
                
                for (int c = 0; c < v.length; c++)
                {
                     n1 = (byte)((v[c] < 0 ? -v[c] + 127 : v[c]) / 0x10);
                     n2 = (byte)((v[c] < 0 ? -v[c] + 127 : v[c]) % 0x10);
                     
                     sb.append (n1 >= 0xA ? (char)(n1 - 0xA + 'a') : (char)(n1 + '0'));
                     sb.append (n2 >= 0xA ? (char)(n2 - 0xA + 'a') : (char)(n2 + '0'));
                }
                
                return sb.toString();
           }
      PHP Code:

      $s = $_GET['r'] . ":" . $_GET['code'] . ":" . ((int)$_GET['r'] ^ 426473384);
      echo $s . "<BR>";
      echo $_GET['fp'] . " == " . md5($s);

      The results always differ, and I have verified that the XOR mathematical expression is consistent between Java and PHP. Thanks for your time, any help would be greatly appreciated.

      Jonathan Neufeld
      Software Engineer
      http://www.extollit.com
        • 1. Re: MessageDigest MD5 does not match up with PHP md5
          843810
          Are you sure that PHP converts your string into bytes using UTF-8??? That is possibly your issue....
          • 2. Re: MessageDigest MD5 does not match up with PHP md5
            843810
            I checked into the PHP String encoding format, and since it doesn't support UTF-8 encoding, it's encoding is US-ASCII. However, that shouldn't matter because UTF-8 is backward compatible for ASCII characters < 0x7F, which is what I'm passing to the hashing function.

            After more thorough examination of my code (and some sleep), I found the problem was with my toHexString function afterall (I am a little surprised that Sun doesn't provide one built-into the JDK). Java "byte" types are stored as 2's complement and so I corrected my toHexString function as follows:
                 private String toHexString (byte [] v)
                 {
                      StringBuffer     sb = new StringBuffer ();
                      byte          n1, n2;
                      
                      for (int c = 0; c < v.length; c++)
                      {
                           n1 = (byte)((v[c] & 0xF0) >>> 4); // This line was changed
                           n2 = (byte)((v[c] & 0x0F)); // So was this line
                           
                           sb.append (n1 >= 0xA ? (char)(n1 - 0xA + 'a') : (char)(n1 + '0'));
                           sb.append (n2 >= 0xA ? (char)(n2 - 0xA + 'a') : (char)(n2 + '0'));
                      }
                      
                      return sb.toString();
                 }
            Thanks for your help.
            • 3. Re: MessageDigest MD5 does not match up with PHP md5
              843810
              Use this simpler version:
                  private static String toHexString(byte[] v) {
                      StringBuffer sb = newStringBuffer(v.length * 2);
                      for (int i = 0; i < v.length; i++) {
                           int b = v[i] & 0xFF;
                           sb.append(HEX_DIGITS.charAt(b >>> 4))
                             .append(HEX_DIGITS.charAt(b & 0xF));
                      }
                      return sb.toString();
                  }
                  private static final String HEX_DIGITS = "0123456789abcdef";
              Note also that PHP strings have an internal 8-bit encoding depending on the platform on which it runs and it was compiled. Prior to interpreting which byte values will be stored in PHP strings, you need to determine the origin of the encoded string to determine also its encoding. PHP Strings may contain UTF-8 strings or other 8-bit encodings.
              It's up to you in your PHP script to provide the encoding conversion before using te PHP MD5 implementation (which ignores all encoding differences as it just considers byte values stored in the PHP string value). There's no problem in the PHP implementation of MD5. The complexity comes from the difficulty to trace the encoding effectively used in the source PHP string value.
              In Java you would have exactly the same difficulties if all your strings were stored only in arrays of bytes. Java adds a UCS2 semantic to char values, and UTF-16 semantic to string values, so that you can perform conversion from Java UTF-16 "Strings" to any other encoding made for streams of 8-bit bytes...