mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2026-05-03 19:30:57 +02:00
Update 1.19.1 public key verification
This commit is contained in:
+23
-8
@@ -34,6 +34,7 @@ import com.google.common.primitives.Longs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
@@ -51,6 +52,7 @@ import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Encoder;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@@ -77,6 +79,8 @@ final class EncryptionUtil {
|
||||
private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(
|
||||
LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
private static final int MILLISECOND_SIZE = 8;
|
||||
private static final int UUID_SIZE = 2 * MILLISECOND_SIZE;
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -146,7 +150,7 @@ final class EncryptionUtil {
|
||||
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
|
||||
}
|
||||
|
||||
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp)
|
||||
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp, UUID premiumId)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
if (clientKey.isExpired(verifyTimestamp)) {
|
||||
return false;
|
||||
@@ -155,10 +159,27 @@ final class EncryptionUtil {
|
||||
Signature verifier = Signature.getInstance("SHA1withRSA");
|
||||
// key of the signer
|
||||
verifier.initVerify(MOJANG_SESSION_KEY);
|
||||
verifier.update(toSignable(clientKey).getBytes(StandardCharsets.US_ASCII));
|
||||
verifier.update(toSignable(clientKey, premiumId));
|
||||
return verifier.verify(clientKey.signature());
|
||||
}
|
||||
|
||||
private static byte[] toSignable(ClientPublicKey clientPublicKey, UUID ownerPremiumId) {
|
||||
if (ownerPremiumId == null) {
|
||||
long expiry = clientPublicKey.expiry().toEpochMilli();
|
||||
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
|
||||
return (expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n")
|
||||
.getBytes(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
byte[] keyData = clientPublicKey.key().getEncoded();
|
||||
return ByteBuffer.allocate(keyData.length + UUID_SIZE + MILLISECOND_SIZE)
|
||||
.putLong(ownerPremiumId.getMostSignificantBits())
|
||||
.putLong(ownerPremiumId.getLeastSignificantBits())
|
||||
.putLong(clientPublicKey.expiry().toEpochMilli())
|
||||
.put(keyData)
|
||||
.array();
|
||||
}
|
||||
|
||||
public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce)
|
||||
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
||||
BadPaddingException, InvalidKeyException {
|
||||
@@ -186,12 +207,6 @@ final class EncryptionUtil {
|
||||
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
|
||||
}
|
||||
|
||||
private static String toSignable(ClientPublicKey clientPublicKey) {
|
||||
long expiry = clientPublicKey.expiry().toEpochMilli();
|
||||
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
|
||||
return expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n";
|
||||
}
|
||||
|
||||
private static byte[] decrypt(PrivateKey key, byte[] data)
|
||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
IllegalBlockSizeException, BadPaddingException {
|
||||
|
||||
+8
-3
@@ -33,6 +33,7 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
@@ -50,6 +51,7 @@ import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
@@ -215,7 +217,10 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
return Optional.of(ClientPublicKey.of(expires, key, signature));
|
||||
});
|
||||
|
||||
if (verifyClientKeys && clientKey.isPresent() && verifyPublicKey(clientKey.get())) {
|
||||
// start reading from index 1, because 0 is already used by the public key
|
||||
Optional<UUID> sessionUUID = packet.getOptionals(Converters.passthrough(UUID.class)).readSafely(1);
|
||||
if (verifyClientKeys && sessionUUID.isPresent() && clientKey.isPresent()
|
||||
&& verifyPublicKey(clientKey.get(), sessionUUID.get())) {
|
||||
// missing or incorrect
|
||||
// expired always not allowed
|
||||
player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
|
||||
@@ -232,9 +237,9 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
plugin.getScheduler().runAsync(nameCheckTask);
|
||||
}
|
||||
|
||||
private boolean verifyPublicKey(ClientPublicKey clientKey) {
|
||||
private boolean verifyPublicKey(ClientPublicKey clientKey, UUID sessionPremiumUUID) {
|
||||
try {
|
||||
return EncryptionUtil.verifyClientKey(clientKey, Instant.now());
|
||||
return EncryptionUtil.verifyClientKey(clientKey, Instant.now(), sessionPremiumUUID);
|
||||
} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+11
@@ -27,6 +27,8 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -41,4 +43,13 @@ public class ClientPublicKey {
|
||||
public boolean isExpired(Instant verifyTimestamp) {
|
||||
return !verifyTimestamp.isBefore(expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + '[', "]")
|
||||
.add("expiry=" + expiry)
|
||||
.add("key=" + Base64.getEncoder().encodeToString(key.getEncoded()))
|
||||
.add("signature=" + Base64.getEncoder().encodeToString(signature))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user