mysql 协议的认证包及代码详情介绍

git

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

概况

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

client                 server     |-------connect------>|     |                     |     ||     |                     |

mysql通信报文结构

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 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";      }    }
  1. 加解密工具

/**   *    * @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;      }    }
  1. 测试类

/**   *    * @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 END
喜欢就支持一下吧
点赞11 分享