视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
mysql协议的认证包及代码详情介绍
2020-11-09 09:09:06 责编:小采
文档


git

https://github.com/sea-boat/mysql-protocol

概况

mysql客户端登陆到mysql服务端需要一个交互的过程,首先服务端给客户端发送的初始握手包,客户端接收到握手包后向服务端返回认证包。如下,这里分析下认证包。

client server
 |-------connect------>|
 | |
 |<-----handshake------|
 | |
 |---authentication--->|
 | |

mysql通信报文结构

Payload认证包

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
 }

更多详情 : http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse

认证包操作

1.认证包类

/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://www.gxlcms.com/;/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";
 }

}
  1. 加解密工具

/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://www.gxlcms.com/;/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) + );
 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;
 }

}
  1. 测试类

/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://www.gxlcms.com/;/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;
 }
}

下载本文
显示全文
专题