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

git

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

概况

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

|-------connect------>|

| |

|

| |

|---authentication--->|

| |

mysql通信报文结构

1488953034453256.png

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

*

email: 849586227@qq.com

*

blog: http://www.gxlcms.com/;/pre>

*

mysql auth packet.

*/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

*

email: 849586227@qq.com

*

blog: http://www.gxlcms.com/;/pre>

*

a security util .

*/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

*

email: 849586227@qq.com

*

blog: http://www.gxlcms.com/;/pre>

*

test auth packet.

*/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;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值