I need to encode a string in md5 and then it's bytes to 2 chars hex encoding.
I've got this short JAVA snippets and need to convert it into PHP.
// Condensed the message and do MD5
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] data = cleartext.getBytes(ENCODING);
md.update(data);
byte[] digestedByteArray = md.digest();
// Convert digested bytes to 2 chars Hex Encoding
md5String = HexUtils.bytesToHex(digestedByteArray);
} catch (NoSuchAlgorithmException ns) {
ns.printStackTrace();
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
I'm not quite sure how to achieve this in PHP. ENCODING is UTF-8
What I have in PHP is
$md5String = md5($clearText);
$hexString = "";
for($i = 0; $i < strlen($md5String); $i++) {
$hexString .= dechex(ord($md5String[$i]));
}
But the output is different, so I think I'm failing on the hex conversion.
So what's the proper port of this JAVA snippet to PHP?
The output of md5 is already a hex-encoded string, so there is nothing else you need to do (as long as you don't set the optional second parameter to true).
$hexString = md5($clearText);
Related
I can simply hash in PHP with a salt:
$orig_pw = "abcd";
$salt = 5f8f041b75042e56;
$password = hash('sha256', $orig_pw . $salt);
(This is not how I implement it, this is just an example. Salt is different for everyone)
And with this, the stored password is:
bc20a09bc9b3d3e1fecf0ed5742769726c93573d4133dbd91e2d309155fa9929
But if I try to do the same in Java, I get a different result. I tried String password = "abcd";
byte[] salt = hexStringToByteArray("5f8f041b75042e56");
try {
System.out.println(new String(getHash(password, salt)));
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
And the two methods:
public byte[] getHash(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.reset();
digest.update(salt);
try {
return digest.digest(password.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
The result is:
/¬1¶ĆĽëüFd?[$?¶»_9ËZ»ç¶S‘Ęŗש
Which coded to hex is not even close to it:
2fac31b6434c14ebfc46643f5b243fb6bb5f39cb5abb10e7b65391454c97d7a90d0a
Can anyone help with this?
Apart from the order being swapped, it looks like in PHP you're treating the salt value as a literal string to be appended to the password, while in Java you do a hex conversion of the salt first and then use the resulting bytes to update the MessageDigest. This will obviously yield different results. Looking only at the salt:
PHP: Salt -> To bytes (literal) -> SHA-256
Java: Salt -> To bytes (unhex) -> SHA-256
I just tried your Java code, and it's absolutely fine. I also tried to hash the same value in PHP as in Java and it gave me identical results.
The Java equivalent to your PHP code would be:
String password = "abcd";
String salt = "5f8f041b75042e56";
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest((password + salt).getBytes("UTF-8"));
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
return null;
}
After hexing the bytes it returns the following result:
60359BC8A0B09898335AA5A037B1E1B9CE3A1FE0D4CEF13514901FB32F3BCEB0
And in PHP doing:
<?
echo hash('sha256', "abcd"."5f8f041b75042e56");
?>
Returns exactly the same.
I think
digest.update(salt);
digest.digest(password.getBytes("UTF-8"));
is equivalent to:
hash('sha256', $salt . $orig_pw);
So the hash and salt are swapped. Can you confirm this?
I have java program which calculates SHA-256 checksum as follows. For example, if I provide the input abcd123ABCD00-4000 it outputs 0QtyIu4B+lU+TLqM/zfJz5ULVpyXgfLRs5mKXCQvbHM= . It also matches the online check sum calculator.
However the PHP code I used won't match it.
PHP code :
$s = 'abcd123ABCD00-4000';
$signature = base64_encode(hash_hmac("sha256", $s, True));
print base64_encode($signature);
Output : TWpRM1pEWXpaVFl4TURoallqTXdPRFV6WVRrNU5XVTRNRGxoWkRJMU1XWTVNREk1TnpBeU4ySXhaR0psTW1ZMk16Y3hPRE01WldFelkySXhOalJrWXc9PQ==
Java Code :
private static String getSHA256Hash(String text) {
String hash = null;
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes("UTF-8"));
byte[] shaDig = md.digest();
// hash = Hex.encodeHexString(shaDig);
hash = Base64.encodeBase64String(shaDig);
} catch (NoSuchAlgorithmException ex) {
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return hash;
}
Output : 0QtyIu4B+lU+TLqM/zfJz5ULVpyXgfLRs5mKXCQvbHM=
Here what I want is to get the equivalent result in PHP (i.e to change PHP code to match result of the Java program[ I won't able to change the Java code ]. Any help is appreciated
If you use hash() and base64 encode once you get the same result.
$s = 'abcd123ABCD00-4000';
$signature = base64_encode(hash("sha256", $s, True));
print $signature; // 0QtyIu4B+lU+TLqM/zfJz5ULVpyXgfLRs5mKXCQvbHM=
I have a sample application which generates a SHA1 hash in PHP as follows.
base64_encode(pack('H*', sha1($pass)));
I tried to achieve the same in Java, but so far, the output is different. The approach I used is as follows (Base64 and Hex classes come from commons-codec library).
byte[] rawSHA = null;
byte[] base64HexSHA = null;
String hex = null;
MessageDigest md= null;
// Get Message Digest Instance.
try {
md = MessageDigest.getInstance(SHA1_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
LOG.error("Unable to load SHA-1 Message Digest : " + e.getMessage(), e);
throw new IllegalStateException("SHA-1 Message Digest Instance Not Found");
}
// Build SHA1 Hash
rawSHA = md.digest(rawText.getBytes("UTF-8"));
// Convert to HEX
hex = new String(Hex.encodeHex(rawSHA));
// Encode to Base 64
base64HexSHA = Base64.encodeBase64(hex.getBytes("UTF-8"));
// Return String
return new String(base64HexSHA);
My question is, would the approach I have taken yield the same output as PHP's pack() function? My guess is that PHP pack() function returns the raw bytes where as the Hex.encodeHex returns hex string form (ref : http://www.w3schools.com/php/func_misc_pack.asp).
How can I achieve the same output as PHP's pack() function in Java (or the full output of the above PHP code) ?
Convertion to HEX is not required, just use this:
base64HexSHA = Base64.encodeBase64(rawSHA);
I find myself in a need to change website platforms from Java to PHP but I'd like to keep all my user's passwords...
I had this code do the password hashing prior to writting the hashed value as the password to the website:
MessageDigest md = null;
md = MessageDigest.getInstance("SHA");
md.update(plaintext.getBytes("UTF-8"));
byte raw[] = md.digest();
hash = new Base64().encodeToString(raw).replaceAll("\n", "").replaceAll("\r", "");
I think the Java code did SHA-1 hashing of the password but just prior to that it was byte encoded to UTF-8 and afterwards it was Base64 encoded.
I'd like to have a PHP code do the same, i.e. return the same value of a hash for the same password as in Java, only it seems that the PHP code doing SHA-1 hashing I have won't return the same SHA(-1, not Base64 encoded, I think?) value when compared to a Java Base64 decoded value of the hash...could it have something to do with the fact that my passwords in PHP are not UTF-8 byte encoded first (and how can I do that in PHP) please?
p.s.
Another strange thing...my passwords in Java are all 28characters long (usually something like this rnwn4zTNgH30l4pP8V05lRVGmF4=)...but the Base64().decode(hash) value of those password hashes is 10 characters long (an example [B#14e1f2b).
I thought Base64 did an additional 1 character to each 3 charters (28 or 27, excluding the padding = charter, is much more that a third larger than those 10 charcters) so am I doing the decoding call wrong somehow maybe???
And on top of all that the SHA-1 password hashed values in PHP are 40 characters long (in a UTF-8 mysql database) like so dd94709528bb1c83d08f3088d4043f4742891f4f?
[B#14e1f2b is definitely not a hash. It's a result of implicit conversion from byte[] to String.
It looks like you do something like this:
String decodedHash = Base64().decode(hash); // Produces [B#14e1f2b
However, the correct representation of the hash is a byte array:
byte[] decodedHash = Base64().decode(hash);
What I normally do with Java to compute a SHA-1 hash that is exactly identical to the PHP sha1() function is the following. The key is that toHexString is used to show the raw bytes in a printable way. If you use the PHP function and want to obtain the same result of your convoluted process, you need to use the parameter $raw_output to true in PHP to get the raw bytes and apply Base64. Full source code.
/**
* Compute a SHA-1 hash of a String argument
*
* #param arg the UTF-8 String to encode
* #return the sha1 hash as a string.
*/
public static String computeSha1OfString(String arg) {
try {
return computeSha1OfByteArray(arg.getBytes(("UTF-8")));
} catch (UnsupportedEncodingException ex) {
throw new UnsupportedOperationException(ex);
}
}
private static String computeSha1OfByteArray(byte[] arg) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(arg);
byte[] res = md.digest();
return toHexString(res);
} catch (NoSuchAlgorithmException ex) {
throw new UnsupportedOperationException(ex);
}
}
private static String toHexString(byte[] v) {
StringBuilder sb = new StringBuilder(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();
}
PHP's sha1() encodes each byte of the output as hexadecimal by default, but you can get the raw output by passing true as the second argument:
$digest = sha1($password, true); // This returns the same string of bytes as md.digest()
Then pass the digest to base64_encode and you are done:
base64_encode(sha1($password, true));
This returns the exact same SHA-1 hash as your java code.
I'm trying to create a PHP version of an existing JSP program, however I'm stuck at the password encryption part.
Could you please tell me how to convert this one? I know it tries to get the md5() but after that, I don't get it. I get lost in the Stringbuffer and for() parts.
Can you help me out?
public static String encryptPassword( String password )
{
String encrypted = "";
try
{
MessageDigest digest = MessageDigest.getInstance( "MD5" );
byte[] passwordBytes = password.getBytes( );
digest.reset( );
digest.update( passwordBytes );
byte[] message = digest.digest( );
StringBuffer hexString = new StringBuffer();
for ( int i=0; i < message.length; i++)
{
hexString.append( Integer.toHexString(
0xFF & message[ i ] ) );
}
encrypted = hexString.toString();
}
catch( Exception e ) { }
return encrypted;
}
Iraklis should be right. md5() gives you a hex-encoded output string by default. You only get the unencoded bytes like in Java by passing in TRUE for the optional $raw_output argument.
the lengths range from 29 to 32
Then your Java code has a bug. MD5 hashes are always 128 bits (32 hex digits). Here it is:
hexString.append( Integer.toHexString(0xFF & message[ i ] ) );
this will generate 1 instead of 01 for all bytes below 16. What you have stored is a mangled hash, from which you cannot recover the original MD5 value. If you absolutely must keep this broken data, you will have to reproduce the bug in PHP:
function makeBrokenMD5($s) {
$hash= md5($s, TRUE);
$bytes= preg_split('//', $hash, -1, PREG_SPLIT_NO_EMPTY);
$broken= '';
foreach ($bytes as $byte)
$broken.= dechex(ord($byte));
return $broken;
}
It converts the MD5 hash to a string hexadecimal numbers of the least significan byte of the character. In Java all chars are 2 bytes.
In practice this means just the ASCII value.
<?php
$password = "MyPass";
$hash = md5($password);
?>
UPDATE:
There are some discrepancies between the two versions. To fix this see #bobince answer.Here is the test code:
Java
package tests;
import java.security.MessageDigest;
/**
* Created by IntelliJ IDEA.
* User: Iraklis
* Date: 2 Ιουν 2010
* Time: 2:15:03 μμ
* To change this template use File | Settings | File Templates.
*/
public class Md5Test {
public static String encryptPassword(String password) {
String encrypted = "";
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] passwordBytes = password.getBytes();
digest.reset();
digest.update(passwordBytes);
byte[] message = digest.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < message.length; i++) {
hexString.append(Integer.toHexString(
0xFF & message[i]));
}
encrypted = hexString.toString();
}
catch (Exception e) {
}
return encrypted;
}
public static void main(String[] args) {
System.out.println("Pass1 md5 = " + encryptPassword("Test123FORXTREMEpass"));
System.out.println("Pass1 md5 = " + encryptPassword("Ijdsaoijds"));
System.out.println("Pass1 md5 = " + encryptPassword("a"));
System.out.println("Pass1 md5 = " + encryptPassword(" "));
}
}
Output:
Pass1 md5 = dc3a7b42a97a3598105936ef22ad2c1
Pass1 md5 = df7ca542bdbf7c4b8776cb21c45e7eef
Pass1 md5 = cc175b9c0f1b6a831c399e269772661
Pass1 md5 = 7215ee9c7d9dc229d2921a40e899ec5f
PHP
<?php
echo "Pass1 md5 = ".md5("Test123FORXTREMEpass")."<BR>";
echo "Pass2 md5 = ".md5("Ijdsaoijds")."<BR>";
echo "Pass3 md5 = ".md5("a")."<BR>";
echo "Pass4 md5 = ".md5(" ")."<BR>";
?>
output:
Pass1 md5 = dc3a7b42a97a35981059036ef22ad2c1
Pass2 md5 = df7ca542bdbf7c4b8776cb21c45e7eef
Pass3 md5 = 0cc175b9c0f1b6a831c399e269772661
Pass4 md5 = 7215ee9c7d9dc229d2921a40e899ec5f
To get the same results in both java and php I used this.
Java(make sure to call the method inside a "try" block):
public static String getHash(String pass) throws Exception
{
MessageDigest md=MessageDigest.getInstance("MD5");
md.update(pass.getBytes(),0,pass.length());
return new BigInteger(1,md.digest()).toString(16);
}
PHP:
<?php
echo md5(pass);
?>
Hope this helps
Edit: If the java variant returns 31 characters, adds a "0" in front of the string to match the php hash which returns 32 characters.