package com.impossibl.postgres.utils;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Authentication {

	public static String encode(String password, String user, byte salt[]) {
		
		byte[] tempDigest, passDigest;
		byte[] hexDigest = new byte[35];

		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
		}
		catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		md.update(password.getBytes(UTF_8));
		md.update(user.getBytes(UTF_8));
		tempDigest = md.digest();

		bytesToHex(tempDigest, hexDigest, 0);
		md.update(hexDigest, 0, 32);
		md.update(salt);
		passDigest = md.digest();

		bytesToHex(passDigest, hexDigest, 3);
		hexDigest[0] = (byte) 'm';
		hexDigest[1] = (byte) 'd';
		hexDigest[2] = (byte) '5';

		return new String(hexDigest, UTF_8);
	}

	static void bytesToHex(byte[] bytes, byte[] hex, int offset) {
		final char lookup[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

		int i, c, j, pos = offset;

		for (i = 0; i < 16; i++) {
			c = bytes[i] & 0xFF;
			j = c >> 4;
			hex[pos++] = (byte) lookup[j];
			j = (c & 0xF);
			hex[pos++] = (byte) lookup[j];
		}
	}

}

@@ -32,16 +32,74 @@
 utils;%0A%0A
+import static java.nio.charset.StandardCharsets.US_ASCII;%0A
 import s
@@ -234,41 +234,380 @@
 n;%0A%0A
-public class MD5Authentication %7B%0A
+import javax.xml.bind.annotation.adapters.HexBinaryAdapter;%0A%0Apublic class MD5Authentication %7B%0A%0A%09/**%0A%09 * Generates an authentication token compatible with PostgreSQL's%0A%09 * MD5 scheme%0A%09 * %0A%09 * @param password Password to generate token from%0A%09 * @param user Username to generate token from%0A%09 * @param salt Salt to generate token from%0A%09 * @return MD5 authentication token%0A%09 */
 %0A%09pu
@@ -680,16 +680,65 @@
 %5D) %7B%0A%09%09%0A
+%09%09HexBinaryAdapter hex = new HexBinaryAdapter();%0A
 %09%09byte%5B%5D
@@ -1059,45 +1059,80 @@
 %0A%0A%09%09
-bytesToHex(tempDigest, hexDigest, 0);
+hexDigest = hex.marshal(tempDigest).toLowerCase().getBytes(US_ASCII);%0A%09%09
 %0A%09%09m
@@ -1153,15 +1153,8 @@
 gest
-, 0, 32
 );%0A%09
@@ -1203,540 +1203,66 @@
 ();%0A
-%0A
 %09%09
-bytesToHex(passDigest, hexDigest, 3);%0A%09%09hexDigest%5B0%5D = (byte) 'm';%0A%09%09hexDigest%5B1%5D = (byte) 'd';%0A%09%09hexDigest%5B2%5D = (byte) '5';%0A%0A%09%09return new String(hexDigest, UTF_8);%0A%09%7D%0A%0A%09static void bytesToHex(byte%5B%5D bytes, byte%5B%5D hex, int offset) %7B%0A%09%09final char lookup%5B%5D = %7B '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' %7D;%0A%0A%09%09int i, c, j, pos = offset;%0A%0A%09%09for (i = 0; i %3C 16; i++) %7B%0A%09%09%09c = bytes%5Bi%5D & 0xFF;%0A%09%09%09j = c %3E%3E 4;%0A%09%09%09hex%5Bpos++%5D = (byte) lookup%5Bj%5D;%0A%09%09%09j = (c & 0xF);%0A%09%09%09hex%5Bpos++%5D = (byte) lookup%5Bj%5D;%0A%09%09%7D
+%0A%09%09return %22md5%22 + hex.marshal(passDigest).toLowerCase();
 %0A%09%7D%0A

Use Java 6's built in HexBinaryAdapter for hex encoding in MD5