import org.postgresql.core.*;
/**
- * $Id: Connection.java,v 1.34 2001/11/01 01:08:36 barry Exp $
+ * $Id: Connection.java,v 1.35 2001/11/12 19:11:56 barry Exp $
*
* This abstract class is used by org.postgresql.Driver to open either the JDBC1 or
* JDBC2 versions of the Connection class.
private static final int AUTH_REQ_KRB5 = 2;
private static final int AUTH_REQ_PASSWORD = 3;
private static final int AUTH_REQ_CRYPT = 4;
+ private static final int AUTH_REQ_MD5 = 5;
// New for 6.3, salt value for crypt authorisation
private String salt;
// Get the type of request
areq = pg_stream.ReceiveIntegerR(4);
- // Get the password salt if there is one
+ // Get the crypt password salt if there is one
if (areq == AUTH_REQ_CRYPT)
{
byte[] rst = new byte[2];
rst[0] = (byte)pg_stream.ReceiveChar();
rst[1] = (byte)pg_stream.ReceiveChar();
salt = new String(rst, 0, 2);
- DriverManager.println("Salt=" + salt);
+ DriverManager.println("Crypt salt=" + salt);
+ }
+
+ // Or get the md5 password salt if there is one
+ if (areq == AUTH_REQ_MD5)
+ {
+ byte[] rst = new byte[4];
+ rst[0] = (byte)pg_stream.ReceiveChar();
+ rst[1] = (byte)pg_stream.ReceiveChar();
+ rst[2] = (byte)pg_stream.ReceiveChar();
+ rst[3] = (byte)pg_stream.ReceiveChar();
+ salt = new String(rst, 0, 4);
+ DriverManager.println("MD5 salt=" + salt);
}
// now send the auth packet
switch (areq)
{
case AUTH_REQ_OK:
- break;
-
+ break;
+
case AUTH_REQ_KRB4:
DriverManager.println("postgresql: KRB4");
throw new PSQLException("postgresql.con.kerb4");
pg_stream.flush();
break;
+ case AUTH_REQ_MD5:
+ DriverManager.println("postgresql: MD5");
+ byte[] digest = MD5Digest.encode(PG_USER, PG_PASSWORD, salt);
+ pg_stream.SendInteger(5 + digest.length, 4);
+ pg_stream.Send(digest);
+ pg_stream.SendInteger(0, 1);
+ pg_stream.flush();
+ break;
+
default:
throw new PSQLException("postgresql.con.auth", new Integer(areq));
}
--- /dev/null
+package org.postgresql.util;
+
+/**
+ * MD5-based utility function to obfuscate passwords before network transmission
+ *
+ * @author Jeremy Wohl
+ *
+ */
+
+import java.security.*;
+
+public class MD5Digest
+{
+ private MD5Digest() {}
+
+
+ /**
+ * Encodes user/password/salt information in the following way:
+ * MD5(MD5(password + user) + salt)
+ *
+ * @param user The connecting user.
+ * @param password The connecting user's password.
+ * @param salt A four-character string sent by the server.
+ *
+ * @return A 35-byte array, comprising the string "md5", followed by an MD5 digest.
+ */
+ public static byte[] encode(String user, String password, String salt)
+ {
+ MessageDigest md;
+ byte[] temp_digest, pass_digest;
+ byte[] hex_digest = new byte[35];
+
+
+ try {
+ md = MessageDigest.getInstance("MD5");
+
+ md.update(password.getBytes());
+ md.update(user.getBytes());
+ temp_digest = md.digest();
+
+ bytesToHex(temp_digest, hex_digest, 0);
+ md.update(hex_digest, 0, 32);
+ md.update(salt.getBytes());
+ pass_digest = md.digest();
+
+ bytesToHex(pass_digest, hex_digest, 3);
+ hex_digest[0] = (byte) 'm'; hex_digest[1] = (byte) 'd'; hex_digest[2] = (byte) '5';
+ } catch (Exception e) {
+ ; // "MessageDigest failure; " + e
+ }
+
+ return hex_digest;
+ }
+
+
+ /**
+ * Turn 16-byte stream into a human-readable 32-byte hex string
+ */
+ private 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];
+ }
+ }
+}