writing vpnconnection

This commit is contained in:
free will committed 2021-07-13 20:09:29 +08:00
1 parent 49d12bffcb
commit 87ae0d3c77
4 files changed
+295 -2

No files matched your search

@@ -19,10 +19,17 @@ import android.util.Log;
import androidx.annotation.RequiresApi;
import com.pkusz.min_vpn_client.model.MINVpnAPI;
import com.pkusz.min_vpn_client.utils.AESHelperForConnection;
import com.pkusz.min_vpn_client.utils.IPPackageUtil;
import com.pkusz.min_vpn_client.utils.KeyManager;
import com.pkusz.min_vpn_client.utils.RSACryptoForConnection;
import com.pkusz.min_vpn_client.utils.RuleUtil;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Set;
import common.LoggerHelper;
@@ -31,8 +38,10 @@ import component.Identifier;
import component.IdentifierComponent;
import logicface.LogicFace;
import logicface.LogicFaceException;
import mgmt.RegisterPrefixHelper;
import packet.Data;
import packet.Interest;
import security.KeyChain;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -145,16 +154,108 @@ public class MINVpnConnection implements Runnable{
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public void run() {
// 建立一条连接服务器的TCP-LogicFace
// 1. 建立一条连接服务器的TCP-LogicFace
LogicFace face=initTCPLogicFace(this.mServerName,this.mServerPort);
if(face==null){
LoggerHelper.severe("vpn connection run error: tcp logic face init error");
return;
}
// VPN握手,交换信息
// 2. VPN握手,交换信息
handShake(face);
if(this.mParcelFileDescriptor==null){
LoggerHelper.severe("vpn connection run error: handle shake error");
}
// 3. 进行数据中转操作
transferData(face);
}
/**
* 双线程中转数据
* @param face
* @return
*/
private boolean transferData(LogicFace face){
FileInputStream inputStream=new FileInputStream(
this.mParcelFileDescriptor.getFileDescriptor());
FileOutputStream outputStream=new FileOutputStream(
this.mParcelFileDescriptor.getFileDescriptor());
// -------------- 线程1:从tun网卡读出网络包,并往服务器发 -----------------
ByteBuffer buff1 = ByteBuffer.allocate(MAX_PACKET_SIZE);
ByteBuffer buff2 = ByteBuffer.allocate(MAX_PACKET_SIZE);
Thread recvFromTunAndSendThread = new Thread(() -> {
while (!this.stop) {
buff1.clear();
buff2.clear();
int readLen;
int encryptedLen;
try {
readLen = inputStream.read(buff1.array(), 0, MAX_PACKET_SIZE);
} catch (Exception e) {
e.printStackTrace();
break;
}
if (readLen > 0) {
System.out.println("read from tun , readLen = " + readLen);
buff1.position(0);
buff1.limit(readLen);
//buff2.put(IPPackageUtil.getSrcIpAddress(buff1.array()));
buff2.put(IPPackageUtil.StringAddress2byteAddress(allocatedAddress));
byte[] ipPackageData = new byte[readLen];
System.arraycopy(buff1.array(), 0, ipPackageData, 0, readLen);
byte[] encrytedIPPackage = AESHelperForConnection.encryptWithAES(mSharedSecretString, ipPackageData);
System.out.println("encryptedIPPackage len: " + encrytedIPPackage.length);
System.out.println("buff2 position: " + buff2.position());
buff2.put(encrytedIPPackage);
encryptedLen = buff2.position();
buff2.position(0);
buff2.limit(encryptedLen);
//buff2.flip();
sendInterestPkt(face, buff2);
}
}
});
recvFromTunAndSendThread.start();
try {
RegisterPrefixHelper helper=new RegisterPrefixHelper();
face.registerIdentifier(new Identifier(this.localPrefix),5000,helper);
Interest interest= face.receiveInterest(5000);
System.out.println("recv interest packet, name : " + interest.getName().toString());
System.out.println("兴趣包中装的数据长度 : " + interest.payload.getValue().length);
int dataLen = interest.payload.getValue().length;
ByteBuffer buf2 = ByteBuffer.allocate(dataLen);
buf2.put(interest.payload.getValue());
System.out.println("buf2 data len = " + buf2.position());
byte[] decrpytedIPPackage = new byte[buf2.position()];
System.arraycopy(buf2.array(), 0, decrpytedIPPackage, 0, buf2.position());
ByteBuffer buf3 = ByteBuffer.allocate(5000);
buf3.put(Objects.requireNonNull(AESHelperForConnection.decryptWithAES(mSharedSecretString, decrpytedIPPackage)));
int decryptedDataLen = buf3.position();
byte firstByte = buf3.get(0);
if(firstByte == 0) {
//提示连接超时
mOnTimeoutFailedListener.onTimeout();
} else {
try {
outputStream.write(buf3.array(), 0, decryptedDataLen);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SecurityException | ComponentException | LogicFaceException e) {
e.printStackTrace();
}
return true;
}
/**
@@ -318,10 +419,25 @@ public class MINVpnConnection implements Runnable{
return vpnInterface;
}
// 输出提示信息时使用
private final String getTag() {
return MINVpnConnection.class.getSimpleName() + "[" + mConnectionId + "]";
}
/**
* 根据builder进行规则路由配置
* @param builder
*/
private void configRoute(VpnService.Builder builder) {
String[] routes = RuleUtil.getRules(mService);
for(int i = 0; i < routes.length; i++) {
String route = routes[i];
String[] fields = route.split("/");
//System.out.println(fields[0]);
builder.addRoute(fields[0], Integer.parseInt(fields[1]));
}
}
/**
* 生成一个连接指定服务器地址的TCP LogicFace
* @param hostIP
@@ -0,0 +1,123 @@
package com.pkusz.min_vpn_client.utils;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.security.MessageDigest;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESHelperForConnection {
// AES + Base64 encrypt
@RequiresApi(api = Build.VERSION_CODES.O)
public static String encryptWithAES(String key, String message) {
try {
// Use md5 value as the real key
byte[] b = key.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] keyData = md.digest(b);
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
// Create an 8-byte initialization vector
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f };
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// CBC requires an initialization vector
ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
byte[] plaintext = message.getBytes();
byte[] result = ecipher.doFinal(plaintext, 0, plaintext.length);
Base64.Encoder encoder = Base64.getEncoder();
// return Base64.encodeToString(result, Base64.DEFAULT);
return encoder.encodeToString(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// AES without base64 encrypt
public static byte[] encryptWithAES(String key, byte[] plaintext) {
try {
// Use md5 value as the real key
byte[] b = key.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] keyData = md.digest(b);
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
// Create an 8-byte initialization vector
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f };
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// CBC requires an initialization vector
ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
return ecipher.doFinal(plaintext, 0, plaintext.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// base64 -> AES decrypt
@RequiresApi(api = Build.VERSION_CODES.O)
public static String decryptWithAES(String key, String message) {
try {
// Use md5 value as the real key
byte[] b = key.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] keyData = md.digest(b);
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
// Create an 8-byte initialization vector
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f };
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// CBC requires an initialization vector
dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
Base64.Decoder decoder = Base64.getDecoder();
byte[] messageData = decoder.decode(message);
byte[] result = dcipher.doFinal(messageData, 0, messageData.length);
// System.out.println("result = " + Arrays.toString(result));
return new String(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// AES without base64
public static byte[] decryptWithAES(String key, final byte[] message) {
try {
// Use md5 value as the real key
byte[] b = key.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] keyData = md.digest(b);
SecretKeySpec skey = new SecretKeySpec(keyData, "AES");
// Create an 8-byte initialization vector
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f };
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// CBC requires an initialization vector
dcipher.init(Cipher.DECRYPT_MODE, skey, paramSpec);
return dcipher.doFinal(message, 0, message.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,25 @@
package com.pkusz.min_vpn_client.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
public class IPPackageUtil {
public static byte[] getSrcIpAddress(byte[] ipPackage) {
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
byteBuffer.put(ipPackage, 12, 4);
byteBuffer.flip();
return byteBuffer.array();
}
public static byte[] StringAddress2byteAddress(String address) {
try {
return InetAddress.getByName(address).getAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,29 @@
package com.pkusz.min_vpn_client.utils;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Scanner;
public class RuleUtil {
public static String[] getRules(Context context) {
ArrayList<String> ruleArrayList = new ArrayList<>();
try {
InputStream ruleStream = context.getAssets().open("rulelist.txt");
Scanner scanner = new Scanner(ruleStream, "UTF-8");
while (scanner.hasNextLine()) {
String rule = scanner.nextLine();
if(rule.length() > 0) {
ruleArrayList.add(rule);
}
}
} catch (IOException e) {
e.printStackTrace();
}
String[] rules = (String[]) ruleArrayList.toArray(new String[ruleArrayList.size()]);
return rules;
}
}