diff --git a/src/main/java/com/github/netricecake/kakao/LocoSocketHandlerImpl.java b/src/main/java/com/github/netricecake/kakao/LocoSocketHandlerImpl.java index 67c5c92..3801547 100644 --- a/src/main/java/com/github/netricecake/kakao/LocoSocketHandlerImpl.java +++ b/src/main/java/com/github/netricecake/kakao/LocoSocketHandlerImpl.java @@ -4,7 +4,7 @@ import com.github.netricecake.kakao.structs.ChatRoom; import com.github.netricecake.kakao.structs.Member; import com.github.netricecake.kakao.structs.Message; import com.github.netricecake.loco.LocoPacket; -import com.github.netricecake.loco.LocoSocektHandler; +import com.github.netricecake.loco.LocoSocketHandler; import com.github.netricecake.loco.packet.inbound.member.*; import com.github.netricecake.loco.packet.inbound.message.MessageIn; import com.github.netricecake.loco.packet.inbound.room.ChatInfoIn; @@ -18,7 +18,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import lombok.Getter; -public class LocoSocketHandlerImpl extends LocoSocektHandler { +public class LocoSocketHandlerImpl extends LocoSocketHandler { @Getter private TalkClient client; diff --git a/src/main/java/com/github/netricecake/kakao/TalkClient.java b/src/main/java/com/github/netricecake/kakao/TalkClient.java index ab9b832..a0a3084 100644 --- a/src/main/java/com/github/netricecake/kakao/TalkClient.java +++ b/src/main/java/com/github/netricecake/kakao/TalkClient.java @@ -3,7 +3,7 @@ package com.github.netricecake.kakao; import com.github.netricecake.kakao.exception.*; import com.github.netricecake.kakao.structs.ChatRoom; import com.github.netricecake.loco.LocoPacket; -import com.github.netricecake.loco.LocoSocektHandler; +import com.github.netricecake.loco.LocoSocketHandler; import com.github.netricecake.loco.LocoSocket; import com.github.netricecake.loco.packet.inbound.login.CheckInIn; import com.github.netricecake.loco.packet.inbound.login.GetConfIn; @@ -90,7 +90,7 @@ public class TalkClient { bookingData = KakaoApi.getBookingData(loginData.userId); if (bookingData == null || bookingData.getStatus() != 0) throw new BookingFailedException(); - LocoSocket checkInSocket = new LocoSocket(bookingData.getAddr(), bookingData.getPort(), new LocoSocektHandler() { + LocoSocket checkInSocket = new LocoSocket(bookingData.getAddr(), bookingData.getPort(), new LocoSocketHandler() { @Override public void onError(Exception e) { e.printStackTrace(); @@ -181,7 +181,7 @@ public class TalkClient { if (si.getStatus() != 0) return false; final CompletableFuture future = new CompletableFuture<>(); - postSocket = new LocoSocket(si.getVhost(), si.getPort(), new LocoSocektHandler() { + postSocket = new LocoSocket(si.getVhost(), si.getPort(), new LocoSocketHandler() { @Override public void onPacket(LocoPacket packet) { JsonObject jsonObject = BsonUtil.bsonToJsonObject(packet.getBody()); diff --git a/src/main/java/com/github/netricecake/loco/LocoSocket.java b/src/main/java/com/github/netricecake/loco/LocoSocket.java index b17ee19..1e61d09 100644 --- a/src/main/java/com/github/netricecake/loco/LocoSocket.java +++ b/src/main/java/com/github/netricecake/loco/LocoSocket.java @@ -21,11 +21,12 @@ import java.util.concurrent.*; public class LocoSocket { @Getter - private String ip; + private final String ip; @Getter - private int port; + private final int port; - private CryptoManager cryptoManager; + @Getter + private final CryptoManager cryptoManager = new CryptoManager();; private Channel channel; private EventLoopGroup eventLoopGroup; @@ -33,28 +34,24 @@ public class LocoSocket { @Getter private boolean alive = false; - @Getter - private Map> waitList = new HashMap<>(); + private final LocoSocketHandler locoSocektHandler; - @Getter - private LocoSocektHandler locoSocektHandler; + private final ExecutorService handlerPool; - @Getter - private ExecutorService handlerPool; + private final Map> waitList = new HashMap<>(); private int packetIdCounter = 1000; - public LocoSocket(String ip, int port, LocoSocektHandler locoSocektHandler, ExecutorService handlerPool) { + public LocoSocket(String ip, int port, LocoSocketHandler locoSocektHandler, ExecutorService handlerPool) { this.ip = ip; this.port = port; this.locoSocektHandler = locoSocektHandler; this.handlerPool = handlerPool; - cryptoManager = new CryptoManager(); } public void connect() throws IOException { + if (alive) throw new IOException("Already connected"); try { - byte[] handshakePacket = cryptoManager.generateHandshakeMessage(); eventLoopGroup = new MultiThreadIoEventLoopGroup(1, NioIoHandler.newFactory()); Bootstrap bootstrap = new Bootstrap(); bootstrap.remoteAddress(new InetSocketAddress(ip, port)) @@ -70,21 +67,17 @@ public class LocoSocket { }); channel = bootstrap.connect().sync().channel(); alive = true; - channel.writeAndFlush(handshakePacket).sync(); + channel.writeAndFlush(cryptoManager.generateHandshakeMessage()).sync(); channel.pipeline().addLast(new SecureLayerCodec(cryptoManager)); - channel.pipeline().addLast(new LocoCodec(this)); - handlerPool.execute(() -> { - locoSocektHandler.onConnect(); - }); + channel.pipeline().addLast(new LocoCodec(locoSocektHandler, handlerPool, waitList)); + handlerPool.execute(locoSocektHandler::onConnect); new Thread() { @Override public void run() { try { channel.closeFuture().sync(); eventLoopGroup.shutdownGracefully(); - handlerPool.execute(() -> { - locoSocektHandler.onDisconnect(); - }); + handlerPool.execute(locoSocektHandler::onDisconnect); alive = false; } catch (Exception e) { handlerPool.execute(() -> { @@ -119,7 +112,9 @@ public class LocoSocket { waitList.put(packetId, future); channel.writeAndFlush(packet); try { - return future.get(); + LocoPacket result = future.get(5, TimeUnit.SECONDS); + waitList.remove(packetId); + return result; } catch (Exception e) { handlerPool.execute(() -> { locoSocektHandler.onError(e); @@ -135,6 +130,7 @@ public class LocoSocket { }); eventLoopGroup.shutdownGracefully(); channel.close(); + handlerPool.execute(locoSocektHandler::onDisconnect); alive = false; } diff --git a/src/main/java/com/github/netricecake/loco/LocoSocketHandler.java b/src/main/java/com/github/netricecake/loco/LocoSocketHandler.java new file mode 100644 index 0000000..0ada6a0 --- /dev/null +++ b/src/main/java/com/github/netricecake/loco/LocoSocketHandler.java @@ -0,0 +1,13 @@ +package com.github.netricecake.loco; + +public class LocoSocketHandler { + + public void onPacket(LocoPacket packet) {} + + public void onConnect() {} + + public void onDisconnect() {} + + public void onError(Exception e) {} + +} diff --git a/src/main/java/com/github/netricecake/loco/codec/LocoCodec.java b/src/main/java/com/github/netricecake/loco/codec/LocoCodec.java index a8643e2..887b867 100644 --- a/src/main/java/com/github/netricecake/loco/codec/LocoCodec.java +++ b/src/main/java/com/github/netricecake/loco/codec/LocoCodec.java @@ -1,24 +1,33 @@ package com.github.netricecake.loco.codec; import com.github.netricecake.loco.LocoPacket; -import com.github.netricecake.loco.LocoSocket; +import com.github.netricecake.loco.LocoSocketHandler; import com.github.netricecake.loco.util.BsonUtil; import com.github.netricecake.loco.util.ByteUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; public class LocoCodec extends MessageToMessageCodec { private LocoPacket currentLocoPacket = null; private byte[] buffer = new byte[0]; - private final LocoSocket locoSocket; + private final Map> waitList; - public LocoCodec(LocoSocket locoSocekt) { - this.locoSocket = locoSocekt; + private final LocoSocketHandler locoSocektHandler; + + private final ExecutorService handlerPool; + + public LocoCodec(LocoSocketHandler locoSocektHandler, ExecutorService handlerPool, Map> waitList) { + this.locoSocektHandler = locoSocektHandler; + this.handlerPool = handlerPool; + this.waitList = waitList; } @Override @@ -59,17 +68,16 @@ public class LocoCodec extends MessageToMessageCodec { } if (currentLocoPacket.getBodyLength() > buffer.length) break; byte[] body = ByteUtil.sliceBytes(buffer, 0, currentLocoPacket.getBodyLength()); - System.out.println(currentLocoPacket.getMethod()); - System.out.println(BsonUtil.bsonToJson(body)); + //System.out.println(currentLocoPacket.getMethod()); + //System.out.println(BsonUtil.bsonToJson(body)); buffer = ByteUtil.sliceBytes(buffer, currentLocoPacket.getBodyLength(), buffer.length - currentLocoPacket.getBodyLength()); currentLocoPacket.setBody(body); - if (locoSocket.getWaitList().containsKey(currentLocoPacket.getPacketId())) { - ((CompletableFuture) locoSocket.getWaitList().get(currentLocoPacket.getPacketId())).complete(currentLocoPacket); - locoSocket.getWaitList().remove(currentLocoPacket.getPacketId()); + if (waitList.containsKey(currentLocoPacket.getPacketId())) { + ((CompletableFuture) waitList.get(currentLocoPacket.getPacketId())).complete(currentLocoPacket); } else { final LocoPacket p = currentLocoPacket; - locoSocket.getHandlerPool().execute(() -> { - locoSocket.getLocoSocektHandler().onPacket(p); + handlerPool.execute(() -> { + locoSocektHandler.onPacket(p); }); } currentLocoPacket = null; diff --git a/src/main/java/com/github/netricecake/loco/crypto/CryptoManager.java b/src/main/java/com/github/netricecake/loco/crypto/CryptoManager.java index 73857bc..792e951 100644 --- a/src/main/java/com/github/netricecake/loco/crypto/CryptoManager.java +++ b/src/main/java/com/github/netricecake/loco/crypto/CryptoManager.java @@ -1,10 +1,12 @@ package com.github.netricecake.loco.crypto; import com.github.netricecake.loco.util.ByteUtil; +import lombok.Getter; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.security.KeyFactory; import java.security.PublicKey; @@ -13,18 +15,23 @@ import java.security.spec.X509EncodedKeySpec; public class CryptoManager { - public final static int HANDSHAKE_BODY_SIZE = 256; // ENCRYPTED KEY + public final static int HANDSHAKE_BODY_SIZE = 256; + // Key share(RSA ES) spec public final static String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; public final static int RSA_LOCO_HEADER = 16; public final static byte[] RSA_PUBLIC_KEY_BYTES = ByteUtil.hexStringToByteArray("30820120300D06092A864886F70D01010105000382010D00308201080282010100A3B076E8C445851F19A670C231AAC6DB42EFD09717D06048A5CC56906CD1AB27B9DF37FFD5017E7C13A1405B5D1C3E4879A6A499D3C618A72472B0B50CA5EF1EF6EEA70369D9413FE662D8E2B479A9F72142EE70CEE6C2AD12045D52B25C4A204A28968E37F0BA6A49EE3EC9F2AC7A65184160F22F62C43A4067CD8D2A6F13D9B8298AB002763D236C9D1879D7FCE5B8FA910882B21E15247E0D0A24791308E51983614402E9FA03057C57E9E178B1CC39FE67288EFC461945CBCAA11D1FCC123E750B861F0D447EBE3C115F411A42DC95DDB21DA42774A5BCB1DDF7FA5F10628010C74F36F31C40EFCFE289FD81BABA44A6556A6C301210414B6023C3F46371020103"); + // AES spec public final static String AES_ALGORITHM = "AES/GCM/NoPadding"; public final static int AES_KEY_SIZE = 128; public final static int AES_NONCE_SIZE = 12; public final static int AES_LOCO_HEADER = 3; + @Getter private Key aesKey; + + // nonce(iv) generator private final SecureRandom generator = new SecureRandom(); public CryptoManager() { @@ -32,13 +39,11 @@ public class CryptoManager { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(AES_KEY_SIZE); aesKey = keyGenerator.generateKey(); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception e) {} } - public CryptoManager(Key aesKey) { - this.aesKey = aesKey; + public CryptoManager(byte[] key) { + this.aesKey = new SecretKeySpec(key, "AES"); } public byte[] generateHandshakeMessage() { @@ -49,24 +54,19 @@ public class CryptoManager { byte[] encryptedKey = cipher.doFinal(aesKey.getEncoded()); byte[] length = ByteUtil.intToByteArrayLE(HANDSHAKE_BODY_SIZE); return ByteUtil.concatBytes(length, ByteUtil.intToByteArrayLE(RSA_LOCO_HEADER), ByteUtil.intToByteArrayLE(AES_LOCO_HEADER), encryptedKey); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception e) {} return new byte[0]; } // 바디 사이즈가 131067가 최대인거 같은데 잘 모르겠음 public byte[] encryptMessage(byte[] message) { try { - byte[] nonce = new byte[AES_NONCE_SIZE]; + byte[] nonce = new byte[AES_NONCE_SIZE]; generator.nextBytes(nonce); Cipher cipher = Cipher.getInstance(AES_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce)); - byte[] encryptedBody = cipher.doFinal(message); - return ByteUtil.concatBytes(nonce, encryptedBody); - } catch (Exception e) { - e.printStackTrace(); - } + return ByteUtil.concatBytes(nonce, cipher.doFinal(message)); + } catch (Exception e) {} return new byte[0]; } @@ -76,9 +76,7 @@ public class CryptoManager { Cipher cipher = Cipher.getInstance(AES_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(AES_KEY_SIZE, nonce)); return cipher.doFinal(ByteUtil.sliceBytes(message, AES_NONCE_SIZE, message.length - nonce.length)); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception e) {} return new byte[0]; } diff --git a/src/main/java/com/github/netricecake/loco/util/BsonUtil.java b/src/main/java/com/github/netricecake/loco/util/BsonUtil.java index 75b3a4c..239110c 100644 --- a/src/main/java/com/github/netricecake/loco/util/BsonUtil.java +++ b/src/main/java/com/github/netricecake/loco/util/BsonUtil.java @@ -4,6 +4,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.bson.BsonBinaryReader; +import org.bson.BsonDocument; import org.bson.RawBsonDocument; import org.bson.codecs.BsonDocumentCodec; import org.bson.codecs.DecoderContext; @@ -16,7 +17,7 @@ public class BsonUtil { private final static Gson gson = new Gson(); public static byte[] jsonToBson(String json) { - var rawBson = RawBsonDocument.parse(json); + RawBsonDocument rawBson = RawBsonDocument.parse(json); ByteBuffer buffer = rawBson.getByteBuffer().asNIO(); byte[] exactBytes = new byte[buffer.remaining()]; buffer.get(exactBytes); @@ -28,7 +29,7 @@ public class BsonUtil { } public static String bsonToJson(byte[] bson) { - var doc = bsonDocumentCodec.decode( + BsonDocument doc = bsonDocumentCodec.decode( new BsonBinaryReader(ByteBuffer.wrap(bson)), DecoderContext.builder().build() );