Can you get this same Java SHA-1 in PHP please?

2019-02-14 08:47发布

问题:

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?

回答1:

[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); 


回答2:

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();
}


回答3:

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.