Skip to content
This repository was archived by the owner on May 1, 2025. It is now read-only.

Commit eb8d2d4

Browse files
Added comments
Better exception logging
1 parent e413161 commit eb8d2d4

File tree

12 files changed

+136
-116
lines changed

12 files changed

+136
-116
lines changed

src/main/java/com/andcool/OAuthServer.java

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,10 @@
66
import java.io.IOException;
77
import java.net.InetSocketAddress;
88
import java.security.KeyPair;
9-
import java.security.KeyPairGenerator;
10-
import java.security.NoSuchAlgorithmException;
11-
import java.security.SecureRandom;
129
import java.util.Base64;
13-
import java.util.concurrent.TimeUnit;
1410

1511
import javax.imageio.ImageIO;
1612

17-
import io.netty.handler.timeout.IdleStateHandler;
1813
import org.json.JSONObject;
1914

2015
import com.andcool.config.UserConfig;
@@ -37,6 +32,9 @@
3732
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
3833
import io.netty.handler.codec.LengthFieldPrepender;
3934

35+
import static com.andcool.encryption.Keys.generateKeyPair;
36+
import static com.andcool.encryption.Keys.generateVerifyToken;
37+
4038

4139
public class OAuthServer {
4240
public static ExpiringHashMap<String, JSONObject> expiringMap = new ExpiringHashMap<>(5 * 60 * 1000);
@@ -80,22 +78,6 @@ public void initChannel(SocketChannel ch) {
8078
}
8179
}
8280

83-
private static KeyPair generateKeyPair() {
84-
try {
85-
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
86-
keyPairGenerator.initialize(1024);
87-
return keyPairGenerator.generateKeyPair();
88-
} catch (NoSuchAlgorithmException e) {
89-
throw new RuntimeException(e);
90-
}
91-
}
92-
93-
private static byte[] generateVerifyToken() {
94-
byte[] token = new byte[4];
95-
new SecureRandom().nextBytes(token);
96-
return token;
97-
}
98-
9981
private static String loadIcon(){
10082
String imagePath = "server_icon.png";
10183
BufferedImage image;

src/main/java/com/andcool/bytebuf/ByteBufUtils.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -53,38 +53,6 @@ public static void writeVarInt(ByteBuf buf, int value) {
5353

5454
}
5555

56-
public static long readVarLong(ByteBuf in) {
57-
long value = 0;
58-
int position = 0;
59-
byte currentByte;
60-
61-
while (true) {
62-
currentByte = in.readByte();
63-
value |= (long) (currentByte & 0x7F) << position;
64-
65-
if ((currentByte & 0x80) == 0) break;
66-
67-
position += 7;
68-
69-
if (position >= 64) throw new RuntimeException("VarLong is too big");
70-
}
71-
72-
return value;
73-
}
74-
75-
public static void writeVarLong(ByteBuf buf, long value) {
76-
do {
77-
byte part = (byte)((int)(value & 127L));
78-
value >>>= 7;
79-
if (value != 0L) {
80-
part = (byte)(part | 128);
81-
}
82-
83-
buf.writeByte(part);
84-
} while(value != 0L);
85-
86-
}
87-
8856
public static void sendPacket(ChannelHandlerContext ctx, ByteBuf data) {
8957
ByteBuf packet = ctx.alloc().buffer();
9058
try {

src/main/java/com/andcool/handlers/Encryption.java renamed to src/main/java/com/andcool/encryption/Encryption.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.andcool.handlers;
1+
package com.andcool.encryption;
22

33
import com.andcool.sillyLogger.Level;
44
import io.netty.buffer.ByteBuf;
@@ -33,14 +33,12 @@ public Encryption(SecretKey sharedSecret) {
3333
}
3434

3535
@Override
36-
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
37-
throws Exception {
36+
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
3837
encodeBuf.crypt(msg, out);
3938
}
4039

4140
@Override
42-
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
43-
throws Exception {
41+
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
4442
decodeBuf.crypt(msg, out);
4543
}
4644

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.andcool.encryption;
2+
3+
import java.security.KeyPair;
4+
import java.security.KeyPairGenerator;
5+
import java.security.NoSuchAlgorithmException;
6+
import java.security.SecureRandom;
7+
8+
public class Keys {
9+
public static KeyPair generateKeyPair() {
10+
try {
11+
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
12+
keyPairGenerator.initialize(1024);
13+
return keyPairGenerator.generateKeyPair();
14+
} catch (NoSuchAlgorithmException e) {
15+
throw new RuntimeException(e);
16+
}
17+
}
18+
19+
public static byte[] generateVerifyToken() {
20+
byte[] token = new byte[4];
21+
new SecureRandom().nextBytes(token);
22+
return token;
23+
}
24+
}
Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,64 @@
11
package com.andcool.handlers;
22

3-
import java.io.IOException;
4-
import java.math.BigInteger;
5-
import java.security.InvalidKeyException;
6-
import java.security.MessageDigest;
7-
import java.security.NoSuchAlgorithmException;
8-
import java.security.SecureRandom;
9-
import java.util.Arrays;
10-
import java.util.Random;
11-
12-
import javax.crypto.BadPaddingException;
13-
import javax.crypto.Cipher;
14-
import javax.crypto.IllegalBlockSizeException;
15-
import javax.crypto.NoSuchPaddingException;
16-
import javax.crypto.SecretKey;
17-
import javax.crypto.spec.SecretKeySpec;
18-
19-
import org.json.JSONException;
20-
import org.json.JSONObject;
21-
223
import com.andcool.MojangSession;
234
import com.andcool.OAuthServer;
245
import com.andcool.bytebuf.ByteBufUtils;
256
import com.andcool.config.UserConfig;
7+
import com.andcool.encryption.Encryption;
268
import com.andcool.session.Session;
279
import com.andcool.session.SessionHandler;
2810
import com.andcool.sillyLogger.Level;
29-
3011
import io.netty.buffer.ByteBuf;
3112
import io.netty.channel.ChannelHandlerContext;
13+
import org.json.JSONException;
14+
import org.json.JSONObject;
15+
16+
import javax.crypto.*;
17+
import javax.crypto.spec.SecretKeySpec;
18+
import java.io.IOException;
19+
import java.math.BigInteger;
20+
import java.security.InvalidKeyException;
21+
import java.security.MessageDigest;
22+
import java.security.NoSuchAlgorithmException;
23+
import java.security.SecureRandom;
24+
import java.util.Arrays;
3225

3326
public class EncryptionHandler {
27+
28+
/*
29+
Generate 6-digit code for client data
30+
*/
3431
public static String getRandomCode() {
3532
SecureRandom secureRandom = new SecureRandom();
3633
StringBuilder code = new StringBuilder();
3734
for (int i = 0; i < 6; i++) {
38-
code.append(secureRandom.nextInt(10)); // Генерация случайной цифры от 0 до 9
35+
code.append(secureRandom.nextInt(10));
3936
}
4037
return code.toString();
4138
}
4239

43-
public static void handleEncryptionResponse(ChannelHandlerContext ctx, ByteBuf in, Session session) throws Exception {
40+
public static void handleEncryptionResponse(ChannelHandlerContext ctx, ByteBuf in, Session session) {
4441
try {
42+
43+
// Read encrypted shared secret and it's length
4544
int sharedSecretLength = ByteBufUtils.readVarInt(in);
4645
byte[] encryptedSharedSecret = new byte[sharedSecretLength];
4746
in.readBytes(encryptedSharedSecret);
4847

48+
// Read encrypted verify token and it's length
4949
int verifyTokenLength = ByteBufUtils.readVarInt(in);
5050
byte[] encryptedVerifyToken = new byte[verifyTokenLength];
5151
in.readBytes(encryptedVerifyToken);
5252

53+
// Init decrypt cipher for shared secret
5354
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
5455
rsaCipher.init(Cipher.DECRYPT_MODE, OAuthServer.KEY_PAIR.getPrivate());
55-
SecretKey sharedSecret;
5656

57-
sharedSecret = new SecretKeySpec(rsaCipher.doFinal(encryptedSharedSecret), "AES");
57+
// Trying to decrypt shared secret and verify token
58+
SecretKey sharedSecret = new SecretKeySpec(rsaCipher.doFinal(encryptedSharedSecret), "AES");
5859
byte[] verifyToken = rsaCipher.doFinal(encryptedVerifyToken);
5960

61+
// Comparing our verify and decrypted verify tokens
6062
if (!Arrays.equals(OAuthServer.VERIFY_TOKEN, verifyToken)) {
6163
OAuthServer.logger.log(Level.ERROR, "Invalid verify token");
6264
SessionHandler.disconnect(ctx, "Error while encryption!");
@@ -65,20 +67,27 @@ public static void handleEncryptionResponse(ChannelHandlerContext ctx, ByteBuf i
6567
OAuthServer.logger.log(Level.DEBUG, "Verify tokens match!");
6668
}
6769

70+
// Creating hash for Mojang API
6871
MessageDigest digest = MessageDigest.getInstance("SHA-1");
6972
digest.update(UserConfig.SERVER_ID.getBytes());
7073
digest.update(sharedSecret.getEncoded());
7174
digest.update(OAuthServer.KEY_PAIR.getPublic().getEncoded());
72-
7375
String hash = new BigInteger(digest.digest()).toString(16);
74-
JSONObject response = MojangSession.sendRequest(session.nickname, hash);
76+
77+
JSONObject response = MojangSession.sendRequest(session.nickname, hash); // Do Mojang API request
78+
79+
// Establishing encrypted connection with client
7580
ctx.pipeline().replace("encryption", "encryption", new Encryption(sharedSecret));
7681

82+
// If client didn't Mojang API request (in offline mode)
7783
if (response == null) {
78-
SessionHandler.disconnect(ctx, "You are using unlicensed copy of Minecraft!");
84+
SessionHandler.disconnect(ctx, "Failed to login: Invalid session (Try restarting your game and the launcher)");
7985
return;
8086
}
8187

88+
// Create code for client
89+
// Disconnecting with code
90+
// Saving client data to hash map
8291
String code = getRandomCode();
8392
SessionHandler.disconnect(ctx, String.format("Hello, %s. Your code is: %s", session.nickname, code));
8493
JSONObject jsonResponse = new JSONObject();
@@ -88,8 +97,15 @@ public static void handleEncryptionResponse(ChannelHandlerContext ctx, ByteBuf i
8897
OAuthServer.expiringMap.put(code, jsonResponse);
8998

9099
OAuthServer.logger.log(Level.INFO, "Created code " + code + " for " + session.nickname);
91-
} catch (IOException | InterruptedException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException | JSONException e) {
92-
OAuthServer.logger.log(Level.DEBUG, "Exception in handleEncryptionResponse: " + e);
100+
} catch (IOException
101+
| InterruptedException
102+
| InvalidKeyException
103+
| NoSuchAlgorithmException
104+
| BadPaddingException
105+
| IllegalBlockSizeException
106+
| NoSuchPaddingException
107+
| JSONException e) {
108+
OAuthServer.logger.log(Level.ERROR, e, true);
93109
}
94110
}
95111
}

src/main/java/com/andcool/handlers/HandshakeHandler.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package com.andcool.handlers;
22

3-
import com.andcool.OAuthServer;
43
import com.andcool.bytebuf.ByteBufUtils;
54
import com.andcool.config.UserConfig;
65
import com.andcool.responses.PingResponse;
76
import com.andcool.session.Session;
8-
import com.andcool.sillyLogger.Level;
9-
107
import io.netty.buffer.ByteBuf;
118
import io.netty.channel.ChannelHandlerContext;
129

@@ -16,14 +13,15 @@ public static void handleHandshake(ChannelHandlerContext ctx, ByteBuf in, Sessio
1613
ByteBufUtils.readUTF8(in);
1714
in.readUnsignedShort();
1815
int nextState = ByteBufUtils.readVarInt(in);
19-
OAuthServer.logger.log(Level.DEBUG, "Received handshake! Protocol version: " +
20-
protocolVersion +
21-
" Next state: " + nextState);
16+
2217
session.protocolVersion = protocolVersion;
2318
session.loginPhase = 1;
2419
session.nextState = nextState;
2520
switch (nextState) {
26-
case 1 -> PingResponse.sendPingResponse(ctx, UserConfig.PROTOCOL_VERSION == -1 ? protocolVersion : UserConfig.PROTOCOL_VERSION);
21+
// If server doesn't have specified protocol version, using client's version
22+
case 1 -> PingResponse.sendPingResponse(
23+
ctx,
24+
UserConfig.PROTOCOL_VERSION == -1 ? protocolVersion : UserConfig.PROTOCOL_VERSION);
2725
case 2 -> LoginStartHandler.handleLoginStart(ctx, in, session);
2826
}
2927
}

src/main/java/com/andcool/pipeline/EncryptionRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ public static void sendEncryptionRequest(ChannelHandlerContext ctx, int protocol
1515
ByteBufUtils.writeUTF8(out, UserConfig.SERVER_ID); // Server ID
1616
byte[] publicKey = OAuthServer.KEY_PAIR.getPublic().getEncoded();
1717

18+
// Write server's public key
1819
ByteBufUtils.writeVarInt(out, publicKey.length);
1920
out.writeBytes(publicKey);
2021

22+
// Write server's verify token
2123
ByteBufUtils.writeVarInt(out, OAuthServer.VERIFY_TOKEN.length);
2224
out.writeBytes(OAuthServer.VERIFY_TOKEN);
2325

26+
// For widest client protocol version support
2427
if (protocolVersion >= 766) {
2528
out.writeBoolean(true);
2629
}

src/main/java/com/andcool/responses/PingResponse.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package com.andcool.responses;
22

33
import com.andcool.OAuthServer;
4+
import com.andcool.bytebuf.ByteBufUtils;
45
import com.andcool.config.UserConfig;
56
import io.netty.buffer.ByteBuf;
67
import io.netty.channel.ChannelHandlerContext;
78
import org.json.JSONObject;
8-
import com.andcool.bytebuf.ByteBufUtils;
99

1010
import java.io.IOException;
1111
import java.util.Collections;
1212

1313
import static com.andcool.bytebuf.ByteBufUtils.sendPacket;
1414

1515
public class PingResponse {
16-
public static String Response(int protoVersion){
16+
public static String Response(int protoVersion) {
1717
JSONObject json_response = new JSONObject();
1818

1919
JSONObject version = new JSONObject();

src/main/java/com/andcool/session/Session.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ public class Session {
99
// 1 - waiting for login request
1010
// 2 - waiting for encryption response
1111

12-
public Session() {
13-
}
12+
public Session() {}
1413
}

0 commit comments

Comments
 (0)