All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.utils.ClientChainData Maven / Gradle / Ivy

Go to download

A Minecraft Bedrock Edition server software implementation made in Java from scratch which supports all new features.

There is a newer version: 1.6.0.1-PN
Show newest version
package cn.nukkit.utils;

import cn.nukkit.network.protocol.LoginPacket;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory;
import net.minidev.json.JSONObject;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * ClientChainData is a container of chain data sent from clients.
 * 

* Device information such as client UUID, xuid and serverAddress, can be * read from instances of this object. *

* To get chain data, you can use player.getLoginChainData() or read(loginPacket) *

* =============== * @author boybook (Nukkit Project) * =============== */ public final class ClientChainData implements LoginChainData { private static final String MOJANG_PUBLIC_KEY_BASE64 = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V"; private static final PublicKey MOJANG_PUBLIC_KEY; static { try { MOJANG_PUBLIC_KEY = generateKey(MOJANG_PUBLIC_KEY_BASE64); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { throw new AssertionError(e); } } public static ClientChainData of(byte[] buffer) { return new ClientChainData(buffer); } public static ClientChainData read(LoginPacket pk) { return of(pk.getBuffer()); } @Override public String getUsername() { return username; } @Override public UUID getClientUUID() { return clientUUID; } @Override public String getIdentityPublicKey() { return identityPublicKey; } @Override public long getClientId() { return clientId; } @Override public String getServerAddress() { return serverAddress; } @Override public String getDeviceModel() { return deviceModel; } @Override public int getDeviceOS() { return deviceOS; } @Override public String getDeviceId() { return deviceId; } @Override public String getGameVersion() { return gameVersion; } @Override public int getGuiScale() { return guiScale; } @Override public String getLanguageCode() { return languageCode; } @Override public String getXUID() { return xuid; } private boolean xboxAuthed; @Override public int getCurrentInputMode() { return currentInputMode; } @Override public int getDefaultInputMode() { return defaultInputMode; } @Override public String getCapeData() { return capeData; } public final static int UI_PROFILE_CLASSIC = 0; public final static int UI_PROFILE_POCKET = 1; @Override public int getUIProfile() { return UIProfile; } @Override public JsonObject getRawData() { return rawData; } /////////////////////////////////////////////////////////////////////////// // Override /////////////////////////////////////////////////////////////////////////// @Override public boolean equals(Object obj) { return obj instanceof ClientChainData && Objects.equals(bs, ((ClientChainData) obj).bs); } @Override public int hashCode() { return bs.hashCode(); } /////////////////////////////////////////////////////////////////////////// // Internal /////////////////////////////////////////////////////////////////////////// private String username; private UUID clientUUID; private String xuid; private static PublicKey generateKey(String base64) throws NoSuchAlgorithmException, InvalidKeySpecException { return KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(base64))); } private String identityPublicKey; private long clientId; private String serverAddress; private String deviceModel; private int deviceOS; private String deviceId; private String gameVersion; private int guiScale; private String languageCode; private int currentInputMode; private int defaultInputMode; private int UIProfile; private String capeData; private JsonObject rawData; private BinaryStream bs = new BinaryStream(); private ClientChainData(byte[] buffer) { bs.setBuffer(buffer, 0); decodeChainData(); decodeSkinData(); } @Override public boolean isXboxAuthed() { return xboxAuthed; } private void decodeSkinData() { JsonObject skinToken = decodeToken(new String(bs.get(bs.getLInt()))); if (skinToken == null) return; if (skinToken.has("ClientRandomId")) this.clientId = skinToken.get("ClientRandomId").getAsLong(); if (skinToken.has("ServerAddress")) this.serverAddress = skinToken.get("ServerAddress").getAsString(); if (skinToken.has("DeviceModel")) this.deviceModel = skinToken.get("DeviceModel").getAsString(); if (skinToken.has("DeviceOS")) this.deviceOS = skinToken.get("DeviceOS").getAsInt(); if (skinToken.has("DeviceId")) this.deviceId = skinToken.get("DeviceId").getAsString(); if (skinToken.has("GameVersion")) this.gameVersion = skinToken.get("GameVersion").getAsString(); if (skinToken.has("GuiScale")) this.guiScale = skinToken.get("GuiScale").getAsInt(); if (skinToken.has("LanguageCode")) this.languageCode = skinToken.get("LanguageCode").getAsString(); if (skinToken.has("CurrentInputMode")) this.currentInputMode = skinToken.get("CurrentInputMode").getAsInt(); if (skinToken.has("DefaultInputMode")) this.defaultInputMode = skinToken.get("DefaultInputMode").getAsInt(); if (skinToken.has("UIProfile")) this.UIProfile = skinToken.get("UIProfile").getAsInt(); if (skinToken.has("CapeData")) this.capeData = skinToken.get("CapeData").getAsString(); this.rawData = skinToken; } private JsonObject decodeToken(String token) { String[] base = token.split("\\."); if (base.length < 2) return null; String json = new String(Base64.getDecoder().decode(base[1]), StandardCharsets.UTF_8); //Server.getInstance().getLogger().debug(json); return new Gson().fromJson(json, JsonObject.class); } private void decodeChainData() { Map> map = new Gson().fromJson(new String(bs.get(bs.getLInt()), StandardCharsets.UTF_8), new TypeToken>>() { }.getType()); if (map.isEmpty() || !map.containsKey("chain") || map.get("chain").isEmpty()) return; List chains = map.get("chain"); // Validate keys try { xboxAuthed = verifyChain(chains); } catch (Exception e) { xboxAuthed = false; } for (String c : chains) { JsonObject chainMap = decodeToken(c); if (chainMap == null) continue; if (chainMap.has("extraData")) { JsonObject extra = chainMap.get("extraData").getAsJsonObject(); if (extra.has("displayName")) this.username = extra.get("displayName").getAsString(); if (extra.has("identity")) this.clientUUID = UUID.fromString(extra.get("identity").getAsString()); if (extra.has("XUID")) this.xuid = extra.get("XUID").getAsString(); } if (chainMap.has("identityPublicKey")) this.identityPublicKey = chainMap.get("identityPublicKey").getAsString(); } if (!xboxAuthed) { xuid = null; } } private boolean verifyChain(List chains) throws Exception { PublicKey lastKey = null; boolean mojangKeyVerified = false; for (String chain: chains) { JWSObject jws = JWSObject.parse(chain); if (!mojangKeyVerified) { // First chain should be signed using Mojang's private key. We'd be in big trouble if it leaked... mojangKeyVerified = verify(MOJANG_PUBLIC_KEY, jws); } if (lastKey != null) { if (!verify(lastKey, jws)) { throw new JOSEException("Unable to verify key in chain."); } } JSONObject payload = jws.getPayload().toJSONObject(); String base64key = payload.getAsString("identityPublicKey"); if (base64key == null) { throw new RuntimeException("No key found"); } lastKey = generateKey(base64key); } return mojangKeyVerified; } private boolean verify(PublicKey key, JWSObject object) throws JOSEException { JWSVerifier verifier = new DefaultJWSVerifierFactory().createJWSVerifier(object.getHeader(), key); return object.verify(verifier); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy