https://github.com/sea-boat/mysql-protocol
Overview
Mysql client requires an interactive process to log in to the mysql server. First, the server sends an initial handshake packet to the client. After receiving the handshake packet, the client returns an authentication packet to the server. As follows, the authentication package is analyzed here.
client server |-------connect------>| | | |<-----handshake------| | | |---authentication--->| | |
4 capability flags, CLIENT_PROTOCOL_41 always set 4 max-packet size 1 character set string[23] reserved (all [0]) string[NUL] username if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA { lenenc-int length of auth-response string[n] auth-response } else if capabilities & CLIENT_SECURE_CONNECTION { 1 length of auth-response string[n] auth-response } else { string[NUL] auth-response } if capabilities & CLIENT_CONNECT_WITH_DB { string[NUL] database } if capabilities & CLIENT_PLUGIN_AUTH { string[NUL] auth plugin name } if capabilities & CLIENT_CONNECT_ATTRS { lenenc-int length of all key-values lenenc-str key lenenc-str value if-more data in 'length of all key-values', more keys and value pairs }
/** * * @author seaboat * @date 2016-09-25 * @version 1.0 * <pre class="brush:php;toolbar:false"><b>email: </b>849586227@qq.com
<b>blog: </b>http://www.php.cn/;/pre> * <p>mysql auth packet.</p> */public class AuthPacket extends MySQLPacket { private static final byte[] FILLER = new byte[23]; public long clientFlags; public long maxPacketSize; public int charsetIndex; public byte[] extra; public String user; public byte[] password; public String database; public void read(byte[] data) { MySQLMessage mm = new MySQLMessage(data); packetLength = mm.readUB3(); packetId = mm.read(); clientFlags = mm.readUB4(); maxPacketSize = mm.readUB4(); charsetIndex = (mm.read() & 0xff); int current = mm.position(); int len = (int) mm.readLength(); if (len > 0 && len < FILLER.length) { byte[] ab = new byte[len]; System.arraycopy(mm.bytes(), mm.position(), ab, 0, len); this.extra = ab; } mm.position(current + FILLER.length); user = mm.readStringWithNull(); password = mm.readBytesWithLength(); if (((clientFlags & Capabilities.CLIENT_CONNECT_WITH_DB) != 0) && mm.hasRemaining()) { database = mm.readStringWithNull(); } } public void write(ByteBuffer buffer) throws IOException { BufferUtil.writeUB3(buffer, calcPacketSize()); buffer.put(packetId); BufferUtil.writeUB4(buffer, clientFlags); BufferUtil.writeUB4(buffer, maxPacketSize); buffer.put((byte) charsetIndex); buffer.put(FILLER); if (user == null) { buffer.put((byte) 0); } else { BufferUtil.writeWithNull(buffer, user.getBytes()); } if (password == null) { buffer.put((byte) 0); } else { BufferUtil.writeWithLength(buffer, password); } if (database == null) { buffer.put((byte) 0); } else { BufferUtil.writeWithNull(buffer, database.getBytes()); } } @Override public int calcPacketSize() { int size = 32;// 4+4+1+23; size += (user == null) ? 1 : user.length() + 1; size += (password == null) ? 1 : BufferUtil.getLength(password); size += (database == null) ? 1 : database.length() + 1; return size; } @Override protected String getPacketInfo() { return "MySQL Authentication Packet"; } }
/** * * @author seaboat * @date 2016-09-25 * @version 1.0 * <pre class="brush:php;toolbar:false"><b>email: </b>849586227@qq.com
<b>blog: </b>http://www.php.cn/;/pre> * <p>a security util .</p> */public class SecurityUtil { public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] pass1 = md.digest(pass); md.reset(); byte[] pass2 = md.digest(pass1); md.reset(); md.update(seed); byte[] pass3 = md.digest(pass2); for (int i = 0; i < pass3.length; i++) { pass3[i] = (byte) (pass3[i] ^ pass1[i]); } return pass3; } public static final String scramble323(String pass, String seed) { if ((pass == null) || (pass.length() == 0)) { return pass; } byte b; double d; long[] pw = hash(seed); long[] msg = hash(pass); long max = 0x3fffffffL; long seed1 = (pw[0] ^ msg[0]) % max; long seed2 = (pw[1] ^ msg[1]) % max; char[] chars = new char[seed.length()]; for (int i = 0; i < seed.length(); i++) { seed1 = ((seed1 * 3) + seed2) % max; seed2 = (seed1 + seed2 + 33) % max; d = (double) seed1 / (double) max; b = (byte) java.lang.Math.floor((d * 31) + 64); chars[i] = (char) b; } seed1 = ((seed1 * 3) + seed2) % max; seed2 = (seed1 + seed2 + 33) % max; d = (double) seed1 / (double) max; b = (byte) java.lang.Math.floor(d * 31); for (int i = 0; i < seed.length(); i++) { chars[i] ^= (char) b; } return new String(chars); } private static long[] hash(String src) { long nr = 1345345333L; long add = 7; long nr2 = 0x12345671L; long tmp; for (int i = 0; i < src.length(); ++i) { switch (src.charAt(i)) { case ' ': case '\t': continue; default: tmp = (0xff & src.charAt(i)); nr ^= ((((nr & 63) + add) * tmp) + (nr << 8)); nr2 += ((nr2 << 8) ^ nr); add += tmp; } } long[] result = new long[2]; result[0] = nr & 0x7fffffffL; result[1] = nr2 & 0x7fffffffL; return result; } }
/** * * @author seaboat * @date 2016-09-25 * @version 1.0 * <pre class="brush:php;toolbar:false"><b>email: </b>849586227@qq.com
<b>blog: </b>http://www.php.cn/;/pre> * <p>test auth packet.</p> */public class AuthPacketTest { @Test public void produce() { // handshake packet's rand1 and rand2 byte[] rand1 = RandomUtil.randomBytes(8); byte[] rand2 = RandomUtil.randomBytes(12); byte[] seed = new byte[rand1.length + rand2.length]; System.arraycopy(rand1, 0, seed, 0, rand1.length); System.arraycopy(rand2, 0, seed, rand1.length, rand2.length); AuthPacket auth = new AuthPacket(); auth.packetId = 0; auth.clientFlags = getClientCapabilities(); auth.maxPacketSize = 1024 * 1024 * 16; auth.user = "seaboat"; try { auth.password = SecurityUtil .scramble411("seaboat".getBytes(), seed); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } auth.database = "test"; ByteBuffer buffer = ByteBuffer.allocate(256); auth.write(buffer); buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes, 0, bytes.length); String result = HexUtil.Bytes2HexString(bytes); System.out.println(result); assertTrue(Integer.valueOf(result.substring(0, 2), 16) == result .length() / 2 - 4); AuthPacket auth2 = new AuthPacket(); auth2.read(bytes); assertTrue(auth2.database.equals("test")); } protected int getClientCapabilities() { int flag = 0; flag |= Capabilities.CLIENT_LONG_PASSWORD; flag |= Capabilities.CLIENT_FOUND_ROWS; flag |= Capabilities.CLIENT_LONG_FLAG; flag |= Capabilities.CLIENT_CONNECT_WITH_DB; flag |= Capabilities.CLIENT_ODBC; flag |= Capabilities.CLIENT_IGNORE_SPACE; flag |= Capabilities.CLIENT_PROTOCOL_41; flag |= Capabilities.CLIENT_INTERACTIVE; flag |= Capabilities.CLIENT_IGNORE_SIGPIPE; flag |= Capabilities.CLIENT_TRANSACTIONS; flag |= Capabilities.CLIENT_SECURE_CONNECTION; return flag; } }
The above is the detailed content of A detailed introduction to the authentication package and code of the mysql protocol. For more information, please follow other related articles on the PHP Chinese website!