diff --git a/src/main/java/VMSConnection/BC_API.java b/src/main/java/VMSConnection/BC_API.java new file mode 100644 index 0000000..84b8f3b --- /dev/null +++ b/src/main/java/VMSConnection/BC_API.java @@ -0,0 +1,137 @@ +package VMSConnection; + +import VMSConnection.Model.LoginRequest; +import VMSConnection.Model.LoginWithSignRequest; +import VMSConnection.Model.RegisterRequest; +import VMSConnection.Model.RegisterWithSignRequest; +import VMSConnection.Security.BC_KeyManager; +import VMSConnection.TCPNet.Conn; +import VMSConnection.TCPNet.Message.BaseStruct; +import VMSConnection.TCPNet.Message.NetworkRequest; +import VMSConnection.TCPNet.Message.NetworkResponse; +import VMSConnection.TCPNet.TCPNet; + +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: VPN注册登录API + * 交互后台:VPN-Management + * @Version: 1.0.0 + * @Date: 16:57 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class BC_API { + public String ip="121.15.171.82"; + public Integer port=12345; + public Integer sslPort=54321; + public String serverKey="04e067d23d3fd3ba4ead731b78346cde1084a83" + + "7760f234621e36a93632b6f6418d4d6b735cd0ee19a8707cec" + + "a12ee4ff8106196ad8122ab2c89a7e11ba00d35b7"; + // 主要结构体 + private TCPNet tcpNet; + private BC_KeyManager keyManager; + + public BC_API(){ + init(); + } + + public BC_API(String ip, Integer port, Integer sslPort, String serverKey) { + this.ip = ip; + this.port = port; + this.sslPort = sslPort; + this.serverKey = serverKey; + init(); + } + + + public BC_API(String ip,Integer port,Integer sslPort){ + this.ip=ip; + this.port=port; + this.sslPort=sslPort; + init(); + } + + private void init(){ + // 初始化keymanager + this.tcpNet=new TCPNet(); + this.keyManager=new BC_KeyManager(); + this.keyManager.GenKeyPair(); + } + + public NetworkResponse login(LoginWithSignRequest request){ + return doRequest(this.ip,this.port,this.sslPort,request,"user","login"); + } + public NetworkResponse login(LoginRequest request){ + return doRequest(this.ip,this.port,this.sslPort,request,"user","login"); + } + + public NetworkResponse register(RegisterWithSignRequest request){ + return doRequest(this.ip,this.port,this.sslPort,request,"user","register"); + } + public NetworkResponse register(RegisterRequest request){ + return doRequest(this.ip,this.port,this.sslPort,request,"user","register"); + } + + private NetworkResponse doRequest(String ip, Integer port, Integer sslPort, + BaseStruct model, String type, String command){ + // 建立连接 + Conn conn=this.tcpNet.Dials(ip,port,ip,sslPort, + this.keyManager,this.serverKey.getBytes(StandardCharsets.UTF_8)); + if(conn==null){ + System.out.println("connection link error: "+ip+":"+port+","+sslPort); + return null; + } + + // 构造请求包 + byte[] params; + try { + params = model.toJson().getBytes(StandardCharsets.UTF_8); + } catch (IllegalAccessException e) { + e.printStackTrace(); + System.out.println("model to json error"); + return null; + } + NetworkRequest networkRequest=new NetworkRequest(type,command,params); + + // 接收应答包 + NetworkResponse networkResponse=sendAndResponse(conn,networkRequest); + try { + System.out.println("receive nr: "+networkResponse.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + // 关闭本次连接 + conn.close(); + return networkResponse; + } + + private NetworkResponse sendAndResponse(Conn conn, NetworkRequest networkRequest){ + // 将请求包转成bytes + byte[] request; + try { + request=networkRequest.toJson().getBytes(StandardCharsets.UTF_8); + } catch (IllegalAccessException e) { + e.printStackTrace(); + System.out.println("request to json error"); + return null; + } + // 发送请求包 + if(!conn.writeV1(request)){ + System.out.println("sendAndResponse: conn write error"); + return null; + } + // 接收应答包 + byte[] res=conn.read(); + if(res.length<=0){ + return null; + } + System.out.println("receive: "+new String(res)); + // 转为java对象 + NetworkResponse networkResponse=new NetworkResponse(); + networkResponse.unJson(res,networkResponse); + + return networkResponse; + } +} diff --git a/src/main/java/VMSConnection/Encoding/BC_Encoder.java b/src/main/java/VMSConnection/Encoding/BC_Encoder.java new file mode 100644 index 0000000..9d307e4 --- /dev/null +++ b/src/main/java/VMSConnection/Encoding/BC_Encoder.java @@ -0,0 +1,76 @@ +package VMSConnection.Encoding; + +import VMSConnection.Utils.ByteHelper; + +import java.io.IOException; +import java.nio.channels.SocketChannel; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 19:47 2021/6/24 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class BC_Encoder { + /** + * Encode 将消息编码: 加一个长度头 + * @param message + * @return + */ + public static byte[] Encode(byte[] message){ + byte[] length= ByteHelper.int32ToByteArray(message.length); + byte[] pkg=new byte[4+ message.length]; + System.arraycopy(length,0,pkg,0,4); + System.arraycopy(message,0,pkg,4,message.length); + return pkg; + } + + /** + * 解码消息:去掉长度头 + * @param channel + * @return + */ + public static byte[] DecodeTcp(SocketChannel channel){ + if(!channel.isConnected()){ + // 重新连接 + try { + System.out.println("DecodeTcp-重新连接: "+channel.getRemoteAddress()); + channel.connect(channel.getRemoteAddress()); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + System.out.println("start read..."); + // 读取消息的长度 + byte[] buf=new byte[4]; + try { + int revL=channel.socket().getInputStream().read(buf); +// channel.read(ByteBuffer.wrap(buf)); + if(revL<0){ + System.out.println("read len < 0"); + return null; + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + int length=ByteHelper.byteArrayToInt32(buf); + System.out.println("read 111: "+length); + // 读取真正的消息数据 + byte[] pkg=new byte[length]; + try { + int revL=channel.socket().getInputStream().read(pkg); + if(revL<0){ + System.out.println("read len < 0"); + return null; + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + System.out.println("read 222: "+new String(pkg)); + return pkg; + } +} diff --git a/src/main/java/VMSConnection/Model/LoginRequest.java b/src/main/java/VMSConnection/Model/LoginRequest.java new file mode 100644 index 0000000..bf1e00c --- /dev/null +++ b/src/main/java/VMSConnection/Model/LoginRequest.java @@ -0,0 +1,22 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:21 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class LoginRequest extends BaseStruct { + public String Name; + public String Password; + public String Certification; + + public LoginRequest(String name, String password, String certification) { + Name = name; + Password = password; + Certification = certification; + } +} diff --git a/src/main/java/VMSConnection/Model/LoginWithSignRequest.java b/src/main/java/VMSConnection/Model/LoginWithSignRequest.java new file mode 100644 index 0000000..d853ef6 --- /dev/null +++ b/src/main/java/VMSConnection/Model/LoginWithSignRequest.java @@ -0,0 +1,24 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:21 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class LoginWithSignRequest extends BaseStruct { + public String Name; + public String Password; + public String Certification; + public byte[] Signature; + + public LoginWithSignRequest(String name, String password, String certification, byte[] signature) { + Name = name; + Password = password; + Certification = certification; + Signature = signature; + } +} diff --git a/src/main/java/VMSConnection/Model/RegisterRequest.java b/src/main/java/VMSConnection/Model/RegisterRequest.java new file mode 100644 index 0000000..90c47fd --- /dev/null +++ b/src/main/java/VMSConnection/Model/RegisterRequest.java @@ -0,0 +1,41 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; + +/* + * @Author: Wang Feng + * @Description: 注册用户时用到的请求信息 + * @Version: 1.0.0 + * @Date: 16:17 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class RegisterRequest extends BaseStruct { + public int ID; + public String Name; + public String Password; + public String Phone; + public String Invitation; + public String Email; + public String DeviceInfo; + public String Certification; + public int IsRevoked; + public int UGroupID; + public String TimeStamp; + + public RegisterRequest(int ID, String name, String password, + String phone, String invitation, String email, + String deviceInfo, String certification, int isRevoked, + int UGroupID, String timeStamp) { + this.ID = ID; + Name = name; + Password = password; + Phone = phone; + Invitation = invitation; + Email = email; + DeviceInfo = deviceInfo; + Certification = certification; + IsRevoked = isRevoked; + this.UGroupID = UGroupID; + TimeStamp = timeStamp; + } +} diff --git a/src/main/java/VMSConnection/Model/RegisterWithSignRequest.java b/src/main/java/VMSConnection/Model/RegisterWithSignRequest.java new file mode 100644 index 0000000..ac30c02 --- /dev/null +++ b/src/main/java/VMSConnection/Model/RegisterWithSignRequest.java @@ -0,0 +1,45 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; + +/* + * @Author: Wang Feng + * @Description: 注册用户时用到的请求信息 + * @Version: 1.0.0 + * @Date: 16:17 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class RegisterWithSignRequest extends BaseStruct { + public int ID; + public String Name; + public String Password; + public String Phone; + public String Invitation; + public String Email; + public String DeviceInfo; + public String Certification; + public String Pubkey; + public int IsRevoked; + public int UGroupID; + public String TimeStamp; + public byte[] Signature; + + public RegisterWithSignRequest(int ID, String name, String password, String phone, + String invitation, String email, String deviceInfo, + String certification, String pubkey,int isRevoked, + int UGroupID, String timeStamp, byte[] signature) { + this.ID = ID; + Name = name; + Password = password; + Phone = phone; + Invitation = invitation; + Email = email; + DeviceInfo = deviceInfo; + Certification = certification; + this.Pubkey=pubkey; + IsRevoked = isRevoked; + this.UGroupID = UGroupID; + TimeStamp = timeStamp; + Signature = signature; + } +} diff --git a/src/main/java/VMSConnection/Security/BC_KeyManager.java b/src/main/java/VMSConnection/Security/BC_KeyManager.java new file mode 100644 index 0000000..acfa9f8 --- /dev/null +++ b/src/main/java/VMSConnection/Security/BC_KeyManager.java @@ -0,0 +1,162 @@ +package VMSConnection.Security; + +import VMSConnection.Security.SM2.SM2KeyPair; +import VMSConnection.Security.SM2.SM2PrivateKey; +import VMSConnection.Security.SM2.SM2PublicKey; +import VMSConnection.Security.SM4.BC_SM4; +import VMSConnection.Utils.Base64Helper; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.InvalidCipherTextException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; + +/* + * @Author: Wang Feng + * @Description: 模仿vpn-management实现keymanager + * 主要功能: + * SM2公钥加密、私钥解密; + * SM2私钥签名、公钥验签; + * SM4加密、解密。 + * @Version: 1.0.0 + * @Date: 21:04 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class BC_KeyManager { + private SM2PrivateKey prk; + private SM2PublicKey pub; + + public BC_KeyManager(){ + prk=new SM2PrivateKey(); + pub=new SM2PublicKey(); + } + + public void GenKeyPair(){ + SM2KeyPair sm2KeyPair=SM2KeyPair.generateKeyPair(); + prk=sm2KeyPair.getSm2PrivateKey(); + pub= sm2KeyPair.getSm2PublicKey(); + } + + /** + * 获取公钥 + * @return + */ + public String GetPubkey(){ + return new String(this.pub.getBytes(), StandardCharsets.UTF_8); + } + + /** + * 获取私钥 + * @return + */ + public String GetPriKey(){ + return new String(this.prk.getBytes(),StandardCharsets.UTF_8); + } + + + public boolean SetPubkey(String data){ + return this.pub.setBytes(data.getBytes(StandardCharsets.UTF_8)); + } + + public boolean SetPrikey(String data){ + return this.prk.setBytes(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 公钥加密 + * @param data + * @return + * @throws InvalidCipherTextException + */ + public byte[] Encrypt(String data) throws InvalidCipherTextException { + return this.pub.encrypt(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 私钥解密 + * @param data + * @return + * @throws InvalidCipherTextException + */ + public byte[] Decrypt(String data) throws InvalidCipherTextException { + return this.prk.decrypt(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 判断公私钥是否匹配 + * @return + */ +// public boolean IsOnCurve(){ +// +// } + + /** + * 私钥签名 + * @param text + * @return + * @throws CryptoException + */ + public String Sign(byte[] text) throws CryptoException { + byte[] sig=this.prk.sign(text); + return Base64Helper.BlockChain_Base64Encode(sig); + } + + /** + * 公钥验签 + * @param text + * @param signature + * @param pubkey + * @return + */ + public boolean Verify(byte[] text,String signature,String pubkey){ + byte[] sig=Base64Helper.BlockChain_Base64Decode(signature); + + SM2PublicKey publicKey=new SM2PublicKey(); + boolean flag=publicKey.setBytes(pubkey.getBytes(StandardCharsets.UTF_8)); + if(!flag){ + return false; + } + + return publicKey.verify(text,sig); + } + + /** + * SM4对称加密 + * @param secretkey + * @param origData + * @return + * @throws NoSuchPaddingException + * @throws IllegalBlockSizeException + * @throws NoSuchAlgorithmException + * @throws BadPaddingException + * @throws InvalidKeyException + * @throws NoSuchProviderException + */ + public static byte[] SM4Encrypt(String secretkey,byte[] origData) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, NoSuchProviderException { + return BC_SM4.encrypt_ECB_Padding(secretkey.getBytes(StandardCharsets.UTF_8),origData); + } + + /** + * SM4对称解密 + * @param secretkey + * @param crypted + * @return + * @throws IllegalBlockSizeException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws NoSuchProviderException + */ + public static byte[] SM4Decrypt(String secretkey,byte[] crypted) throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + System.out.println("sec: "+secretkey); + System.out.println("cry: "+ Arrays.toString(crypted)); + return BC_SM4.decrypt_ECB_Padding(secretkey.getBytes(StandardCharsets.UTF_8),crypted); + } +} diff --git a/src/main/java/VMSConnection/Security/SM2/AsymKeyException.java b/src/main/java/VMSConnection/Security/SM2/AsymKeyException.java new file mode 100644 index 0000000..29db357 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/AsymKeyException.java @@ -0,0 +1,14 @@ +package VMSConnection.Security.SM2; + +/* + * @Author: hongyu guo + * @Description: + * @Version: 1.0.0 + * @Date: 10:51 2021/03/12 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class AsymKeyException extends Exception{ + public AsymKeyException(String msg){ + super(msg); + } +} diff --git a/src/main/java/VMSConnection/Security/SM2/Common.java b/src/main/java/VMSConnection/Security/SM2/Common.java new file mode 100644 index 0000000..8ac675d --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/Common.java @@ -0,0 +1,51 @@ +package VMSConnection.Security.SM2; + +/* + * @Author: hongyu guo + * @Description: 归纳所用常用变量 + * @Version: 1.0.0 + * @Date: 16:40 2021/03/04 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class Common { + public static final String BC_NAME = "BC"; + public static final String X509 = "X.509"; + public static final String ALGO_NAME_EC = "EC"; + + /* Storage */ + public static final int SQLITE = 0; + /* PublicKeyAlgorithm */ + public static final int SM2 = 0; + /* SignatureAlgorithm */ + public static final int SM3withSM2 = 0; + /* SymmetricAlgorithm */ + public static final int SM4ECB = 0; + public static final int SM4CBC = 1; + /* KeyUsage */ + public static final int ContentCommitment = 0; + public static final int DataEncipherment = 1; + public static final int CertSign = 2; + /* tpyeOfCert */ + public static final int InnerCertificate = 0; + public static final int Certificate = 1; + public static final int TbsCertificate = 2; + // public enum Storage { + // SQLITE; + // } + // public enum PublicKeyAlgorithm { + // SM2; + // } + // public enum SignatureAlgorithm { + // SM3withSM2 + // } + // public enum SymmetricAlgorithm { + // SM4ECB, + // SM4CBC; + // } + // public enum KeyUsage { + // ContentCommitment, + // DataEncipherment, + // CertSign; + // } + +} diff --git a/src/main/java/VMSConnection/Security/SM2/PrivateKeyInterface.java b/src/main/java/VMSConnection/Security/SM2/PrivateKeyInterface.java new file mode 100644 index 0000000..4d178c2 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/PrivateKeyInterface.java @@ -0,0 +1,18 @@ +package VMSConnection.Security.SM2; + +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.InvalidCipherTextException; + +/* + * @Author: hongyu guo + * @Description: 所有对称加密方法必须实现的基本方法 + * @Version: 1.0.0 + * @Date: 17:28 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public interface PrivateKeyInterface { + public byte[] getBytes(); + public boolean setBytes(byte[] bytes); + public byte[] decrypt(byte[] cipher) throws InvalidCipherTextException; + public byte[] sign(byte[] content) throws CryptoException; +} diff --git a/src/main/java/VMSConnection/Security/SM2/PublicKeyInterface.java b/src/main/java/VMSConnection/Security/SM2/PublicKeyInterface.java new file mode 100644 index 0000000..8a39e14 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/PublicKeyInterface.java @@ -0,0 +1,17 @@ +package VMSConnection.Security.SM2; +/* + * @Author: hongyu guo + * @Description: 所有对称加密方法必须实现的基本方法 + * @Version: 1.0.0 + * @Date: 17:06 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +import org.bouncycastle.crypto.InvalidCipherTextException; + +public interface PublicKeyInterface { + public byte[] getBytes(); + public boolean setBytes(byte[] bytes); + public byte[] encrypt(byte[] content) throws InvalidCipherTextException; + public boolean verify(byte[] content, byte[] digest); +} \ No newline at end of file diff --git a/src/main/java/VMSConnection/Security/SM2/SM2Base.java b/src/main/java/VMSConnection/Security/SM2/SM2Base.java new file mode 100644 index 0000000..ef386ce --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/SM2Base.java @@ -0,0 +1,219 @@ +package VMSConnection.Security.SM2; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import static VMSConnection.Security.SM2.Common.ALGO_NAME_EC; + +/* + * @Author: hongyu guo + * @Description: SM2推荐曲线参数 + * @Version: 1.0.0 + * @Date: 18:48 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class SM2Base { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + +// private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SM2Base.class); + + /* + * 以下为SM2推荐曲线参数 + */ + public static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); + public final static BigInteger SM2_ECC_P = CURVE.getQ(); + public final static BigInteger SM2_ECC_A = CURVE.getA().toBigInteger(); + public final static BigInteger SM2_ECC_B = CURVE.getB().toBigInteger(); + public final static BigInteger SM2_ECC_N = CURVE.getOrder(); + public final static BigInteger SM2_ECC_H = CURVE.getCofactor(); + public final static BigInteger SM2_ECC_GX = new BigInteger( + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + public final static BigInteger SM2_ECC_GY = new BigInteger( + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); + public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, + SM2_ECC_N, SM2_ECC_H); + + private static final String PEM_STRING_PUBLIC = "PUBLIC KEY"; + private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY"; + + + /** + * 生成BC自定义的SM2公私钥 + * @param + * @return org.bouncycastle.crypto.AsymmetricCipherKeyPair + * @author hongyu guo + * @date 2021/3/7 + **/ + public static AsymmetricCipherKeyPair generateKeyPairParameter(){ + SecureRandom random = new SecureRandom(); + ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(DOMAIN_PARAMS, + random); + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + keyGen.init(keyGenerationParams); + AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair(); + return keyPair; + } + + /** + * 生成java.security.KeyPair类型的SM2公私钥 + * @param + * @return java.security.KeyPair + * @author hongyu guo + * @date 2021/3/7 + **/ + public static KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + SecureRandom random = new SecureRandom(); + return generateKeyPair(DOMAIN_PARAMS, random); + } + + /** + * 生成java.security.KeyPair类型的SM2公私钥, 需自定义曲线参数 + * @param domainParameters + * @param random + * @return + * @author hongyu guo + * @date 2021/3/7 + **/ + public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) + throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); + ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(), + domainParameters.getN(), domainParameters.getH()); + kpg.initialize(parameterSpec, random); + return kpg.generateKeyPair(); + } + + /** + * BC公私钥转换为java.security.KeyPair公私钥 + * @param bcKeyPair + * @return + * @author hongyu guo + * @date 2021/3/7 + **/ + public static KeyPair convertBcToJceKeyPair(AsymmetricCipherKeyPair bcKeyPair) throws Exception { + byte[] pkcs8Encoded = PrivateKeyInfoFactory.createPrivateKeyInfo(bcKeyPair.getPrivate()).getEncoded(); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pkcs8Encoded); + byte[] spkiEncoded = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(bcKeyPair.getPublic()).getEncoded(); + X509EncodedKeySpec spkiKeySpec = new X509EncodedKeySpec(spkiEncoded); + KeyFactory keyFac = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); + return new KeyPair(keyFac.generatePublic(spkiKeySpec), keyFac.generatePrivate(pkcs8KeySpec)); + } + + /** + * java.security.KeyPair转换为BC类型的公私钥对 + * @param keyPair + * @return + * @author hongyu guo + * @date 2021/3/7 + **/ + public static AsymmetricCipherKeyPair convertKeyPair2Bc(KeyPair keyPair) { + ECPrivateKeyParameters priKey = convertPrivateKeyToParameters((BCECPrivateKey) keyPair.getPrivate()); + ECPublicKeyParameters pubKey = convertPublicKeyToParameters((BCECPublicKey) keyPair.getPublic()); + return new AsymmetricCipherKeyPair(priKey,pubKey); + } + + /** + * BCECPrivateKey类型私钥转化为ECPrivateKeyParameters类型的私钥, + * 调用者若想将java.security.PrivateKey转化为ECPrivateKeyParameters, 调用时将其强制转换为BCECPrivateKey类型 + * @param ecPriKey + * @return ECPrivateKeyParameters + * @author hongyu guo + * @date 2021/3/7 + **/ + public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) { + ECParameterSpec parameterSpec = ecPriKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters); + } + + /** + * BCECPublicKey类型私钥转化为ECPublicKeyParameters类型的私钥, + * 调用者若想将java.security.PublicKey转化为ECPublicKeyParameters, 调用时将其强制转换为BCECPublicKey类型 + * @param ecPubKey + * @return ECPrivateKeyParameters + * @author hongyu guo + * @date 2021/3/7 + **/ + public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) { + ECParameterSpec parameterSpec = ecPubKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters); + } + /** + * 生成一对sm2公私钥 + * @param + * @return minsecurity.crypto.sm2.SM2KeyPair + * @throws + * @author hongyu guo + * @date 2021/3/15 + **/ + public static SM2KeyPair generateSM2KeyPair(){ + return SM2KeyPair.generateKeyPair(); + } + + /** + * SM2字节数组->16进制字节数组 辅助函数 + * @param bytes byte[] + * @return byte[] + * @throws + * @author zhengqi wu + * @date 2021/4/12 + **/ + public static byte[] byteArraytoHexByteArray(byte[] bytes){ + // 16进制表示法 +// StringBuilder sb = new StringBuilder(); +// for(int i=0;i < bytes.length;i++) { +// sb.append(String.format("%02x", bytes[i])); +// } + String hexStr = ByteUtils.toHexString(bytes); + return hexStr.getBytes(); + } + + /** + * 16进制字节数组->SM2字节数组 辅助函数 + * @param bytes byte[] + * @return byte[] + * @throws + * @author zhengqi wu + * @date 2021/4/12 + **/ + public static byte[] hexByteArrayToByteArray(byte[] bytes){ + String hex = new String(bytes); + if (hex.length() < 1) { + return null; + } else { + byte[] result = new byte[hex.length() / 2]; + int j = 0; + for(int i = 0; i < hex.length(); i+=2) { + result[j++] = (byte)Integer.parseInt(hex.substring(i,i+2), 16); + } + return result; + } + } +} diff --git a/src/main/java/VMSConnection/Security/SM2/SM2Exception.java b/src/main/java/VMSConnection/Security/SM2/SM2Exception.java new file mode 100644 index 0000000..c443d83 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/SM2Exception.java @@ -0,0 +1,14 @@ +package VMSConnection.Security.SM2; + +/* + * @Author: hongyu guo + * @Description: sm2的自定义异常 + * @Version: 1.0.0 + * @Date: 19:00 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class SM2Exception extends AsymKeyException { + public SM2Exception(String msg){ + super(msg); + } +} diff --git a/src/main/java/VMSConnection/Security/SM2/SM2KeyPair.java b/src/main/java/VMSConnection/Security/SM2/SM2KeyPair.java new file mode 100644 index 0000000..0b387b1 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/SM2KeyPair.java @@ -0,0 +1,41 @@ +package VMSConnection.Security.SM2; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; + +/* + * @Author: hongyu guo + * @Description: + * @Version: 1.0.0 + * @Date: 09:57 2021/03/11 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class SM2KeyPair { + private final SM2PublicKey sm2PublicKey; + private final SM2PrivateKey sm2PrivateKey; + + public SM2KeyPair(SM2PublicKey sm2PublicKey, SM2PrivateKey sm2PrivateKey) { + this.sm2PublicKey = sm2PublicKey; + this.sm2PrivateKey = sm2PrivateKey; + } + + public static SM2KeyPair generateKeyPair(){ + AsymmetricCipherKeyPair keyPair = SM2Base.generateKeyPairParameter(); + ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate(); + ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic(); + byte[] d = priKey.getD().toByteArray(); + byte[] x = pubKey.getQ().getAffineXCoord().getEncoded(); + byte[] y = pubKey.getQ().getAffineYCoord().getEncoded(); + return new SM2KeyPair(new SM2PublicKey(x,y), new SM2PrivateKey(d)); + } + + + public SM2PublicKey getSm2PublicKey() { + return sm2PublicKey; + } + + public SM2PrivateKey getSm2PrivateKey() { + return sm2PrivateKey; + } +} diff --git a/src/main/java/VMSConnection/Security/SM2/SM2PrivateKey.java b/src/main/java/VMSConnection/Security/SM2/SM2PrivateKey.java new file mode 100644 index 0000000..272748f --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/SM2PrivateKey.java @@ -0,0 +1,111 @@ +package VMSConnection.Security.SM2; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.SM2Signer; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/* + * @Author: hongyu guo + * @Description: 国密sm2 非对称加密私钥 + * @Version: 1.0.0 + * @Date: 19:40 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class SM2PrivateKey implements PrivateKeyInterface { + + public SM2PrivateKey(){} + + /* + * @param d + * @return + * @author hongyu guo + * @date 2021/3/7 + **/ + + /* + * @param d 大数 + * @return + * @author hongyu guo + * @date 2021/3/7 + **/ + public SM2PrivateKey(byte[] d) { + privateKey = new ECPrivateKeyParameters(new BigInteger(d), SM2Base.DOMAIN_PARAMS); + } + public SM2PrivateKey(byte[] d, ECDomainParameters parameters){ + if(d.length == 32 || d.length == 33) + privateKey = new ECPrivateKeyParameters(new BigInteger(d), parameters); + else { + try { + throw new SM2Exception("SM2私钥中,参数d长度应为32/33"); + } catch (SM2Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public byte[] getBytes() { + byte[] bytes = privateKey.getD().toByteArray(); + return SM2Base.byteArraytoHexByteArray(bytes); + } + + @Override +// public boolean setBytes(byte[] d) { +// // TODO 待与go版本进行兼容 目前版本无法使用 +// if(d.length != 32 && d.length != 33) +// return false; +// privateKey = new ECPrivateKeyParameters(new BigInteger(d), SM2Base.DOMAIN_PARAMS); +// return true; +// } + + /** + * @description 与external_amd64同步 + * @params d {byte[]} + * @return boolean + */ + // TODO 实现不同 + public boolean setBytes(byte[] d){ + if (d.length != 64 && d.length != 66 && d.length != 32 && d.length != 33) + return false; + switch (d.length){ + case 32: + case 33: + privateKey = new ECPrivateKeyParameters(new BigInteger(d), SM2Base.DOMAIN_PARAMS); + break; + case 64: + case 66: + d = SM2Base.hexByteArrayToByteArray(d); + privateKey = new ECPrivateKeyParameters(new BigInteger(d), SM2Base.DOMAIN_PARAMS); + break; + } + return true; + } + + @Override + public byte[] decrypt(byte[] cipher) throws InvalidCipherTextException { + SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C2C3); + engine.init(false, privateKey); + return engine.processBlock(cipher, 0, cipher.length); + } + + @Override + public byte[] sign(byte[] content) throws CryptoException { + SM2Signer signer = new SM2Signer(); + CipherParameters param = null; + ParametersWithRandom pwr = new ParametersWithRandom(privateKey, new SecureRandom()); + param = pwr; + signer.init(true, param); + signer.update(content, 0, content.length); + return signer.generateSignature(); + } + + private ECPrivateKeyParameters privateKey; +} diff --git a/src/main/java/VMSConnection/Security/SM2/SM2PublicKey.java b/src/main/java/VMSConnection/Security/SM2/SM2PublicKey.java new file mode 100644 index 0000000..b5f2406 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM2/SM2PublicKey.java @@ -0,0 +1,148 @@ +package VMSConnection.Security.SM2; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.SM2Signer; +import org.bouncycastle.math.ec.ECCurve; + +import java.nio.ByteBuffer; +import java.security.SecureRandom; + +/* + * @Author: hongyu guo + * @Description: 国密sm2 非对称加密公钥, 实现PublicKeyInterface中的方法 + * @Version: 1.0.0 + * @Date: 17:32 2021/03/03 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class SM2PublicKey implements PublicKeyInterface { + + public SM2PublicKey(){} + + public SM2PublicKey(byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters){ + publicKey = createPublicKey(xBytes,yBytes,curve,domainParameters); + } + /** + * 使用SM2推荐曲线参数 + * @param xBytes 曲线x参数 + * @param yBytes 曲线y参数 + * @return + * @author ghy + * @date 2021/3/7 + **/ + public SM2PublicKey(byte[] xBytes, byte[] yBytes){ + this(xBytes,yBytes, SM2Base.CURVE, SM2Base.DOMAIN_PARAMS); + } + + public SM2PublicKey(byte[] bytes){ + setBytes(bytes); + } + + + + @Override + public byte[] getBytes() { + if(publicKey != null){ + byte[] bytes = publicKey.getQ().getEncoded(false); //不压缩 + byte[] basic = SM2Base.byteArraytoHexByteArray(bytes); + return basic; + } + return null; + } + + @Override + public boolean setBytes(byte[] bytes) { + switch (bytes.length){ + case 65: + createPub(bytes); + break; + case 130: + bytes = SM2Base.hexByteArrayToByteArray(bytes); // 16进制表示法转为十进制表示法 + createPub(bytes); + break; + default: + return false; + } + return true; + } + + private void createPub(byte[] bytes){ + ByteBuffer encoded = ByteBuffer.wrap(bytes); + byte[] x = new byte[32]; + byte[] y = new byte[32]; + encoded.position(1); + encoded.get(x,0,32); + encoded.get(y,0,32); + publicKey = createPublicKey(x,y); + } + + /** + * @param content + * @return byte[] 密文 + * @author ghy + * @date 2021/3/7 + **/ + @Override + public byte[] encrypt(byte[] content) throws InvalidCipherTextException { + SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C2C3); + ParametersWithRandom pwr = new ParametersWithRandom(publicKey, new SecureRandom()); + engine.init(true, pwr); + return engine.processBlock(content, 0, content.length); + } + + @Override + public boolean verify(byte[] content, byte[] digest) { + SM2Signer signer = new SM2Signer(); + CipherParameters param = publicKey; + signer.init(false, param); + signer.update(content, 0, content.length); + return signer.verifySignature(digest); + } + + + ////////////////////////////////////////////// private start ////////////////////////////////////////////// + private static ECPublicKeyParameters createPublicKey( + byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters){ + final byte uncompressedFlag = 0x04; + int curveLength = getCurveLength(domainParameters); + xBytes = fixToCurveLengthBytes(curveLength, xBytes); + yBytes = fixToCurveLengthBytes(curveLength, yBytes); + byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length]; + encodedPubKey[0] = uncompressedFlag; + System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length); + System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length); + return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters); + } + + private static ECPublicKeyParameters createPublicKey(byte[] xBytes, byte[] yBytes){ + return createPublicKey(xBytes,yBytes, SM2Base.CURVE, SM2Base.DOMAIN_PARAMS); + } + + private static int getCurveLength(ECDomainParameters domainParams) { + return (domainParams.getCurve().getFieldSize() + 7) / 8; + } + + private static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) { + if (src.length == curveLength) { + return src; + } + + byte[] result = new byte[curveLength]; + if (src.length > curveLength) { + System.arraycopy(src, src.length - result.length, result, 0, result.length); + } else { + System.arraycopy(src, 0, result, result.length - src.length, src.length); + } + return result; + } + + + + private ECPublicKeyParameters publicKey; + ////////////////////////////////////////////// private end ////////////////////////////////////////////// + +} diff --git a/src/main/java/VMSConnection/Security/SM4/BC_SM4.java b/src/main/java/VMSConnection/Security/SM4/BC_SM4.java new file mode 100644 index 0000000..6bf6913 --- /dev/null +++ b/src/main/java/VMSConnection/Security/SM4/BC_SM4.java @@ -0,0 +1,128 @@ +package VMSConnection.Security.SM4; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; + +/* + * @Author: hongyu guo + * @Description: sm4算法 + * @Version: 1.0.0 + * @Date: 16:26 2021/03/04 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class BC_SM4 { + static { + Security.addProvider(new BouncyCastleProvider()); + } + public static final String ALGORITHM_NAME = "SM4"; + public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding"; + public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding"; + public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding"; + public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding"; + + /** + * SM4算法目前只支持128位(即密钥16字节) + */ + public static final int DEFAULT_KEY_SIZE = 128; + + /** + * 生成16字节的sm4 key + * @param + * @return byte[] + * @throws NoSuchAlgorithmException + * @author hongyu guo + * @date 2021/3/15 + **/ + public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { + return generateKey(DEFAULT_KEY_SIZE); + } + + public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); + kg.init(keySize, new SecureRandom()); + return kg.generateKey().getEncoded(); + } + + public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key) + throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidKeyException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + cipher.init(mode, sm4Key); + return cipher; + } + + private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv) + throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + cipher.init(mode, sm4Key, ivParameterSpec); + return cipher; + } +} diff --git a/src/main/java/VMSConnection/TCPNet/Conn.java b/src/main/java/VMSConnection/TCPNet/Conn.java new file mode 100644 index 0000000..3daac09 --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/Conn.java @@ -0,0 +1,20 @@ +package VMSConnection.TCPNet; + +import java.nio.channels.SocketChannel; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 20:20 2021/6/24 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public interface Conn { + byte[] read(); + byte[] readSSL(); + boolean writeV1(byte[] data); + boolean writeV2(byte[] data); + String getRemote(); + void close(); + SocketChannel getConn(); +} diff --git a/src/main/java/VMSConnection/TCPNet/Message/BaseStruct.java b/src/main/java/VMSConnection/TCPNet/Message/BaseStruct.java new file mode 100644 index 0000000..8f91ae3 --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/Message/BaseStruct.java @@ -0,0 +1,78 @@ +package VMSConnection.TCPNet.Message; + +import com.alibaba.fastjson.JSONObject; + +import java.lang.reflect.Field; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:26 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class BaseStruct { + /** + * 将java对象转为json字符串 + * 结构体中有byte[]字段时,已加入相应base64编码处理 + * @return + * @throws IllegalAccessException + */ + public String toJson() throws IllegalAccessException { + JSONObject jsonObject=new JSONObject(); + // 遍历属性值 + Class cls=this.getClass(); + Field[] fields=cls.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field f=fields[i]; + f.setAccessible(true); + // 将属性值的名字的属性值的值放入jsonObject +// if(f.getType()==(byte[].class)){ +// // byte[]需要编码base64 +// byte[] rawS=(byte[])(f.get(this)); +// String encodeS=Base64Helper.BlockChain_Base64Encode(rawS); +// jsonObject.put(f.getName(),encodeS); +// }else { + jsonObject.put(f.getName(), f.get(this)); +// } + } + return jsonObject.toJSONString(); + } + + /** + * 将json转为指定对象的一个实例 + * 结构体中有byte[]字段时,parseObject将自动进行base64解码处理 + * @param data + * @param thisObj + */ + public void unJson(byte[] data, Object thisObj) { + Object newObj=JSONObject.parseObject(new String(data), thisObj.getClass()); + try { + Class thiscls = thisObj.getClass(); + Class cls = newObj.getClass(); + Field[] fields = cls.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field f = fields[i]; + f.setAccessible(true); + // 将属性值的名字的属性值的值放入this中 + Field thisf = thiscls.getDeclaredField(f.getName()); + thisf.setAccessible(true); + thisf.set(this, f.get(newObj)); + } + } catch (IllegalAccessException | NoSuchFieldException e) { + e.printStackTrace(); + } + } + + +// if(thisf.getType()==(byte[].class)){ +// // byte[]需要解码base64 +// byte[] base64Bytes=(byte[]) (f.get(newObj)); +// System.out.println(Arrays.toString(base64Bytes)); +// String base64S=new String(base64Bytes); +// System.out.println(base64S); +// byte[] decodeBytes= Base64Helper.BlockChain_Base64Decode_GoLanUn(base64S); +// thisf.set(this, decodeBytes); +// }else { +// } +} diff --git a/src/main/java/VMSConnection/TCPNet/Message/NetworkRequest.java b/src/main/java/VMSConnection/TCPNet/Message/NetworkRequest.java new file mode 100644 index 0000000..88e8734 --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/Message/NetworkRequest.java @@ -0,0 +1,24 @@ +package VMSConnection.TCPNet.Message; + +import com.alibaba.fastjson.JSONObject; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 11:37 2021/7/12 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class NetworkRequest extends BaseStruct{ + public String Method=""; + public String Type=""; + public String Command=""; + public JSONObject Header=new JSONObject(); // 暂时用不到 + public byte[] Parameters; + + public NetworkRequest(String type,String command,byte[] parameters){ + this.Type=type; + this.Command=command; + this.Parameters=parameters; + } +} diff --git a/src/main/java/VMSConnection/TCPNet/Message/NetworkResponse.java b/src/main/java/VMSConnection/TCPNet/Message/NetworkResponse.java new file mode 100644 index 0000000..3d81fa2 --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/Message/NetworkResponse.java @@ -0,0 +1,17 @@ +package VMSConnection.TCPNet.Message; + +import com.alibaba.fastjson.JSONObject; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:56 2021/7/9 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class NetworkResponse extends BaseStruct{ + public int Code=0; + public JSONObject Header=new JSONObject(); // 暂时用不到 + public byte[] Data; + public String ErrorMsg=""; +} diff --git a/src/main/java/VMSConnection/TCPNet/SSL.java b/src/main/java/VMSConnection/TCPNet/SSL.java new file mode 100644 index 0000000..b2221fc --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/SSL.java @@ -0,0 +1,200 @@ +package VMSConnection.TCPNet; + +import VMSConnection.Security.BC_KeyManager; +import VMSConnection.TCPNet.Message.BaseStruct; +import org.bouncycastle.crypto.InvalidCipherTextException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.ArrayList; +import java.util.Random; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 20:50 2021/6/24 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +class SSL_SetupMessage extends BaseStruct { + public String Pubkey; + public String Secretkey; +} + +class SSL_Message extends BaseStruct { + public int IsEncrypted=0; + public int MType=0; + public byte[] Data; + public String Pubkey=""; + public int Code=0; +} + +class SSL_RespMessage extends BaseStruct { + public int Code; +} + +public class SSL { + // 是否加密 + public static final int encrypted=0; + public static final int not_encrypted=1; + // SSL_Message类型 + public static final int normal=0; + public static final int setup=1; + public static final int resp=2; + // SSL_RespMessage类型 + public static final int success=200; + public static final int bad_request=400; + public static final int not_authorized=403; + public static final int server_error=500; + + /** + * 协商对称密钥 + * @param conn + * @param pubKey 用户公钥 + * @param key 无用参数,内含pubKey + * @param serverKey 服务器公钥 + * @return 协商成功的对称密钥 + */ + public static String sslSetup(Conn conn, String pubKey, + BC_KeyManager key,BC_KeyManager serverKey){ + // 取一个十六位的密钥 + String secretKey=String.format("%016d", + Math.abs(new Random(System.currentTimeMillis()).nextLong())) + .substring(0,16); + // 构造SSL_SetupMessage(公钥&密钥) + SSL_SetupMessage sm=new SSL_SetupMessage(); + sm.Pubkey=pubKey; + sm.Secretkey=secretKey; + String data; + try { + data=sm.toJson(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + // 用公钥去加密它 + byte[] encData; + try { + encData=serverKey.Encrypt(data); + } catch (InvalidCipherTextException e) { + e.printStackTrace(); + return null; + } + + // 构造SSL_Message + SSL_Message sslMessage=new SSL_Message(); + sslMessage.IsEncrypted=encrypted; + sslMessage.MType=setup; + sslMessage.Data =encData; + String ms=null; + try { + ms=sslMessage.toJson(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + + System.out.println("test 000: mesdata: "+new String(ms.getBytes(StandardCharsets.UTF_8))); + + // 将SSL_Message写给服务器(内含:用户公钥、用户对称密钥) + if(!conn.writeV2(ms.getBytes(StandardCharsets.UTF_8))){ + System.out.println("SSL write error"); + return null; + } + + System.out.println("test 111"); + + // 读取服务器应答信息 + byte[] resData=conn.readSSL(); + if(resData==null){ + System.out.println("SSL readSSL error"); + return null; + } + + System.out.println("test 222: "+new String(resData)); + System.out.println("secretkey: "+secretKey); + // 用对称密钥解密应答信息 + byte[] decRes; + try { + // todo:这里sm4有问题 +// decRes="{\"Code\":200}".getBytes(StandardCharsets.UTF_8); + decRes=BC_KeyManager.SM4Decrypt(secretKey,resData); + } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchProviderException + | NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException e) { + e.printStackTrace(); + return null; + } + + // 将解密信息转为Java对象 + SSL_RespMessage resp=new SSL_RespMessage(); + resp.unJson(decRes,resp); + + // 判断应答信息的code + if(resp.Code!=success){ + System.out.println("SSL setup failed, the server doesn't accept the request, " + + "and the status code is "+resp.Code); + return null; + } + + return secretKey; + } + + /** + * 使用密钥解密数据 + * @param data + * @param role + * @param secretKey + * @return 泛型数组,依次存储:string(对称密钥) int(是否加密) int(code) byte[](解密数据) + */ + public static ArrayList sslDePacket(byte[] data, int role, String secretKey){ + ArrayList result=new ArrayList(); + result.add(""); + result.add(not_encrypted); + result.add(400); + result.add(null); + // json转java对象 + SSL_Message res=new SSL_Message(); + res.unJson(data,res); + // 没有加密直接返回 200 和源数据res.Data + if(res.IsEncrypted==not_encrypted){ + return result; + }else if(res.IsEncrypted==encrypted) { + // 加密 判断role + if(role==0){ + // todo:根据解析出来的数据中的公钥ID从本地MAP获取公钥 对数据SM4解密 + // todo: 这里是server应该事先的处理逻辑 + System.out.println("sslDePacket error: role==0"); + return result; + }else{ + // 直接使用传入的密钥 对数据SM4解密 + if(secretKey.equals("")){ + System.out.println("sslDePacket error: secretKey == \"\""); + return result; + } + byte[] ans; + try { + ans= BC_KeyManager.SM4Decrypt(secretKey,res.Data); + } catch (IllegalBlockSizeException | NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException e) { + e.printStackTrace(); + return result; + } + if(ans==null){ + System.out.println("SSL depacket failed, the secretKey can't use to decrypt the data"); + return result; + } + result.set(1,encrypted); + result.set(2,200); + result.set(3,ans); + return result; + } + }else{ + return result; + } + } +} diff --git a/src/main/java/VMSConnection/TCPNet/TCPConn.java b/src/main/java/VMSConnection/TCPNet/TCPConn.java new file mode 100644 index 0000000..7b8af3b --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/TCPConn.java @@ -0,0 +1,237 @@ +package VMSConnection.TCPNet; + +import VMSConnection.Encoding.BC_Encoder; +import VMSConnection.Security.BC_KeyManager; +import VMSConnection.Utils.ByteHelper; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.ArrayList; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 20:19 2021/6/24 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TCPConn implements Conn{ + public SocketChannel socketChannel; // 网络连接 + public boolean isEncrypt; // 是否加密 + public BC_KeyManager key; // 密钥管理(用户公钥 & 私钥) + public BC_KeyManager serverKey; //just pubkey can be used。服务器公钥 + public String secretKey; // 密钥 + public int role; // 指示是使用传入数据中的公钥ID从本地取出密钥解密 还是直接传入secretKey + + public TCPConn(){ + } + + public TCPConn(SocketChannel socket,BC_KeyManager key,int role){ + this.socketChannel =socket; + this.key=key; + this.role=role; + } + + public TCPConn(SocketChannel socket,BC_KeyManager key, + BC_KeyManager serverKey,boolean isEncrypt, + String secretKey,int role){ + this.socketChannel =socket; + this.key=key; + this.serverKey=serverKey; + this.isEncrypt=isEncrypt; + this.secretKey=secretKey; + this.role=role; + } + + public TCPConn(SocketChannel socket,BC_KeyManager key, + BC_KeyManager serverKey, + String secretKey,int role){ + this.socketChannel =socket; + this.key=key; + this.serverKey=serverKey; + this.isEncrypt=true; + this.secretKey=secretKey; + this.role=role; + } + + @Override + public byte[] read() { + // 四个字节存储大小 防粘包 + byte[] data= BC_Encoder.DecodeTcp(this.socketChannel); + if(data==null){ + System.out.println("Error read normal message failed..."); + return null; + } + // 使用密钥解密数据 + ArrayList res=SSL.sslDePacket(data,this.role,this.secretKey); + if(res.get(3)==null){ + System.out.println("read error: ssl depacket"); + return null; + } + // 如果role == 0说明是非对称加密 得到密钥 加密方式变成encrypted + if(this.role==0){ + this.isEncrypt=true; + this.secretKey=(String)res.get(0); + } + // 返回得到的数据 + return (byte[]) res.get(3); + } + + @Override + public byte[] readSSL() { + // 四个字节存储大小 防粘包 + byte[] data= BC_Encoder.DecodeTcp(this.socketChannel); + if(data==null){ + System.out.println("SSL received message, but decode failed..."); + return null; + } + + // 使用密钥解密数据 + SSL_Message req=new SSL_Message(); + req.unJson(data,req); + System.out.println("readssl mtype: "+req.MType); + System.out.println("readssl data: "+new String(req.Data)); + if(req.MType==SSL.setup){ + // todo: 这里是server应该实现的处理逻辑 + System.out.println("readSSL error: mtype==setup"); + return null; + }else if(req.MType==SSL.resp){ + return req.Data; + }else{ + return null; + } + } + + /** + * 将数据包装成ssl_message,再发出去 + * @param data + * @return + */ + @Override + public boolean writeV1(byte[] data) { + SSL_Message mes=new SSL_Message(); + if(!this.isEncrypt){ + // 不加密 + mes.IsEncrypted=SSL.not_encrypted; + mes.Data=data; + mes.MType=SSL.normal; + mes.Code=200; + try { + return this.writeV2(mes.toJson().getBytes(StandardCharsets.UTF_8)); + } catch (IllegalAccessException e) { + System.out.println("writev1 error: write v2 1"); + return false; + } + }else { + // 加密 + mes.IsEncrypted=SSL.encrypted; + mes.MType=SSL.normal; + if(this.secretKey.equals("")){ + mes.Code=500; + try { + return this.writeV2(mes.toJson().getBytes(StandardCharsets.UTF_8)); + } catch (IllegalAccessException e) { + System.out.println("writev1 error: write v2 2 " + +"SSL depacket failed, the secretKey doesn't exist"); + return false; + } + } + byte[] ans = null; + try { + ans = BC_KeyManager.SM4Encrypt(this.secretKey,data); + } catch (NoSuchPaddingException | NoSuchProviderException | InvalidKeyException | BadPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException e) { + e.printStackTrace(); + mes.Code=500; + try { + return this.writeV2(mes.toJson().getBytes(StandardCharsets.UTF_8)); + } catch (IllegalAccessException ee) { + System.out.println("writev1 error: write v2 3 " + +ee.getMessage()); + } + return false; + } + if(ans==null){ + mes.Code=500; + try { + return this.writeV2(mes.toJson().getBytes(StandardCharsets.UTF_8)); + } catch (IllegalAccessException e) { + System.out.println("writev1 error: write v2 4 " + +"SSL depacket failed, the secretKey can't use to decrypt the data " + +e.getMessage()); + } + return false; + } + mes.Data=ans; + if(this.role==1){ + mes.Pubkey=this.key.GetPubkey(); + } + mes.Code=200; + try { + return this.writeV2(mes.toJson().getBytes(StandardCharsets.UTF_8)); + } catch (IllegalAccessException e) { + System.out.println("writev1 error: write v2 5 "+e.getMessage()); + return false; + } + } + } + + /** + * 加一个TCP-len头部,再发出去 + * @param data + * @return + */ + @Override + public boolean writeV2(byte[] data) { + byte[] newdata=BC_Encoder.Encode(data); + byte[] a=ByteHelper.getLenBytes(newdata,0,4); + System.out.println("newdata: "+ ByteHelper.uint32ToLong(a)); + System.out.println("newdata: "+newdata.length+" ,"+new String(newdata)); + try { + if(!this.socketChannel.isConnected()){ + // 重新连接 + System.out.println("writeV2-重新连接: "+this.socketChannel.getRemoteAddress()); + this.socketChannel.connect(this.socketChannel.getRemoteAddress()); + } + int len=this.socketChannel.write(ByteBuffer.wrap(newdata)); + System.out.println("write len: "+newdata.length); + if(len<=0){ + return false; + } + } catch (IOException e) { + return false; + } + return true; + } + + @Override + public String getRemote() { + try { + return this.socketChannel.getRemoteAddress().toString(); + } catch (IOException e) { + System.out.println("get remote error: "+e.getMessage()); + return null; + } + } + + @Override + public void close() { + try { + this.socketChannel.close(); + } catch (IOException e) { + System.out.println("channel close error: "+e.getMessage()); + } + } + + @Override + public SocketChannel getConn() { + return this.socketChannel; + } +} diff --git a/src/main/java/VMSConnection/TCPNet/TCPNet.java b/src/main/java/VMSConnection/TCPNet/TCPNet.java new file mode 100644 index 0000000..33cea58 --- /dev/null +++ b/src/main/java/VMSConnection/TCPNet/TCPNet.java @@ -0,0 +1,75 @@ +package VMSConnection.TCPNet; + +import VMSConnection.Security.BC_KeyManager; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 19:17 2021/6/23 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TCPNet { + public SocketChannel socketChannel; // 网络套接字 + public BC_KeyManager key; // 密钥管理 + public BC_KeyManager serverKey; //just pubkey can be used + + public Conn Dials(String sslHost,Integer sslPort){ + // 1. 尝试连接TCP + SocketChannel sk= null; + try { + sk = SocketChannel.open(); + sk.connect(new InetSocketAddress(sslHost,sslPort)); + // 设置超时时间 + sk.socket().setKeepAlive(true); + sk.socket().setSoTimeout(5000); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + // 2. 创建一个conn,并返回 + return new TCPConn(sk, this.key,1); + } + + public Conn Dials(String host,Integer port,String sslHost,Integer sslPort, + BC_KeyManager key,byte[] serverPubKey){ + // 2. 设置密钥serverKey + this.key=key; + BC_KeyManager serverK=new BC_KeyManager(); + serverK.SetPubkey(new String(serverPubKey, StandardCharsets.UTF_8)); + // 3. 尝试连接ssl,并获取密钥secretKey + Conn sslConn=this.Dials(sslHost,sslPort); + if(sslConn==null){ + return null; + } + try { + System.out.println("sslconn sk local: "+((TCPConn)sslConn).socketChannel.getLocalAddress()); + System.out.println("sslconn sk remote: "+((TCPConn)sslConn).socketChannel.getRemoteAddress()); + } catch (IOException e) { + e.printStackTrace(); + } + String secretK=SSL.sslSetup(sslConn,key.GetPubkey(),key,serverK); + if(secretK==null){ + return null; + } + // 1. 尝试连接TCP + SocketChannel sk=null; + try { + sk = SocketChannel.open(); + sk.connect(new InetSocketAddress(host,port)); + // 设置超时时间 + sk.socket().setKeepAlive(true); + sk.socket().setSoTimeout(5000); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + // 4. 返回一个TCPConn + return new TCPConn(sk,this.key,serverK,true,secretK,1); + } +} diff --git a/src/main/java/VMSConnection/Utils/Base64Helper.java b/src/main/java/VMSConnection/Utils/Base64Helper.java new file mode 100644 index 0000000..fa39687 --- /dev/null +++ b/src/main/java/VMSConnection/Utils/Base64Helper.java @@ -0,0 +1,48 @@ +package VMSConnection.Utils; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 22:20 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class Base64Helper { + /** + * 与vpn-management适配的base64编码 + * @param src + * @return + */ + public static String BlockChain_Base64Encode(byte[] src){ + Base64.Encoder encoder = Base64.getUrlEncoder(); + encoder = encoder.withoutPadding(); + + return encoder.encodeToString(src); + } + public static String BlockChain_Base64Encode(String src){ + return BlockChain_Base64Encode(src.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 与vpn-management适配的base64解码 + * @param src + * @return + */ + public static byte[] BlockChain_Base64Decode(String src) { + Base64.Decoder decoder = Base64.getUrlDecoder(); + return decoder.decode(src.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 参考链接: https://blog.csdn.net/qq_36928715/article/details/110489464 + * golang的Unmarshal函数,自动把结构体中的base64转成了byte[],这里填补java的此功能 + * @return + */ + public static byte[] BlockChain_Base64Decode_GoLanUn(String src){ + edu.pku.commons.codec.binary.Base64 decoder = new edu.pku.commons.codec.binary.Base64(); + return decoder.decode(src); + } +} diff --git a/src/main/java/VMSConnection/Utils/ByteHelper.java b/src/main/java/VMSConnection/Utils/ByteHelper.java new file mode 100644 index 0000000..ec6b2d3 --- /dev/null +++ b/src/main/java/VMSConnection/Utils/ByteHelper.java @@ -0,0 +1,398 @@ +package VMSConnection.Utils; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 21:03 2021/6/30 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class ByteHelper { + // 小头字节序的Int转byte[] + public static byte[] int32ToByteArray(int int32) { +// return (byte[])int32; + byte[] result = new byte[4]; + + for(int i = 0; i < 4; ++i) { + int offset = (result.length - 1 - i) * 8; + result[result.length - 1 - i] = (byte)((int32 >>> offset & 0xff)); + } + + return result; + } + + // 参考链接:https://blog.csdn.net/jiangxinyu/article/details/8165064 + // 小头字节序的byte[]转Int + public static int byteArrayToInt32(byte[] int32) { + if (int32.length != 4) { + return Integer.parseInt(null); + } else { + int s = 0; + for (int i = 0; i < 3; i++) { + if (int32[3-i] >= 0) { + s = s + int32[3-i]; + } else { + s = s + 256 + int32[3-i]; + } + s = s * 256; + } + if (int32[0] >= 0) { + s = s + int32[0]; + } else { + s = s + 256 + int32[0]; + } + + return s; + } + } + + + + /** + * 获取从start到末尾的字节数组 + * @param buffer + * @param startIndex + * @return + */ + public static byte[] getTailBytes(byte[] buffer,int startIndex) { + if(startIndex >= buffer.length || startIndex < 0) + return null; + int len=buffer.length-startIndex; + byte[] newBytes=new byte[len]; + System.arraycopy(buffer,startIndex,newBytes,0,len); + return newBytes; + } + + // /** +// * 从byte[]数组指定开始位置获取第一个字节 +// * +// * @param buffer +// * @param start +//// * @return +//// */ +// public static byte getFirstByte(byte[] buffer, VlInt start) throws ByteHelperException { +// int startInt= 0; +// try { +// startInt = start.getVlIntValue2Int(); +// } catch (VlIntException e) { +// e.printStackTrace(); +// } +// if(startInt >= buffer.length || startInt < 0){ +// throw new ByteHelperException("buffer out of bounds"); +// } +// return buffer[startInt]; +// // byte[] firstbyte=new byte[1]; +// // System.arraycopy(buffer,startInt,firstbyte,0,firstbyte.length); +// // return firstbyte[0]; +// } + public static byte getFirstByte(byte[] buffer, int startInt) throws Exception { + if(startInt >= buffer.length || startInt < 0){ + throw new Exception("buffer out of bounds"); + } + return buffer[startInt]; + } + + // /** +// * 从byte[]数组指定开始位置获取长度为len的字节数组 +// * todo: start位置可能是大于int类型的数值 +// * @param buffer +// * @param start +// * @return +// */ +// public static byte[] getLenBytes(byte[] buffer, VlInt start,VlInt len) throws ByteHelperException { +// int startInt= -1; +// int lenInt = -1; +// try { +// startInt = start.getVlIntValue2Int(); +// lenInt=len.getVlIntValue2Int(); +// } catch (VlIntException e) { +// e.printStackTrace(); +// } +// if(startInt >= buffer.length || startInt < 0){ +// throw new ByteHelperException("buffer out of bounds"); +// } +// if(lenInt < 0){ +// throw new ByteHelperException("len should > 0"); +// } +// byte[] lenbytes=new byte[lenInt]; +//// byte[] lenbytes=new byte[1]; +// System.arraycopy(buffer,startInt,lenbytes,0,lenInt); +// return lenbytes; +// } +// public static byte[] getLenBytes(byte[] buffer, VlInt start,int len){ +// int startInt= -1; +// try { +// startInt = start.getVlIntValue2Int(); +// } catch (VlIntException e) { +// e.printStackTrace(); +// } +// byte[] lenbytes=new byte[len]; +//// byte[] lenbytes=new byte[1]; +// System.arraycopy(buffer,startInt,lenbytes,0,len); +// return lenbytes; +// } + public static byte[] getLenBytes(byte[] buffer, int startInt,int len){ + byte[] lenbytes=new byte[len]; + System.arraycopy(buffer,startInt,lenbytes,0,len); + return lenbytes; + } + + /** + * 无符号int转byte[] + * @param i_int + * @return + */ + private static byte[] intToByteArray(int i_int) { + byte[] result = new byte[4]; + //由高位到低位 + for(int i=0;i<4;i++) { + int offset=(result.length-i-1)*8; + result[i] = (byte) ((i_int >>>offset) & 0xff); + } + return result; + } + + /** + * 无符号byte[]转int + * @param bytes + * @return + */ + private static int byteArrayToInt(byte[] bytes) { + if(bytes.length!=4){ + return Integer.parseInt(null); + } + int value= 0; + //由高位到低位 + for (int i = 0; i < 4; i++) { + int offset= (bytes.length - 1 - i) * 8; + value +=(bytes[i] & 0x000000ff) << offset; //往高位游 + } + return value; + } + + /** + * 无符号long转byte[] + * @param l_long + * @return + */ + private static byte[] longToByteArray(long l_long) { + byte[] result = new byte[8]; + for (int i = 0; i<8; i++) { + int offset = (result.length - 1 - i) * 8; + result[i] = (byte) ((l_long >>> offset) & 0xff); + } + return result; + } + + /** + * 无符号byte[]转long + * @param bytes + * @return + */ + private static long byteArrayToLong(byte[] bytes) { + if(bytes.length!=8){ + return Integer.parseInt(null); + } + long value= 0; + //由高位到低位 + for (int i = 0; i < 8; i++) { + int offset= (bytes.length - 1 - i) * 8; + value +=((long)bytes[i] & 0xff) << offset; //往高位游 + } + return value; + } + + /** + * 无符号字节转int + * @param b + * @return + */ +// private static int unsignedbyte2int(byte b){ +// return (int) b&0xff; +// } + + /** + * uint8转int + * @param uint8 + * @return + */ + public static int uint8ToInt(byte uint8){ + return uint8 & 0xff; + } + + /** + * 一个uint8转为的int,通过本函数再次转为byte + * @param uint8 + * @return + */ + public static byte uint8ToByte(int uint8){ + return (byte)(uint8 & 0xff); + } + + /** + * uint8转long + * @param uint8 + * @return + */ + public static long uint8ToLong(byte uint8){ + return uint8ToInt(uint8); + } + + /** + * uint16转int + * @param uint16 + * @return + */ + public static int uint16ToInt(byte[] uint16){ + if(uint16.length!=2){ + return Integer.parseInt(null); + } + byte[] bytes=new byte[4]; + for(int i=0;i<2;i++) { + bytes[i] = 0x00; + } + System.arraycopy(uint16,0,bytes,2,uint16.length); + return byteArrayToInt(bytes); + } + + /** + * 一个uint16转为的int,通过本函数再次转为byte[] + * @param uint16 + * @return + */ + public static byte[] uint16ToByteArray(int uint16){ + byte[] result = new byte[2]; + for (int i = 0; i<2; i++) { + int offset = (result.length - 1 - i) * 8; + result[i] = (byte) ((uint16 >>> offset) & 0xff); + } + return result; + } + + /** + * uint16转long + * @param uint16 + * @return + */ + public static long uint16ToLong(byte[] uint16){ + return uint16ToInt(uint16); + } + + /** + * uint32转long + * @param uint32 + * @return + */ + public static long uint32ToLong(byte[] uint32){ + if(uint32.length!=4){ + return Long.parseLong(null); + } + byte[] bytes=new byte[8]; + for(int i=0;i<4;i++) { + bytes[i] = 0x00; + } + System.arraycopy(uint32,0,bytes,4,uint32.length); + // System.out.println(Arrays.toString(uint32) + "uint32ToLong: " + Arrays.toString(bytes)); + return byteArrayToLong(bytes); + } + + /** + * 一个uint32转为的long,通过本函数再次转为byte[] + * @param uint32 + * @return + */ + public static byte[] uint32ToByteArray(long uint32){ + byte[] result = new byte[4]; + for (int i = 0; i<4; i++) { + int offset = (result.length - 1 - i) * 8; + result[i] = (byte) ((uint32 >>> offset) & 0xff); + } + return result; + } + + /** + * uint64转long + * todo: 理论上可能发生越界 + * @param uint64 + * @return + */ + public static long uint64ToLong(byte[] uint64){ + if(uint64.length!=8){ + return Long.parseLong(null); + } + return byteArrayToLong(uint64); + } + + + /** + * 一个uint64转为的long,通过本函数再次转为byte[] + * todo: 理论上可能发生越界 + * @param uint64 + * @return + */ + public static byte[] uint64ToByteArray(long uint64){ + byte[] result = new byte[8]; + for (int i = 0; i<8; i++) { + int offset = (result.length - 1 - i) * 8; + result[i] = (byte) ((uint64 >>> offset) & 0xff); + } + return result; + } + + // short整型转为byte类型的数组 +// private static byte[] unsigned_short_2byte(int length) { +// byte[] targets = new byte[2]; +// for (int i = 0; i < 2; i++) { +// int offset = (targets.length - 1 - i) * 8; +// targets[i] = (byte) ((length >>> offset) & 0xff); +// } +// return targets; +// } + + //测试:对于uint8,应当测试1 127 128 + public static void main(String[] args){ + +// byte[] testBytes={0x01,0x02}; +// for(int i=0;i<2;i++) { +// System.out.println(testBytes[i]&0xFF); +//// System.out.println(Integer.toHexString(testBytes[i]&0xFF)); +// } + byte b=-1; + System.out.println(b&0xff); + } + + // 便于测试,新添加两个public方法: + // 无符号byte[]转int + // 传入的是长度为2或4的byte[] + public static int new_byteArrayToInt(byte[] bytes){ + int value= 0; + //由高位到低位 + for (int i = 0; i < bytes.length; i++) { + int offset= (bytes.length - 1 - i) * 8; + value +=(bytes[i] & 0x000000ff) << offset; //往高位游 + } + return value; + } + // 便于测试,新添加两个public方法: + // 无符号byte[]转int(溢出部分) + // 传入的是长度为4的byte[] + public static long new_byteArrayToAllInt(byte[] bytes){ + int v=new_byteArrayToInt(bytes); + if(v<0){ + return (long) (v+Math.pow(2,32)); + }else{ + return v; + } + } + // 无符号byte[]转long + // 传入的是长度为8的byte[] + public static long new_byteArrayToLong(byte[] bytes){ + long value= 0; + //由高位到低位 + for (int i = 0; i < 8; i++) { + int offset= (8 - 1 - i) * 8; + value +=((long)bytes[i] & 0xff) << offset; //往高位游 + } + return value; + } +} diff --git a/src/test/java/VMSConnection/Model/TestRegister.java b/src/test/java/VMSConnection/Model/TestRegister.java new file mode 100644 index 0000000..48eb3b0 --- /dev/null +++ b/src/test/java/VMSConnection/Model/TestRegister.java @@ -0,0 +1,49 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; +import VMSConnection.TCPNet.Message.NetworkRequest; +import VMSConnection.Utils.Base64Helper; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 15:47 2021/7/12 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestRegister { + @Test + public void testRegister(){ + String pubkey="0430faa210c22db991d7bafcbc1f1682a6dbbbee1fa3838edc1be78de699df8aecaf0845bb1ff38e91394a41aae4f70cae" + + "1abdd06c4428fc9fa5f0386d57036295"; + String baseSig="MEUCIQDvxeEX0JIATriXelXQLaecuEHti+dUebygANDyw7n0zQIgPMZhv5xEXs8td+Na8c9V1zRF5nUz+wurLi0iS4SBKFE="; + byte[] sig= Base64Helper.BlockChain_Base64Decode_GoLanUn(baseSig); + RegisterWithSignRequest request=new RegisterWithSignRequest(1,"/wefree","password", + "13294184003","1234","2642222222@qq.com","dev","", + pubkey,0,0,"1625142358",sig); + try { + System.out.println(request.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + BaseStruct baseStruct=request; + try { + System.out.println(baseStruct.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + try { + NetworkRequest networkRequest=new NetworkRequest("user","register",request.toJson().getBytes(StandardCharsets.UTF_8)); + System.out.println(networkRequest.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + + } +} diff --git a/src/test/java/VMSConnection/Model/TestStruct.java b/src/test/java/VMSConnection/Model/TestStruct.java new file mode 100644 index 0000000..5bf4f9f --- /dev/null +++ b/src/test/java/VMSConnection/Model/TestStruct.java @@ -0,0 +1,118 @@ +package VMSConnection.Model; + +import VMSConnection.TCPNet.Message.BaseStruct; +import VMSConnection.TCPNet.Message.NetworkRequest; +import VMSConnection.TCPNet.Message.NetworkResponse; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 14:32 2021/7/12 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +//class SSL_Message_Helper{ +// // 为了直接将SSL_Message的json格式解析为java对象, +// // 而定义本类 +// public int IsEncrypted; +// public int MType; +// public String Data; +// public String Pubkey = ""; +// public int Code = 0; +//} +class SSL_Message extends BaseStruct { + public int IsEncrypted; + public int MType; + public byte[] Data; + public String Pubkey=""; + public int Code=0; + +// @Override +// public void unJson(byte[] data,Object thisObj) { +// SSL_Message_Helper newObj= JSONObject.parseObject(new String(data), SSL_Message_Helper.class); +// try { +// Class thiscls = SSL_Message.class; +// Class cls = SSL_Message_Helper.class; +// Field[] fields = cls.getDeclaredFields(); +// for (int i = 0; i < fields.length; i++) { +// Field f = fields[i]; +// f.setAccessible(true); +// // 将属性值的名字的属性值的值放入this中 +// Field thisf = thiscls.getDeclaredField(f.getName()); +// thisf.setAccessible(true); +// if(thisf.getType()==(byte[].class)){ +// System.out.println("base decode: "+f.getName()); +// // byte[]需要解码base64 +// String base64S=(String)(f.get(newObj)); +// System.out.println("base64: "+base64S); +// byte[] decodeBytes= Base64Helper.BlockChain_Base64Decode_GoLanUn(base64S); +// thisf.set(this, decodeBytes); +// }else { +// thisf.set(this, f.get(newObj)); +// } +// } +// } catch (IllegalAccessException | NoSuchFieldException e) { +// e.printStackTrace(); +// } +// } +} + +public class TestStruct { + @Test + public void testRequest(){ + byte[] bytes=new byte[]{0x1, 0x2}; + NetworkRequest nr=new NetworkRequest("user","command", bytes); + nr.Header.put("Key",1); + try { + System.out.println(nr.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @Test + public void testResponse(){ + String base64="eyJJRCI6MCwiTmFtZSI6Ii93ZWZyZWUiLCJQYXNzd29yZCI6InBhc3N3b3JkIiwiUGhvbmUiOiIxM" + + "zI5NDE4NDAwMyIsIkludml0YXRpb24iOiIxMjM0IiwiRW1haWwiOiIyMjkxNTYzOTM0OEBxcS5jb20iLCJEZ" + + "XZpY2VJbmZvIjoiZGV2aWNlSW5mbyIsIkNlcnRpZmljYXRlIjoiIiwiUHVia2V5IjoiMDQzMGZhYTIxMGMyMmR" + + "iOTkxZDdiYWZjYmMxZjE2ODJhNmRiYmJlZTFmYTM4MzhlZGMxYmU3OGRlNjk5ZGY4YWVjYWYwODQ1YmIxZmYzOG" + + "U5MTM5NGE0MWFhZTRmNzBjYWUxYWJkZDA2YzQ0MjhmYzlmYTVmMDM4NmQ1NzAzNjI5NSIsIklzUmV2b2tlZCI6MCw" + + "iVUdyb3VwSUQiOjAsIlRpbWVTdGFtcCI6IjE2MjUxNDIzNTgiLCJTaWduYXR1cmUiOiJNRVVDSVFEdnhlRVgwSklBVHJp" + + "WGVsWFFMYWVjdUVIdGkrZFVlYnlnQU5EeXc3bjB6UUlnUE1aa" + + "HY1eEVYczh0ZCtOYThjOVYxelJGNW5Veit3dXJMaTBpUzRTQktGRT0ifQ=="; + String res="{\"Data\":\""+"AQI"+"\",\"Header\":{\"Key\":1},\"ErrorMsg\":\"error\",\"Code\":1}"; + NetworkResponse nr=new NetworkResponse(); + nr.unJson(res.getBytes(StandardCharsets.UTF_8),nr); + System.out.println(nr.Code); + System.out.println(nr.ErrorMsg); + System.out.println(nr.Header); + System.out.println(nr.Header.get("Key")); + System.out.println(Arrays.toString(nr.Data)); + System.out.println(new String(nr.Data)); + } + + @Test + public void testSSL(){ + String base64="eyJJRCI6MCwiTmFtZSI6Ii93ZWZyZWUiLCJQYXNzd29yZCI6InBhc3N3b3JkIiwiUGhvbmUiOiIxM" + + "zI5NDE4NDAwMyIsIkludml0YXRpb24iOiIxMjM0IiwiRW1haWwiOiIyMjkxNTYzOTM0OEBxcS5jb20iLCJEZ" + + "XZpY2VJbmZvIjoiZGV2aWNlSW5mbyIsIkNlcnRpZmljYXRlIjoiIiwiUHVia2V5IjoiMDQzMGZhYTIxMGMyMmR" + + "iOTkxZDdiYWZjYmMxZjE2ODJhNmRiYmJlZTFmYTM4MzhlZGMxYmU3OGRlNjk5ZGY4YWVjYWYwODQ1YmIxZmYzOG" + + "U5MTM5NGE0MWFhZTRmNzBjYWUxYWJkZDA2YzQ0MjhmYzlmYTVmMDM4NmQ1NzAzNjI5NSIsIklzUmV2b2tlZCI6MCw" + + "iVUdyb3VwSUQiOjAsIlRpbWVTdGFtcCI6IjE2MjUxNDIzNTgiLCJTaWduYXR1cmUiOiJNRVVDSVFEdnhlRVgwSklBVHJp" + + "WGVsWFFMYWVjdUVIdGkrZFVlYnlnQU5EeXc3bjB6UUlnUE1aa" + + "HY1eEVYczh0ZCtOYThjOVYxelJGNW5Veit3dXJMaTBpUzRTQktGRT0ifQ=="; + String res="{\"Data\":\""+base64+"\",\"Pubkey\":{\"Key\":1},\"Code\":200,\"IsEncrypted\":1,\"MType\":0}"; + SSL_Message nr=new SSL_Message(); + nr.unJson(res.getBytes(StandardCharsets.UTF_8),nr); + System.out.println(nr.Code); + System.out.println(nr.IsEncrypted); + System.out.println(nr.MType); + System.out.println(nr.Pubkey); + System.out.println(Arrays.toString(nr.Data)); + System.out.println(new String(nr.Data)); + } +} diff --git a/src/test/java/VMSConnection/TCPNet/TCPClientDemo.java b/src/test/java/VMSConnection/TCPNet/TCPClientDemo.java new file mode 100644 index 0000000..3d0ac3b --- /dev/null +++ b/src/test/java/VMSConnection/TCPNet/TCPClientDemo.java @@ -0,0 +1,116 @@ +package VMSConnection.TCPNet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 17:05 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public abstract class TCPClientDemo { + + private String ip; + private int port; + + private Socket mSocket; + private SocketAddress mSocketAddress; + private OutputStream mOutputStream; + private InputStream mInputStream; + private ReadThread mReadThread; + private boolean _isConnected = false; + + + public TCPClientDemo(String ip, int port) { + this.ip = ip; + this.port = port; + } + + + public void connect() { + + try { + this.mSocket = new Socket(); + this.mSocket.setKeepAlive(true); + this.mSocketAddress = new InetSocketAddress(ip, port); + this.mSocket.connect(mSocketAddress, 3000);// 设置连接超时时间为3秒 + + this.mOutputStream = mSocket.getOutputStream(); + this.mInputStream = mSocket.getInputStream(); + + this.mReadThread = new ReadThread(); + this.mReadThread.start(); + this._isConnected = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void close() { + + if (this.mReadThread != null) { + this.mReadThread.interrupt(); + } + if (this.mSocket != null) { + try { + this.mSocket.close(); + this.mSocket = null; + } catch (IOException e) { + e.printStackTrace(); + } + } + this._isConnected = false; + } + + public boolean isConnected() { + return this._isConnected; + } + + + public void send(byte[] bOutArray) { + try { + this.mOutputStream.write(bOutArray); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected abstract void onDataReceive(byte[] bytes, int size); + + private class ReadThread extends Thread { + @Override + public void run() { + super.run(); + while (!isInterrupted()) { + try { + if (TCPClientDemo.this.mInputStream == null) { + return; + } + int available = TCPClientDemo.this.mInputStream.available(); + if (available > 0) { + byte[] buffer = new byte[available]; + int size = TCPClientDemo.this.mInputStream.read(buffer); + if (size > 0) { + onDataReceive(buffer, size); + } + } else { + Thread.sleep(50); + } + + + } catch (Throwable e) { + System.out.println(e.getMessage()); + return; + } + } + } + } +} + + diff --git a/src/test/java/VMSConnection/TCPNet/TestMain.java b/src/test/java/VMSConnection/TCPNet/TestMain.java new file mode 100644 index 0000000..de07a31 --- /dev/null +++ b/src/test/java/VMSConnection/TCPNet/TestMain.java @@ -0,0 +1,43 @@ +package VMSConnection.TCPNet; + +import org.junit.Test; + +import java.util.Random; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:58 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestMain { + @Test + public void testConnectionServer(){ + TCPClientDemo tcpClient = new TCPClientDemo("121.15.171.82", 12345) { + @Override + protected void onDataReceive(byte[] bytes, int size) { + String content = "TCPServer say :" + new String(bytes, 0, size); + System.out.println(content); + } + }; + + tcpClient.connect(); // 连接TCPServer + if (tcpClient.isConnected()) { + //发送数据 + String outContent = "TCPClient say hello"; + tcpClient.send(outContent.getBytes()); + } + + //关闭连接 + tcpClient.close(); + } + + @Test + public void testRand(){ + System.out.println(String.format("%016d", + Math.abs(new Random(System.currentTimeMillis()).nextLong())) + .substring(0,16)); + System.out.println(String.format("%016d",Integer.MAX_VALUE)); + } +} diff --git a/src/test/java/VMSConnection/TCPNet/TestTCPClient.java b/src/test/java/VMSConnection/TCPNet/TestTCPClient.java new file mode 100644 index 0000000..110e33a --- /dev/null +++ b/src/test/java/VMSConnection/TCPNet/TestTCPClient.java @@ -0,0 +1,40 @@ +package VMSConnection.TCPNet; + +import VMSConnection.Security.BC_KeyManager; + +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 9:36 2021/6/25 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestTCPClient { + public static String ip="192.168.43.31"; + public static Integer port=12345; + public static Integer sslPort=54321; + public static String serverKey="04e067d23d3fd3ba4ead731b78346cde1084a83" + + "7760f234621e36a93632b6f6418d4d6b735cd0ee19a8707cec" + + "a12ee4ff8106196ad8122ab2c89a7e11ba00d35b7"; + public static void main(String[] args){ + // 初始化key及连接 + TCPNet tcpNet=new TCPNet(); + BC_KeyManager keyManager=new BC_KeyManager(); + keyManager.GenKeyPair(); + Conn conn=tcpNet.Dials(ip,port,ip,sslPort,keyManager,serverKey.getBytes(StandardCharsets.UTF_8)); + // todo;构造NetRequest包 + String bs="{\"Method\":\"\",\"Type\":\"test\",\"Command\":\"testCommand\",\"Header\":null,\"Parameters\":null}"; + byte[] b=bs.getBytes(StandardCharsets.UTF_8); + // 发送请求包 + if(!conn.writeV1(b)){ + System.out.println("发送失败"); + } + System.out.println("发送成功: "+new String(b)); + // 读取应答 + byte[] res=conn.read(); + System.out.println("读取应答: "+new String(res)); + conn.close(); + } +} diff --git a/src/test/java/VMSConnection/TCPNet/Tres.java b/src/test/java/VMSConnection/TCPNet/Tres.java new file mode 100644 index 0000000..67350eb --- /dev/null +++ b/src/test/java/VMSConnection/TCPNet/Tres.java @@ -0,0 +1,33 @@ +package VMSConnection.TCPNet; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 0:17 2021/6/26 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class Tres { + @Test + public void testR(){ +// String data="{\n" + +// " \"a\":\"hello\",\n" + +// " \"b\":\"world\",\n" + +// " \"Code\":233\n" + +// "}"; +// String newData="{\n" + +// " \"a\":\"hello\",\n" + +// " \"b\":\"world\"\n" + +// "}"; + String ss="{\"IsEncrypted\":0,\"MType\":2,\"Data\":\"dp6ITc30fqkw/1iZYE1rOA==\",\"Pubkey\":\"\",\"Code\":200}"; + SSL_Message tr= new SSL_Message(); + tr.unJson(ss.getBytes(StandardCharsets.UTF_8),tr); + System.out.println(tr.MType); + System.out.println(new String(tr.Data)); + System.out.println(tr.Code); + } +} diff --git a/src/test/java/VMSConnection/TestAPI.java b/src/test/java/VMSConnection/TestAPI.java new file mode 100644 index 0000000..f271327 --- /dev/null +++ b/src/test/java/VMSConnection/TestAPI.java @@ -0,0 +1,70 @@ +package VMSConnection; + +import VMSConnection.Model.LoginWithSignRequest; +import VMSConnection.Model.RegisterWithSignRequest; +import VMSConnection.Security.BC_KeyManager; +import VMSConnection.TCPNet.Message.NetworkResponse; +import org.bouncycastle.crypto.CryptoException; + +import java.nio.charset.StandardCharsets; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 20:17 2021/7/1 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestAPI { + public static void main(String[] args){ + String username="wefree3"; + String email="252553@qq.com"; + // 模拟客户端生成自己的公钥私钥 + BC_KeyManager bc_keyManager=new BC_KeyManager(); + bc_keyManager.GenKeyPair(); + String pubkey=bc_keyManager.GetPubkey(); + byte[] sig; + try { + sig = bc_keyManager.Sign(username.getBytes(StandardCharsets.UTF_8)) + .getBytes(StandardCharsets.UTF_8); + } catch (CryptoException e) { + e.printStackTrace(); + return; + } + + // 构造注册请求包 13294184003 + RegisterWithSignRequest request=new RegisterWithSignRequest(1,username,"password", + "15239970973","1234",email,"dev","", + pubkey,0,0,"1625142358",sig); + try { + System.out.println("login request: "+request.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + BC_API bc_api=new BC_API(); +// bc_api.ip="192.168.43.31"; + bc_api.ip="121.15.171.91"; + // 注册 + NetworkResponse networkResponse=bc_api.register(request); + try { + System.out.println("注册应答: "+networkResponse.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + System.out.println(networkResponse.Code); + + // 构造登录请求包 + LoginWithSignRequest loginWithSignRequest=new LoginWithSignRequest(username, + "password",new String(networkResponse.Data),sig); + + // 登录 + NetworkResponse networkResponse2=bc_api.login(loginWithSignRequest); + try { + System.out.println("登录应答: "+networkResponse2.toJson()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + System.out.println(networkResponse2.Code); + } +} diff --git a/src/test/java/VMSConnection/Utils/TestBase64Helper.java b/src/test/java/VMSConnection/Utils/TestBase64Helper.java new file mode 100644 index 0000000..8c039f9 --- /dev/null +++ b/src/test/java/VMSConnection/Utils/TestBase64Helper.java @@ -0,0 +1,55 @@ +package VMSConnection.Utils; + +import VMSConnection.Security.BC_KeyManager; +import edu.pku.commons.codec.binary.Base64; +import org.junit.Test; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 22:22 2021/6/22 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestBase64Helper { + @Test + public void testBase64(){ + String test = "11342526728"; + String encodeText = Base64Helper.BlockChain_Base64Encode(test); + String test2 = new String(Base64Helper.BlockChain_Base64Decode(encodeText)); + System.out.println(encodeText); + System.out.println(test2); + } + + @Test + public void testSM4(){ + String testBase="Ws1Q0WtKcy7/jiB3rP5mwg=="; + Base64 decoder = new Base64(); + byte[] decodeB=decoder.decode(testBase); + System.out.println(Arrays.toString(decodeB)); + System.out.println(new String(decodeB)); + try { + System.out.println(new String(BC_KeyManager.SM4Decrypt("3775199046620077",decodeB))); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } + } +} diff --git a/src/test/java/logicface/LogicFaceTest.java b/src/test/java/logicface/LogicFaceTest.java index f7e3f88..fcf9052 100644 --- a/src/test/java/logicface/LogicFaceTest.java +++ b/src/test/java/logicface/LogicFaceTest.java @@ -166,12 +166,12 @@ public class LogicFaceTest { cPacket.setDstIdentifier(nameTo); LogicFace face=new LogicFace(); - face.initWithTcp("127.0.0.1",60000); + face.initWithTcp("121.15.171.82",13899); face.sendCPacket(cPacket); // 等待两秒钟,接收推式包 try { - CPacket cPacket1 = face.receiveCPacket(2000); + CPacket cPacket1 = face.receiveCPacket(100000); System.out.println(Arrays.toString( new SelfEncodingBase().selfWireEncode(cPacket1).getRaw())); }catch (Exception e){ diff --git a/src/test/java/security/TestSM4.java b/src/test/java/security/TestSM4.java new file mode 100644 index 0000000..7787fa7 --- /dev/null +++ b/src/test/java/security/TestSM4.java @@ -0,0 +1,70 @@ +package security; + +import minsecurity.crypto.SM4; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; + +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 12:50 2021/7/1 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ +public class TestSM4 { + public static byte[] SM4Encrypt(String secretkey,byte[] origData) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, NoSuchProviderException { + return SM4.encrypt_ECB_Padding(secretkey.getBytes(StandardCharsets.UTF_8),origData); + } + + /** + * SM4对称解密 + * @param secretkey + * @param crypted + * @return + * @throws IllegalBlockSizeException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws NoSuchProviderException + */ + public static byte[] SM4Decrypt(String secretkey,byte[] crypted) throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + return SM4.decrypt_ECB_Padding(secretkey.getBytes(StandardCharsets.UTF_8),crypted); + } + + public static void main(String[] args){ + try { +// byte[] bytes={(byte) 68, (byte) 87, (byte) 150, (byte) 124, (byte) 188, (byte) 232, +// (byte) 13, (byte) 18, (byte) 44, (byte) 54, (byte) 67, (byte) 71, (byte) 85, +// (byte) 156, (byte) 96, (byte) 115}; +// byte[] res=SM4Decrypt("8746370504263797", +// bytes); + byte[] bytes={(byte)32, (byte)216, (byte)189, (byte)22, (byte)159, + (byte)141, (byte)147, (byte)221, (byte)220, + (byte)22, (byte)164, (byte)30, (byte)146, (byte)38, (byte)159, (byte)174}; + // 上面的这个bytes理论上与字符串"INi9Fp+Nk93cFqQekiafrg=="是等效的 + byte[] res=SM4Decrypt("3168831510184466",bytes); + System.out.println(Arrays.toString(res)); + System.out.println(new String(res)); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } + } +}