上下文回顾
https://yantao.wiki/archives/1711531064319
https://yantao.wiki/archives/1711531136504
Chap认证
初始化Init报文
public byte[] init(String ip, int portalVer, int authType, int size) {
// 构建portal协议中的字段包
byte[] ver = new byte[1];
byte[] type = new byte[1];
byte[] mod = new byte[1];
byte[] rsvd = new byte[1];
byte[] serialNo = doubleConvertBytes(65535 * Math.random());
byte[] reqID = new byte[2];
byte[] userIP = new byte[4];
byte[] userPort = new byte[2];
byte[] errCode = new byte[1];
byte[] attrNum = new byte[1];
byte[] challenge = new byte[16];
/*
* 给UserIP[]赋值 接收客户ip地址 IP地址压缩成4字节,如果要进一步处理的话,就可以转换成一个int了.
*/
String[] ips = ip.split("[.]");
// 将ip地址加入字段UserIP
for (int i = 0; i < 4; i++) {
int m = Integer.parseInt(ips[i]);
byte b = (byte) m;
userIP[i] = b;
}
ver[0] = (byte) portalVer;
type[0] = (byte) 0;
mod[0] = (byte) authType;
rsvd[0] = (byte) 0;
userPort[0] = (byte) 0;
userPort[1] = (byte) 0;
errCode[0] = (byte) 0;
attrNum[0] = (byte) 0;
// 创建Buff包
byte[] buff = new byte[size];
// 给Buff包赋初始值
buff[0] = ver[0];
buff[1] = type[0];
buff[2] = mod[0];
buff[3] = rsvd[0];
buff[4] = serialNo[0];
buff[5] = serialNo[1];
buff[8] = userIP[0];
buff[9] = userIP[1];
buff[10] = userIP[2];
buff[11] = userIP[3];
buff[12] = userPort[0];
buff[13] = userPort[1];
buff[14] = errCode[0];
buff[15] = attrNum[0];
return buff;
}
创建 Challenge 包
public byte[] sendChallengeReqAuth(byte[] buff) throws IOException {
// 创建Req_Challenge包
byte[] reqChallenge = new byte[16];
System.arraycopy(buff, 0, reqChallenge, 0, reqChallenge.length);
reqChallenge[1] = (byte) 1;
return sendSocket(reqChallenge);
}
sendSocket 方法
public byte[] sendSocket(byte[] msgBytes) throws IOException {
log.info("Request Message:{}", Arrays.toString(msgBytes));
DatagramPacket requestPacket;
// 创建发送数据包并发送给服务器
requestPacket = new DatagramPacket(msgBytes, msgBytes.length, InetAddress.getByName(acIp), acPort);
// 设置请求超时3秒
dataSocket.setSoTimeout(timeout);
dataSocket.send(requestPacket);
byte[] ackData = new byte[16];
// 接收服务器的数据包
DatagramPacket receivePacket = new DatagramPacket(ackData, ackData.length);
dataSocket.receive(receivePacket);
byte[] ackAuthData = new byte[receivePacket.getLength()];
System.arraycopy(ackData, 0, ackAuthData, 0, receivePacket.getLength());
return ackAuthData;
}
发送认证请求报文
public byte[] sendReqAuthByChap(String inUsername, String inPassword, byte[] buff, byte[] reqId, byte[] challenge) throws IOException {
byte[] username = inUsername.getBytes();
byte[] password = inPassword.getBytes();
// 生成 Chap 密码
byte[] chapPassword = ChapEncryptUtils.generateChapPassword(reqId, challenge, password);
// 构造认证信息的字节数组
byte[] authBuff = new byte[4 + username.length + 4 + chapPassword.length];
// AttrType: 1 (用户名)
authBuff[0] = (byte) 1;
// AttrLen: 长度为用户名长度+2
authBuff[1] = (byte)(username.length + 2);
// 拷贝用户名
System.arraycopy(username, 0, authBuff, 2, username.length);
// AttrType: 4 (经过 CHAP 加密的密码)
authBuff[2 + username.length] = (byte) 4;
// AttrLen: 长度为密码长度+2
authBuff[3 + username.length] = (byte)(chapPassword.length + 2);
// 拷贝 CHAP 密码
System.arraycopy(chapPassword, 0, authBuff, 4 + username.length, chapPassword.length);
// 创建 Req_Auth 包
byte[] reqAuth = new byte[16 + authBuff.length];
// 给 Req_Auth 包赋值
// 拷贝前16个字节
System.arraycopy(buff, 0, reqAuth, 0, 16);
reqAuth[1] = (byte) 3;
reqAuth[14] = (byte) 0;
short atrAttrNum = 2;
reqAuth[15] = (byte) atrAttrNum;
// 拷贝认证信息
System.arraycopy(authBuff, 0, reqAuth, 16, authBuff.length);
return sendSocket(reqAuth);
}
完整的报文发送工具 - PortalMessageUtils
package com.qunhe.its.networkportal.user.utils;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
/**
* Portal报文发送工具类
*/
@Getter
@Slf4j
public class PortalMessageUtils {
@Setter
private int timeout = 3000;
@Setter
private Integer receiverPort = null;
@Setter
@NonNull
private String acIp;
@Setter
@NonNull
private Integer acPort;
@Setter
@NonNull
DatagramSocket dataSocket;
/**
* 发送 Challenge 报文
*
* @param buff
* @return
* @throws IOException
*/
public byte[] sendChallengeReqAuth(byte[] buff) throws IOException {
// 创建Req_Challenge包
byte[] reqChallenge = new byte[16];
System.arraycopy(buff, 0, reqChallenge, 0, reqChallenge.length);
reqChallenge[1] = (byte) 1;
return sendSocket(reqChallenge);
}
/**
* 发送认证报文
*
* @param inUsername
* @param inPassword
* @param buff
* @return
* @throws IOException
*/
public byte[] sendReqAuthByChap(String inUsername, String inPassword, byte[] buff, byte[] reqId, byte[] challenge) throws IOException {
byte[] username = inUsername.getBytes();
byte[] password = inPassword.getBytes();
// 生成 Chap 密码
byte[] chapPassword = ChapEncryptUtils.generateChapPassword(reqId, challenge, password);
// 构造认证信息的字节数组
byte[] authBuff = new byte[4 + username.length + 4 + chapPassword.length];
// AttrType: 1 (用户名)
authBuff[0] = (byte) 1;
// AttrLen: 长度为用户名长度+2
authBuff[1] = (byte) (username.length + 2);
// 拷贝用户名
System.arraycopy(username, 0, authBuff, 2, username.length);
// AttrType: 4 (经过 CHAP 加密的密码)
authBuff[2 + username.length] = (byte) 4;
// AttrLen: 长度为密码长度+2
authBuff[3 + username.length] = (byte) (chapPassword.length + 2);
// 拷贝 CHAP 密码
System.arraycopy(chapPassword, 0, authBuff, 4 + username.length, chapPassword.length);
// 创建 Req_Auth 包
byte[] reqAuth = new byte[16 + authBuff.length];
// 给 Req_Auth 包赋值
// 拷贝前16个字节
System.arraycopy(buff, 0, reqAuth, 0, 16);
reqAuth[1] = (byte) 3;
reqAuth[14] = (byte) 0;
short atrAttrNum = 2;
reqAuth[15] = (byte) atrAttrNum;
// 拷贝认证信息
System.arraycopy(authBuff, 0, reqAuth, 16, authBuff.length);
return sendSocket(reqAuth);
}
/**
* 发送认证报文
*
* @param inUsername
* @param inPassword
* @param buff
* @return
* @throws IOException
*/
public byte[] sendReqAuthByPap(String inUsername, String inPassword, byte[] buff) throws IOException {
byte[] username = inUsername.getBytes();
byte[] password = inPassword.getBytes();
// 构造认证信息的字节数组
byte[] authBuff = new byte[4 + username.length + 4 + password.length];
// AttrType: 1 (用户名)
authBuff[0] = (byte) 1;
// AttrLen: 长度为用户名长度+2
authBuff[1] = (byte) (username.length + 2);
// 拷贝用户名
System.arraycopy(username, 0, authBuff, 2, username.length);
// AttrType: 4 (经过 CHAP 加密的密码)
authBuff[2 + username.length] = (byte) 4;
// AttrLen: 长度为密码长度+2
authBuff[3 + username.length] = (byte) (password.length + 2);
// 拷贝 CHAP 密码
System.arraycopy(password, 0, authBuff, 4 + username.length, password.length);
// 创建 Req_Auth 包
byte[] reqAuth = new byte[16 + authBuff.length];
// 给 Req_Auth 包赋值
// 拷贝前16个字节
System.arraycopy(buff, 0, reqAuth, 0, 16);
reqAuth[1] = (byte) 3;
reqAuth[14] = (byte) 0;
short atrAttrNum = 2;
reqAuth[15] = (byte) atrAttrNum;
// 拷贝认证信息
System.arraycopy(authBuff, 0, reqAuth, 16, authBuff.length);
return sendSocket(reqAuth);
}
/**
* 发送报文
*
* @param msgBytes
* @return
* @throws IOException
*/
public byte[] sendSocket(byte[] msgBytes) throws IOException {
log.info("Request Message:{}", Arrays.toString(msgBytes));
DatagramPacket requestPacket;
// 创建发送数据包并发送给服务器
requestPacket = new DatagramPacket(msgBytes, msgBytes.length, InetAddress.getByName(acIp), acPort);
// 设置请求超时3秒
dataSocket.setSoTimeout(timeout);
dataSocket.send(requestPacket);
byte[] ackData = new byte[16];
// 接收服务器的数据包
DatagramPacket receivePacket = new DatagramPacket(ackData, ackData.length);
dataSocket.receive(receivePacket);
byte[] ackAuthData = new byte[receivePacket.getLength()];
System.arraycopy(ackData, 0, ackAuthData, 0, receivePacket.getLength());
return ackAuthData;
}
/**
* 发送报文,不回传
*
* @param msgBytes
* @throws IOException
*/
public void sendSocketNoResponse(byte[] msgBytes) throws IOException {
log.info("Request Message:{}", Arrays.toString(msgBytes));
DatagramPacket requestPacket;
// 创建发送数据包并发送给服务器
requestPacket = new DatagramPacket(msgBytes, msgBytes.length, InetAddress.getByName(acIp), acPort);
// 设置请求超时3秒
dataSocket.setSoTimeout(timeout);
dataSocket.send(requestPacket);
}
/**
* Double to Bytes
*
* @param d
* @return
*/
private byte[] doubleConvertBytes(Double d) {
byte[] output = new byte[8];
long lng = Double.doubleToLongBits(d);
for (int i = 0; i < 8; i++) {
output[i] = (byte) ((lng >> ((7 - i) * 8)) & 0xff);
}
return output;
}
/**
* 初始化报文
*
* @param ip
* @param portalVer
* @param authType
* @param size 因为有时候认证需要截断长度
* @return
*/
public byte[] init(String ip, int portalVer, int authType, int size) {
// 构建portal协议中的字段包
byte[] ver = new byte[1];
byte[] type = new byte[1];
byte[] mod = new byte[1];
byte[] rsvd = new byte[1];
byte[] serialNo = doubleConvertBytes(65535 * Math.random());
byte[] reqID = new byte[2];
byte[] userIP = new byte[4];
byte[] userPort = new byte[2];
byte[] errCode = new byte[1];
byte[] attrNum = new byte[1];
byte[] challenge = new byte[16];
/*
* 给UserIP[]赋值 接收客户ip地址 IP地址压缩成4字节,如果要进一步处理的话,就可以转换成一个int了.
*/
String[] ips = ip.split("[.]");
// 将ip地址加入字段UserIP
for (int i = 0; i < 4; i++) {
int m = Integer.parseInt(ips[i]);
byte b = (byte) m;
userIP[i] = b;
}
ver[0] = (byte) portalVer;
type[0] = (byte) 0;
mod[0] = (byte) authType;
rsvd[0] = (byte) 0;
userPort[0] = (byte) 0;
userPort[1] = (byte) 0;
errCode[0] = (byte) 0;
attrNum[0] = (byte) 0;
// 创建Buff包
byte[] buff = new byte[size];
// 给Buff包赋初始值
buff[0] = ver[0];
buff[1] = type[0];
buff[2] = mod[0];
buff[3] = rsvd[0];
buff[4] = serialNo[0];
buff[5] = serialNo[1];
buff[8] = userIP[0];
buff[9] = userIP[1];
buff[10] = userIP[2];
buff[11] = userIP[3];
buff[12] = userPort[0];
buff[13] = userPort[1];
buff[14] = errCode[0];
buff[15] = attrNum[0];
return buff;
}
/**
* 发送认证成功报文
*
* @param buff
* @return
* @throws IOException
*/
public void sendAffAckReqAuth(byte[] buff) throws IOException {
buff[1] = 7;
buff[14] = 0;
buff[15] = 0;
sendSocketNoResponse(buff);
}
/**
* 下线
*
* @param buff
* @return
* @throws IOException
*/
public byte[] sendLogoutReqAuth(byte[] buff) throws IOException {
buff[1] = 5;
buff[14] = 0;
buff[15] = 0;
return sendSocket(buff);
}
}