mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-04-19 14:33:04 +03:00
Merge remote-tracking branch 'origin/feature/eccrypt' into feature/redefineExitFix
This commit is contained in:
commit
5822a854e4
35 changed files with 356 additions and 272 deletions
|
@ -13,6 +13,8 @@
|
|||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
@ -220,9 +222,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
|||
public LaunchServerRuntimeConfig runtime;
|
||||
|
||||
|
||||
public final RSAPublicKey publicKey;
|
||||
public final ECPublicKey publicKey;
|
||||
|
||||
public final RSAPrivateKey privateKey;
|
||||
public final ECPrivateKey privateKey;
|
||||
// Launcher binary
|
||||
|
||||
public final JARLauncherBinary launcherBinary;
|
||||
|
@ -279,7 +281,7 @@ public void collect()
|
|||
}
|
||||
}
|
||||
|
||||
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, RSAPublicKey publicKey, RSAPrivateKey privateKey, CommandHandler commandHandler) throws IOException, InvalidKeySpecException {
|
||||
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler) throws IOException, InvalidKeySpecException {
|
||||
this.dir = directories.dir;
|
||||
this.env = env;
|
||||
this.config = config;
|
||||
|
@ -306,9 +308,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this));
|
||||
|
||||
// Print keypair fingerprints
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(publicKey.getModulus().toByteArray()); // IDEA говорит, что это Java 9 API. WTF?
|
||||
LogHelper.subInfo("Modulus CRC32: 0x%08x", crc.getValue());
|
||||
|
||||
// Load class bindings.
|
||||
launcherEXEBinaryClass = defaultLauncherEXEBinaryClass;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package pro.gravit.launchserver;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
|
@ -16,8 +18,8 @@ public class LaunchServerBuilder {
|
|||
private LaunchServer.LaunchServerEnv env;
|
||||
private LaunchServerModulesManager modulesManager;
|
||||
private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
|
||||
private RSAPublicKey publicKey;
|
||||
private RSAPrivateKey privateKey;
|
||||
private ECPublicKey publicKey;
|
||||
private ECPrivateKey privateKey;
|
||||
private LaunchServer.LaunchServerConfigManager launchServerConfigManager;
|
||||
|
||||
public LaunchServerBuilder setConfig(LaunchServerConfig config) {
|
||||
|
@ -55,12 +57,12 @@ public LaunchServerBuilder setDir(Path dir) {
|
|||
return this;
|
||||
}
|
||||
|
||||
public LaunchServerBuilder setPublicKey(RSAPublicKey publicKey) {
|
||||
public LaunchServerBuilder setPublicKey(ECPublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchServerBuilder setPrivateKey(RSAPrivateKey privateKey) {
|
||||
public LaunchServerBuilder setPrivateKey(ECPrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.hwid.HWIDProvider;
|
||||
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
||||
|
@ -35,8 +38,11 @@
|
|||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class LaunchServerStarter {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
JVMHelper.checkStackTrace(LaunchServerStarter.class);
|
||||
JVMHelper.verifySystemProperties(LaunchServer.class, true);
|
||||
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
|
||||
|
@ -51,8 +57,8 @@ public static void main(String[] args) throws Exception {
|
|||
Path configFile, runtimeConfigFile;
|
||||
Path publicKeyFile =dir.resolve("public.key");
|
||||
Path privateKeyFile = dir.resolve("private.key");
|
||||
RSAPublicKey publicKey;
|
||||
RSAPrivateKey privateKey;
|
||||
ECPublicKey publicKey;
|
||||
ECPrivateKey privateKey;
|
||||
|
||||
LaunchServerRuntimeConfig runtimeConfig;
|
||||
LaunchServerConfig config;
|
||||
|
@ -84,19 +90,17 @@ public static void main(String[] args) throws Exception {
|
|||
LogHelper.warning("JLine2 isn't in classpath, using std");
|
||||
}
|
||||
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
|
||||
LogHelper.info("Reading RSA keypair");
|
||||
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile));
|
||||
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile));
|
||||
if (!publicKey.getModulus().equals(privateKey.getModulus()))
|
||||
throw new IOException("Private and public key modulus mismatch");
|
||||
LogHelper.info("Reading EC keypair");
|
||||
publicKey = SecurityHelper.toPublicECKey(IOHelper.read(publicKeyFile));
|
||||
privateKey = SecurityHelper.toPrivateECKey(IOHelper.read(privateKeyFile));
|
||||
} else {
|
||||
LogHelper.info("Generating RSA keypair");
|
||||
KeyPair pair = SecurityHelper.genRSAKeyPair();
|
||||
publicKey = (RSAPublicKey) pair.getPublic();
|
||||
privateKey = (RSAPrivateKey) pair.getPrivate();
|
||||
LogHelper.info("Generating EC keypair");
|
||||
KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
|
||||
publicKey = (ECPublicKey) pair.getPublic();
|
||||
privateKey = (ECPrivateKey) pair.getPrivate();
|
||||
|
||||
// Write key pair list
|
||||
LogHelper.info("Writing RSA keypair list");
|
||||
LogHelper.info("Writing EC keypair list");
|
||||
IOHelper.write(publicKeyFile, publicKey.getEncoded());
|
||||
IOHelper.write(privateKeyFile, privateKey.getEncoded());
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
|
|||
db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory));
|
||||
db_hwid.macAddr = set.getString(hwidFieldMAC);
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString());
|
||||
LogHelper.dev("Compare HWID: %s vs %s", hwid.toString(), db_hwid.toString());
|
||||
}
|
||||
int compare_point = hwid.compare(db_hwid);
|
||||
if (compare_point < compare) continue;
|
||||
|
|
|
@ -65,6 +65,10 @@ public void setAddress(String address) {
|
|||
setStringField("address", address);
|
||||
}
|
||||
|
||||
public void setPasswordEncryptKey(String pass) {
|
||||
setStringField("passwordEncryptKey", pass);
|
||||
}
|
||||
|
||||
public void setProjectName(String name) {
|
||||
setStringField("projectname", name);
|
||||
}
|
||||
|
@ -146,32 +150,4 @@ private void setBooleanField(String name, boolean b)
|
|||
constructor.instructions.add(new InsnNode(b ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, name, Type.BOOLEAN_TYPE.getInternalName()));
|
||||
}
|
||||
|
||||
public void setGuardLicense(String name, String key, String encryptKey) {
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new LdcInsnNode(name));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseName", stringDesc));
|
||||
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new LdcInsnNode(key));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseKey", stringDesc));
|
||||
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new LdcInsnNode(encryptKey));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseEncryptKey", stringDesc));
|
||||
}
|
||||
|
||||
public void nullGuardLicense() {
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseName", stringDesc));
|
||||
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseKey", stringDesc));
|
||||
|
||||
constructor.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseEncryptKey", stringDesc));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,16 +124,13 @@ public Path process(Path inputJar) throws IOException {
|
|||
BuildContext context = new BuildContext(output, launcherConfigurator, this);
|
||||
server.buildHookManager.hook(context);
|
||||
launcherConfigurator.setAddress(server.config.netty.address);
|
||||
if (server.config.guardLicense != null)
|
||||
launcherConfigurator.setGuardLicense(server.config.guardLicense.name, server.config.guardLicense.key, server.config.guardLicense.encryptKey);
|
||||
else
|
||||
launcherConfigurator.nullGuardLicense();
|
||||
launcherConfigurator.setProjectName(server.config.projectName);
|
||||
launcherConfigurator.setSecretKey(SecurityHelper.randomStringAESKey());
|
||||
launcherConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512));
|
||||
launcherConfigurator.setGuardType(server.config.launcher.guardType);
|
||||
launcherConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava);
|
||||
launcherConfigurator.setEnv(server.config.env);
|
||||
launcherConfigurator.setPasswordEncryptKey(server.runtime.passwordEncryptKey);
|
||||
String launcherSalt = SecurityHelper.randomStringToken();
|
||||
byte[] launcherSecureHash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
|
||||
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));
|
||||
|
|
|
@ -21,6 +21,6 @@ public String getUsageDescription() {
|
|||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
LogHelper.info("You publickey modulus: %s", server.publicKey.getModulus().toString(16));
|
||||
//LogHelper.info("You publickey modulus: %s", server.publicKey.getModulus().toString(16));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ public AuthProviderPair getAuthProviderPair() {
|
|||
|
||||
public ExeConf launch4j;
|
||||
public NettyConfig netty;
|
||||
public GuardLicenseConf guardLicense;
|
||||
|
||||
public String whitelistRejectString;
|
||||
public LauncherConf launcher;
|
||||
|
@ -280,12 +279,6 @@ public NettyBindAddress(String address, int port) {
|
|||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuardLicenseConf {
|
||||
public String name;
|
||||
public String key;
|
||||
public String encryptKey;
|
||||
}
|
||||
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env)
|
||||
{
|
||||
LaunchServerConfig newConfig = new LaunchServerConfig();
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
public class LaunchServerRuntimeConfig {
|
||||
public String clientToken;
|
||||
public String passwordEncryptKey;
|
||||
public String oemUnlockKey;
|
||||
public String registerApiKey;
|
||||
public String clientCheckSecret;
|
||||
|
||||
public void verify() {
|
||||
if (clientToken == null) LogHelper.error("[RuntimeConfig] clientToken must not be null");
|
||||
if (passwordEncryptKey == null) LogHelper.error("[RuntimeConfig] passwordEncryptKey must not be null");
|
||||
if (clientCheckSecret == null) { LogHelper.warning("[RuntimeConfig] clientCheckSecret must not be null"); clientCheckSecret = SecurityHelper.randomStringToken(); }
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
clientToken = SecurityHelper.randomStringToken();
|
||||
passwordEncryptKey = SecurityHelper.randomStringToken();
|
||||
registerApiKey = SecurityHelper.randomStringToken();
|
||||
clientCheckSecret = SecurityHelper.randomStringToken();
|
||||
}
|
||||
|
|
|
@ -42,13 +42,13 @@ public OshiHWID toHWID()
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSerializeString() {
|
||||
return toHWID().getSerializeString();
|
||||
public int getLevel() {
|
||||
return toHWID().getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel() {
|
||||
return toHWID().getLevel();
|
||||
public int getAntiLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,4 +60,9 @@ public int compare(HWID hwid) {
|
|||
public boolean isNull() {
|
||||
return toHWID().isNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalize() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||
import pro.gravit.launcher.request.auth.password.AuthRSAPassword;
|
||||
import pro.gravit.launcher.request.auth.password.AuthECPassword;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.hwid.HWIDException;
|
||||
|
@ -59,11 +59,11 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
AuthProvider.authError("Don't skip Launcher Update");
|
||||
return;
|
||||
}
|
||||
if(password instanceof AuthRSAPassword)
|
||||
if(password instanceof AuthECPassword)
|
||||
{
|
||||
try {
|
||||
password = new AuthPlainPassword(IOHelper.decode(SecurityHelper.newRSADecryptCipher(server.privateKey).
|
||||
doFinal(((AuthRSAPassword) password).password)));
|
||||
password = new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
||||
, ((AuthECPassword) password).password)));
|
||||
} catch (IllegalBlockSizeException | BadPaddingException ignored) {
|
||||
throw new AuthException("Password decryption error");
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
|
@ -35,9 +38,9 @@ public static void prepare() throws Exception
|
|||
Launcher.gsonManager.initGson();
|
||||
LaunchServerRuntimeConfig runtimeConfig = new LaunchServerRuntimeConfig();
|
||||
LaunchServerBuilder builder = new LaunchServerBuilder();
|
||||
KeyPair pair = SecurityHelper.genRSAKeyPair();
|
||||
RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) pair.getPrivate();
|
||||
KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
|
||||
ECPublicKey publicKey = (ECPublicKey) pair.getPublic();
|
||||
ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
|
||||
builder.setDir(dir)
|
||||
.setEnv(LaunchServer.LaunchServerEnv.TEST)
|
||||
.setConfig(config)
|
||||
|
|
|
@ -50,6 +50,7 @@ task javadocJar(type: Jar) {
|
|||
pack project(':LauncherAuthlib')
|
||||
bundle 'com.github.oshi:oshi-core:3.13.0'
|
||||
bundle 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
bundle 'org.bouncycastle:bcprov-jdk15:1.46'
|
||||
pack 'io.netty:netty-codec-http:4.1.36.Final'
|
||||
pack 'org.ow2.asm:asm-tree:7.1'
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var config = {
|
||||
//*** Настройки лаунчера ***//
|
||||
dir: "GravitLauncher", // Название папки лаунчера
|
||||
// Название папки лаунчера настраивается в LaunchServer.conf(строка projectName)
|
||||
title: "GravitLauncher", // Заголовок окна
|
||||
icons: ["favicon.png"], // Путь/Пути до иконки окна
|
||||
|
||||
|
@ -36,12 +36,6 @@ var config = {
|
|||
settingsMagic: 0xC0DE5, // Магия вне хогвартса
|
||||
};
|
||||
|
||||
DirBridge.dir = DirBridge.getLauncherDir(config.dir);
|
||||
DirBridge.dirStore = DirBridge.getStoreDir(config.dir);
|
||||
DirBridge.dirProjectStore = DirBridge.getProjectStoreDir(config.dir);
|
||||
if (!IOHelper.isDir(DirBridge.dir)) {
|
||||
java.nio.file.Files.createDirectory(DirBridge.dir);
|
||||
}
|
||||
DirBridge.defaultUpdatesDir = DirBridge.dir.resolve("updates");
|
||||
if (!IOHelper.isDir(DirBridge.defaultUpdatesDir)) {
|
||||
java.nio.file.Files.createDirectory(DirBridge.defaultUpdatesDir);
|
||||
|
|
|
@ -155,7 +155,7 @@ var settingsOverlay = {
|
|||
},
|
||||
|
||||
setPassword: function(password) {
|
||||
var encrypted = SecurityHelper.newRSAEncryptCipher(Launcher.getConfig().publicKey).doFinal(IOHelper.encode(password));
|
||||
var encrypted = FunctionalBridge.encryptPassword(password);
|
||||
return encrypted;
|
||||
},
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package pro.gravit.launcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import pro.gravit.launcher.client.ClientModuleManager;
|
||||
import pro.gravit.launcher.client.DirBridge;
|
||||
import pro.gravit.launcher.client.FunctionalBridge;
|
||||
import pro.gravit.launcher.client.LauncherUpdateController;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import pro.gravit.launcher.client.*;
|
||||
import pro.gravit.launcher.client.events.ClientEngineInitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
|
||||
import pro.gravit.launcher.guard.LauncherGuardManager;
|
||||
|
@ -22,10 +26,9 @@
|
|||
import pro.gravit.launcher.request.auth.RestoreSessionRequest;
|
||||
import pro.gravit.launcher.request.update.UpdateRequest;
|
||||
import pro.gravit.launcher.request.websockets.StandartClientWebSocketService;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.EnvHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.*;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class LauncherEngine {
|
||||
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
|
||||
|
@ -37,6 +40,12 @@ public static void main(String... args) throws Throwable {
|
|||
//if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set");
|
||||
LogHelper.printVersion("Launcher");
|
||||
LogHelper.printLicense("Launcher");
|
||||
try {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
} catch (Exception ignored)
|
||||
{
|
||||
LogHelper.warning("BouncyCastle not found");
|
||||
}
|
||||
|
||||
LauncherEngine.modulesManager = new ClientModuleManager();
|
||||
LauncherConfig.getAutogenConfig().initModules();
|
||||
|
@ -69,9 +78,33 @@ public static void initGson(ClientModuleManager modulesManager) {
|
|||
Launcher.gsonManager.initGson();
|
||||
}
|
||||
|
||||
public void readKeys() throws IOException, InvalidKeySpecException {
|
||||
if(privateKey != null || publicKey != null) return;
|
||||
Path dir = DirBridge.dir;
|
||||
Path publicKeyFile =dir.resolve("public.key");
|
||||
Path privateKeyFile = dir.resolve("private.key");
|
||||
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
|
||||
LogHelper.info("Reading EC keypair");
|
||||
publicKey = SecurityHelper.toPublicECKey(IOHelper.read(publicKeyFile));
|
||||
privateKey = SecurityHelper.toPrivateECKey(IOHelper.read(privateKeyFile));
|
||||
} else {
|
||||
LogHelper.info("Generating EC keypair");
|
||||
KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
|
||||
publicKey = (ECPublicKey) pair.getPublic();
|
||||
privateKey = (ECPrivateKey) pair.getPrivate();
|
||||
|
||||
// Write key pair list
|
||||
LogHelper.info("Writing EC keypair list");
|
||||
IOHelper.write(publicKeyFile, publicKey.getEncoded());
|
||||
IOHelper.write(privateKeyFile, privateKey.getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
// Instance
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public RuntimeProvider runtimeProvider;
|
||||
public ECPublicKey publicKey;
|
||||
public ECPrivateKey privateKey;
|
||||
|
||||
public static ClientModuleManager modulesManager;
|
||||
|
||||
|
@ -116,6 +149,7 @@ public void start(String... args) throws Throwable {
|
|||
Objects.requireNonNull(args, "args");
|
||||
if (started.getAndSet(true))
|
||||
throw new IllegalStateException("Launcher has been already started");
|
||||
readKeys();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
|
||||
runtimeProvider.preLoad();
|
||||
LauncherGuardManager.initGuard(false);
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package pro.gravit.launcher.bridge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import pro.gravit.launcher.LauncherAPI;
|
||||
|
||||
@LauncherAPI
|
||||
public class GravitGuardBridge {
|
||||
@LauncherAPI
|
||||
public static native void callGuard();
|
||||
|
||||
@LauncherAPI
|
||||
public static int sendHTTPRequest(String strurl) throws IOException {
|
||||
URL url = new URL(strurl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Content-Language", "en-US");
|
||||
return connection.getResponseCode();
|
||||
}
|
||||
}
|
|
@ -114,7 +114,7 @@ public Params(byte[] launcherDigest, Path assetDir, Path clientDir, PlayerProfil
|
|||
this.clientDir = clientDir;
|
||||
// Client params
|
||||
this.pp = pp;
|
||||
this.accessToken = SecurityHelper.verifyToken(accessToken);
|
||||
this.accessToken = accessToken;
|
||||
this.autoEnter = autoEnter;
|
||||
this.fullScreen = fullScreen;
|
||||
this.ram = ram;
|
||||
|
@ -132,8 +132,7 @@ public Params(HInput input) throws Exception {
|
|||
// Client params
|
||||
pp = new PlayerProfile(input);
|
||||
byte[] encryptedAccessToken = input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH);
|
||||
String accessTokenD = new String(SecurityHelper.decrypt(Launcher.getConfig().secretKeyClient.getBytes(), encryptedAccessToken));
|
||||
accessToken = SecurityHelper.verifyToken(accessTokenD);
|
||||
accessToken = new String(SecurityHelper.decrypt(Launcher.getConfig().secretKeyClient.getBytes(), encryptedAccessToken));
|
||||
autoEnter = input.readBoolean();
|
||||
fullScreen = input.readBoolean();
|
||||
ram = input.readVarInt();
|
||||
|
@ -458,9 +457,7 @@ public static void main(String... args) throws Throwable {
|
|||
EnvHelper.checkDangerousParams();
|
||||
JVMHelper.checkStackTrace(ClientLauncher.class);
|
||||
LogHelper.printVersion("Client Launcher");
|
||||
if (engine.runtimeProvider == null) engine.runtimeProvider = new JSRuntimeProvider();
|
||||
engine.runtimeProvider.init(true);
|
||||
engine.runtimeProvider.preLoad();
|
||||
engine.readKeys();
|
||||
HWIDProvider.registerHWIDs();
|
||||
LauncherGuardManager.initGuard(true);
|
||||
LogHelper.debug("Reading ClientLauncher params");
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherAPI;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
public class DirBridge {
|
||||
|
||||
|
@ -98,4 +100,18 @@ public static Path getLegacyLauncherDir(String projectname) {
|
|||
public static void setUseLegacyDir(boolean b) {
|
||||
useLegacyDir = b;
|
||||
}
|
||||
|
||||
static {
|
||||
String projectName = Launcher.getConfig().projectname;
|
||||
try {
|
||||
DirBridge.dir = getLauncherDir(projectName);
|
||||
if(!IOHelper.exists(DirBridge.dir)) Files.createDirectories(DirBridge.dir);
|
||||
DirBridge.dirStore = getStoreDir(projectName);
|
||||
if(!IOHelper.exists(DirBridge.dirStore)) Files.createDirectories(DirBridge.dirStore);
|
||||
DirBridge.dirProjectStore = getProjectStoreDir(projectName);
|
||||
if(!IOHelper.exists(DirBridge.dirProjectStore)) Files.createDirectories(DirBridge.dirProjectStore);
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package pro.gravit.launcher.client;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherAPI;
|
||||
import pro.gravit.launcher.api.AuthService;
|
||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
|
@ -19,7 +21,9 @@
|
|||
import pro.gravit.launcher.managers.HasherStore;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
import pro.gravit.utils.Version;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
public class FunctionalBridge {
|
||||
@LauncherAPI
|
||||
|
@ -133,4 +137,9 @@ public static String getLauncherVersion() {
|
|||
Version.BUILD
|
||||
);
|
||||
}
|
||||
@LauncherAPI
|
||||
public static byte[] encryptPassword(String string) throws Exception {
|
||||
byte[] encode = IOHelper.encode(string);
|
||||
return SecurityHelper.encrypt(Launcher.getConfig().passwordEncryptKey, encode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ public static void initGuard(boolean clientInstance) {
|
|||
LauncherConfig config = Launcher.getConfig();
|
||||
switch (config.guardType) {
|
||||
case "gravitguard": {
|
||||
guard = new LauncherGravitGuard();
|
||||
guard = new LauncherStdGuard();
|
||||
break;
|
||||
}
|
||||
case "wrapper": {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
import pro.gravit.launcher.bridge.GravitGuardBridge;
|
||||
import pro.gravit.launcher.client.ClientLauncher;
|
||||
import pro.gravit.launcher.client.ClientLauncherContext;
|
||||
import pro.gravit.launcher.client.DirBridge;
|
||||
|
@ -16,14 +15,14 @@
|
|||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.UnpackHelper;
|
||||
|
||||
//Используется для всех типов защит, совместимых с новым GravitGuard API
|
||||
public class LauncherGravitGuard implements LauncherGuardInterface {
|
||||
//Стандартный интерфейс для всех AntiInject
|
||||
public class LauncherStdGuard implements LauncherGuardInterface {
|
||||
public String protectToken;
|
||||
public Path javaBinPath;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "gravitguard";
|
||||
return "stdguard";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,7 +49,7 @@ public int getClientJVMBits() {
|
|||
public void init(boolean clientInstance) {
|
||||
try {
|
||||
String projectName = Launcher.getConfig().projectname;
|
||||
UnpackHelper.unpack(Launcher.getResourceURL("wrapper32.exe", "guard"), DirBridge.getGuardDir().resolve(projectName.concat("64.exe")));
|
||||
UnpackHelper.unpack(Launcher.getResourceURL("wrapper64.exe", "guard"), DirBridge.getGuardDir().resolve(projectName.concat("64.exe")));
|
||||
UnpackHelper.unpack(Launcher.getResourceURL("AntiInject64.dll", "guard"), DirBridge.getGuardDir().resolve("AntiInject64.dll"));
|
||||
|
||||
UnpackHelper.unpack(Launcher.getResourceURL("wrapper32.exe", "guard"), DirBridge.getGuardDir().resolve(projectName.concat("32.exe")));
|
||||
|
@ -58,7 +57,6 @@ public void init(boolean clientInstance) {
|
|||
} catch (IOException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
if (clientInstance && JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) GravitGuardBridge.callGuard();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,9 +72,7 @@ public void addCustomEnv(ClientLauncherContext context) {
|
|||
else
|
||||
env.put("JAVA_HOME", javaBinPath.toAbsolutePath().toString());
|
||||
LauncherConfig config = Launcher.getConfig();
|
||||
env.put("GUARD_BRIDGE", GravitGuardBridge.class.getName());
|
||||
env.put("GUARD_USERNAME", context.playerProfile.username);
|
||||
env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16));
|
||||
env.put("GUARD_PROJECTNAME", config.projectname);
|
||||
if (protectToken != null)
|
||||
env.put("GUARD_TOKEN", protectToken);
|
|
@ -63,7 +63,6 @@ public void addCustomEnv(ClientLauncherContext context) {
|
|||
env.put("JAVA_HOME", System.getProperty("java.home"));
|
||||
LauncherConfig config = Launcher.getConfig();
|
||||
env.put("GUARD_USERNAME", context.playerProfile.username);
|
||||
env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16));
|
||||
env.put("GUARD_PROJECTNAME", config.projectname);
|
||||
if (protectToken != null)
|
||||
env.put("GUARD_TOKEN", protectToken);
|
||||
|
|
|
@ -12,6 +12,7 @@ public class AutogenConfig {
|
|||
public String guardLicenseEncryptKey;
|
||||
public String secureCheckHash;
|
||||
public String secureCheckSalt;
|
||||
public String passwordEncryptKey;
|
||||
public int env;
|
||||
public boolean isWarningMissArchJava;
|
||||
// 0 - Dev (дебаг включен по умолчанию, все сообщения)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package pro.gravit.launcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Collections;
|
||||
|
@ -31,7 +33,7 @@ public static AutogenConfig getAutogenConfig() {
|
|||
public String secretKeyClient;
|
||||
public String oemUnlockKey;
|
||||
@LauncherAPI
|
||||
public final RSAPublicKey publicKey;
|
||||
public final ECPublicKey publicKey;
|
||||
|
||||
@LauncherAPI
|
||||
public final Map<String, byte[]> runtime;
|
||||
|
@ -46,12 +48,14 @@ public static AutogenConfig getAutogenConfig() {
|
|||
|
||||
public final String secureCheckHash;
|
||||
public final String secureCheckSalt;
|
||||
public final String passwordEncryptKey;
|
||||
|
||||
@LauncherAPI
|
||||
public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException {
|
||||
publicKey = SecurityHelper.toPublicRSAKey(input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH));
|
||||
publicKey = SecurityHelper.toPublicECKey(input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH));
|
||||
secureCheckHash = config.secureCheckHash;
|
||||
secureCheckSalt = config.secureCheckSalt;
|
||||
passwordEncryptKey = config.passwordEncryptKey;
|
||||
projectname = config.projectname;
|
||||
clientPort = config.clientPort;
|
||||
secretKeyClient = config.secretKeyClient;
|
||||
|
@ -84,7 +88,7 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
|
|||
}
|
||||
|
||||
@LauncherAPI
|
||||
public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]> runtime, String projectname) {
|
||||
public LauncherConfig(String address, ECPublicKey publicKey, Map<String, byte[]> runtime, String projectname) {
|
||||
this.address = address;
|
||||
this.publicKey = Objects.requireNonNull(publicKey, "publicKey");
|
||||
this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime));
|
||||
|
@ -99,10 +103,11 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
|
|||
environment = LauncherEnvironment.STD;
|
||||
secureCheckSalt = null;
|
||||
secureCheckHash = null;
|
||||
passwordEncryptKey = null;
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]> runtime) {
|
||||
public LauncherConfig(String address, ECPublicKey publicKey, Map<String, byte[]> runtime) {
|
||||
this.address = address;
|
||||
this.publicKey = Objects.requireNonNull(publicKey, "publicKey");
|
||||
this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime));
|
||||
|
@ -117,6 +122,7 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
|
|||
environment = LauncherEnvironment.STD;
|
||||
secureCheckSalt = null;
|
||||
secureCheckHash = null;
|
||||
passwordEncryptKey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package pro.gravit.launcher.hwid;
|
||||
|
||||
public interface HWID {
|
||||
String getSerializeString();
|
||||
|
||||
int getLevel(); //Уровень доверия, насколько уникальные значения
|
||||
|
||||
int getAntiLevel(); //Уровень лживости, насколько фальшивые значения
|
||||
|
||||
int compare(HWID hwid);
|
||||
|
||||
boolean isNull();
|
||||
|
||||
void normalize();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package pro.gravit.launcher.hwid;
|
||||
|
||||
public class HWIDCheckHelper {
|
||||
public static int checkString(String str)
|
||||
{
|
||||
int result = 0;
|
||||
//Считаем символы
|
||||
char lastChar = '\0';
|
||||
int combo = 0;
|
||||
int maxCombo = 0;
|
||||
//Считаем род символов
|
||||
int lastCharType = -1;
|
||||
int lastCharTypeCombo = 0;
|
||||
int wtfCharTypeCombo = 0;
|
||||
boolean skipLastCharType = true;
|
||||
for(char c : str.toCharArray())
|
||||
{
|
||||
if(c == lastChar || Math.abs(c - lastChar) == 1 ||
|
||||
( ( lastChar == '0' || lastChar == '9' ) && ( c == 'A' || c == 'a' ))) //Переход с 0 или 9 на A или a
|
||||
{
|
||||
lastChar = c;
|
||||
combo++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(maxCombo < combo)
|
||||
maxCombo = combo;
|
||||
lastChar = c;
|
||||
combo = 1;
|
||||
}
|
||||
int charType = getCharType(c);
|
||||
if(lastCharType == charType) {
|
||||
lastCharTypeCombo++;
|
||||
//Нам подсунули серию из идущих подряд спец символов. Что за?
|
||||
if((charType == -1 || charType == 3) && lastCharTypeCombo > 2)
|
||||
{
|
||||
wtfCharTypeCombo+=3;
|
||||
}
|
||||
//Нам подсунули серию из слишком большого числа идущих подряд чисел. Что за?
|
||||
if((charType == 0) && lastCharTypeCombo > 4)
|
||||
{
|
||||
wtfCharTypeCombo++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(skipLastCharType && ( charType == -1 || charType == 3 ))
|
||||
{
|
||||
skipLastCharType = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
skipLastCharType = true;
|
||||
lastCharType = charType;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Считаем результат
|
||||
if(maxCombo > 3) result+= maxCombo * 3;
|
||||
if(wtfCharTypeCombo > 1) result+= wtfCharTypeCombo * 2;
|
||||
return result;
|
||||
}
|
||||
public static int getCharType(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9') return 0;
|
||||
if(c >= 'A' && c <= 'Z') return 1;
|
||||
if(c >= 'a' && c <= 'z') return 2;
|
||||
if(c == ' ' || c == '-' || c == '_') return 3;
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
package pro.gravit.launcher.hwid;
|
||||
|
||||
public class NoHWID implements HWID {
|
||||
@Override
|
||||
public String getSerializeString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAntiLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(HWID hwid) {
|
||||
return 0;
|
||||
|
@ -20,4 +21,9 @@ public int compare(HWID hwid) {
|
|||
public boolean isNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalize() {
|
||||
//Skip
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,33 +20,33 @@ public class OshiHWID implements HWID {
|
|||
@LauncherAPI
|
||||
public String macAddr;
|
||||
|
||||
@Override
|
||||
public String getSerializeString() {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel() //Уровень доверия, насколько уникальные значения
|
||||
{
|
||||
int result = 0;
|
||||
if (totalMemory != 0) result += 8;
|
||||
if (serialNumber != null && !serialNumber.equals("unknown")) result += 12;
|
||||
if (HWDiskSerial != null && !HWDiskSerial.equals("unknown")) result += 30;
|
||||
if (processorID != null && !processorID.equals("unknown")) result += 10;
|
||||
if (macAddr != null && !macAddr.equals("00:00:00:00:00:00")) result += 15;
|
||||
if (totalMemory != 0) result += 32;
|
||||
if (serialNumber != null) result += isRealSerialNumber() ? 20 : 3;
|
||||
if (HWDiskSerial != null && !HWDiskSerial.isEmpty()) result += 38;
|
||||
if (processorID != null && !processorID.isEmpty()) result += 15;
|
||||
if (macAddr != null && !macAddr.isEmpty()) result += 25;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAntiLevel() {
|
||||
return HWIDCheckHelper.checkString(serialNumber) + HWIDCheckHelper.checkString(HWDiskSerial);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(HWID hwid) {
|
||||
if (hwid instanceof OshiHWID) {
|
||||
int rate = 0;
|
||||
OshiHWID oshi = (OshiHWID) hwid;
|
||||
if (Math.abs(oshi.totalMemory - totalMemory) < 1024 * 1024) rate += 5;
|
||||
if (oshi.totalMemory == totalMemory) rate += 15;
|
||||
if (oshi.HWDiskSerial.equals(HWDiskSerial)) rate += 45;
|
||||
if (oshi.processorID.equals(processorID)) rate += 18;
|
||||
if (oshi.serialNumber.equals(serialNumber)) rate += 15;
|
||||
if (oshi.totalMemory == totalMemory) rate += 32;
|
||||
if (oshi.HWDiskSerial.equals(HWDiskSerial) && !HWDiskSerial.isEmpty()) rate += 38;
|
||||
if (oshi.processorID.equals(processorID) && !processorID.isEmpty()) rate += 15;
|
||||
if (oshi.serialNumber.equals(serialNumber)) rate += isRealSerialNumber() ? 20 : 3;
|
||||
if (!oshi.macAddr.isEmpty() && oshi.macAddr.equals(macAddr)) rate += 19;
|
||||
return rate;
|
||||
}
|
||||
|
@ -58,6 +58,24 @@ public boolean isNull() {
|
|||
return getLevel() < 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalize() {
|
||||
HWDiskSerial = HWDiskSerial.trim();
|
||||
serialNumber = serialNumber.trim();
|
||||
processorID = processorID.trim();
|
||||
macAddr = macAddr.trim();
|
||||
}
|
||||
public boolean isRealSerialNumber()
|
||||
{
|
||||
if(serialNumber.isEmpty()) return false;
|
||||
if(serialNumber.equals("System Serial Number")) return false;
|
||||
if(serialNumber.equals("To be filled by O.E.M.")) return false;
|
||||
if(serialNumber.equals("unknown")) return false;
|
||||
if(serialNumber.equals("None")) return false;
|
||||
if(serialNumber.equals("Default string")) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import pro.gravit.launcher.hwid.HWID;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||
import pro.gravit.launcher.request.auth.password.AuthRSAPassword;
|
||||
import pro.gravit.launcher.request.auth.password.AuthECPassword;
|
||||
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
import pro.gravit.utils.helper.VerifyHelper;
|
||||
|
@ -48,7 +48,7 @@ public enum ConnectTypes {
|
|||
@LauncherAPI
|
||||
public AuthRequest(String login, byte[] password, HWID hwid) {
|
||||
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty");
|
||||
this.password = new AuthRSAPassword(password.clone());
|
||||
this.password = new AuthECPassword(password.clone());
|
||||
this.hwid = hwid;
|
||||
customText = "";
|
||||
auth_id = "";
|
||||
|
@ -59,7 +59,7 @@ public AuthRequest(String login, byte[] password, HWID hwid) {
|
|||
@LauncherAPI
|
||||
public AuthRequest(String login, byte[] password, HWID hwid, String auth_id) {
|
||||
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty");
|
||||
this.password = new AuthRSAPassword(password.clone());
|
||||
this.password = new AuthECPassword(password.clone());
|
||||
this.hwid = hwid;
|
||||
this.auth_id = auth_id;
|
||||
customText = "";
|
||||
|
@ -70,7 +70,7 @@ public AuthRequest(String login, byte[] password, HWID hwid, String auth_id) {
|
|||
@LauncherAPI
|
||||
public AuthRequest(String login, byte[] password, HWID hwid, String customText, String auth_id) {
|
||||
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty");
|
||||
this.password = new AuthRSAPassword(password.clone());
|
||||
this.password = new AuthECPassword(password.clone());
|
||||
this.hwid = hwid;
|
||||
this.auth_id = auth_id;
|
||||
this.customText = customText;
|
||||
|
@ -80,7 +80,7 @@ public AuthRequest(String login, byte[] password, HWID hwid, String customText,
|
|||
|
||||
public AuthRequest(String login, byte[] encryptedPassword, String auth_id, ConnectTypes authType) {
|
||||
this.login = login;
|
||||
this.password = new AuthRSAPassword(encryptedPassword.clone());
|
||||
this.password = new AuthECPassword(encryptedPassword.clone());
|
||||
this.auth_id = auth_id;
|
||||
this.authType = authType;
|
||||
this.hwid = null;
|
||||
|
@ -106,7 +106,7 @@ public String getType() {
|
|||
public static void registerProviders() {
|
||||
if(!registerProviders) {
|
||||
providers.register("plain", AuthPlainPassword.class);
|
||||
providers.register("rsa", AuthRSAPassword.class);
|
||||
providers.register("rsa", AuthECPassword.class);
|
||||
registerProviders = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
import pro.gravit.launcher.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
|
||||
public class AuthRSAPassword implements AuthRequest.AuthPasswordInterface {
|
||||
public class AuthECPassword implements AuthRequest.AuthPasswordInterface {
|
||||
@LauncherNetworkAPI
|
||||
public final byte[] password;
|
||||
|
||||
public AuthRSAPassword(byte[] password) {
|
||||
public AuthECPassword(byte[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
compileOnly 'org.jline:jline:3.11.0'
|
||||
compileOnly 'org.jline:jline-reader:3.11.0'
|
||||
compileOnly 'org.jline:jline-terminal:3.11.0'
|
||||
compileOnly 'org.bouncycastle:bcprov-jdk15:1.46'
|
||||
compile 'com.google.code.gson:gson:2.8.5'
|
||||
testCompile 'org.junit.jupiter:junit-jupiter:5.4.1'
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
@ -110,6 +111,13 @@ public static URL[] getClassPathURL() {
|
|||
return list;
|
||||
}
|
||||
|
||||
public static X509Certificate[] getCertificates(Class<?> clazz)
|
||||
{
|
||||
Object[] signers = clazz.getSigners();
|
||||
if(signers == null) return new X509Certificate[] {};
|
||||
return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new);
|
||||
}
|
||||
|
||||
public static void checkStackTrace(Class<?> mainClass) {
|
||||
LogHelper.debug("Testing stacktrace");
|
||||
Exception e = new Exception("Testing stacktrace");
|
||||
|
|
|
@ -4,19 +4,9 @@
|
|||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
@ -30,6 +20,8 @@
|
|||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.jce.provider.JCEIESCipher;
|
||||
import pro.gravit.launcher.LauncherAPI;
|
||||
|
||||
public final class SecurityHelper {
|
||||
|
@ -77,14 +69,17 @@ public byte[] verify(byte[] digest) {
|
|||
}
|
||||
}
|
||||
|
||||
// Algorithm constants
|
||||
// EC Algorithm constants
|
||||
|
||||
public static final String RSA_ALGO = "RSA";
|
||||
public static final String EC_ALGO = "EC";
|
||||
|
||||
public static final String RSA_SIGN_ALGO = "SHA256withRSA";
|
||||
public static final String EC_CURVE = "secp256k1";
|
||||
|
||||
public static final String EC_SIGN_ALGO = "SHA256withECDSA";
|
||||
|
||||
public static final String RSA_CIPHER_ALGO = "RSA/ECB/PKCS1Padding";
|
||||
public static final String EC_CIPHER_ALGO = "ECIES";
|
||||
|
||||
// RSA Algorithm constants
|
||||
// Algorithm size constants
|
||||
|
||||
public static final int TOKEN_LENGTH = 16;
|
||||
|
@ -144,25 +139,19 @@ public static byte[] digest(DigestAlgorithm algo, URL url) throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static KeyPair genRSAKeyPair() {
|
||||
return genRSAKeyPair(newRandom());
|
||||
}
|
||||
|
||||
|
||||
public static KeyPair genRSAKeyPair(SecureRandom random) {
|
||||
public static KeyPair genECKeyPair(SecureRandom random) {
|
||||
try {
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGO);
|
||||
generator.initialize(RSA_KEY_LENGTH_BITS, random);
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance(EC_ALGO);
|
||||
generator.initialize(new ECGenParameterSpec(EC_CURVE), random);
|
||||
return generator.genKeyPair();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidSign(byte[] bytes, byte[] sign, RSAPublicKey publicKey) throws SignatureException {
|
||||
Signature signature = newRSAVerifySignature(publicKey);
|
||||
public static boolean isValidSign(byte[] bytes, byte[] sign, ECPublicKey publicKey) throws SignatureException {
|
||||
Signature signature = newECVerifySignature(publicKey);
|
||||
try {
|
||||
signature.update(bytes);
|
||||
} catch (SignatureException e) {
|
||||
|
@ -172,33 +161,18 @@ public static boolean isValidSign(byte[] bytes, byte[] sign, RSAPublicKey public
|
|||
}
|
||||
|
||||
|
||||
public static boolean isValidSign(InputStream input, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
||||
Signature signature = newRSAVerifySignature(publicKey);
|
||||
public static boolean isValidSign(InputStream input, byte[] sign, ECPublicKey publicKey) throws IOException, SignatureException {
|
||||
Signature signature = newECVerifySignature(publicKey);
|
||||
updateSignature(input, signature);
|
||||
return signature.verify(sign);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidSign(Path path, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
||||
try (InputStream input = IOHelper.newInput(path)) {
|
||||
return isValidSign(input, sign, publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidSign(URL url, byte[] sign, RSAPublicKey publicKey) throws IOException, SignatureException {
|
||||
try (InputStream input = IOHelper.newInput(url)) {
|
||||
return isValidSign(input, sign, publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidToken(CharSequence token) {
|
||||
return token.length() == TOKEN_STRING_LENGTH && token.chars().allMatch(ch -> HEX.indexOf(ch) >= 0);
|
||||
}
|
||||
|
||||
private static Cipher newCipher(String algo) {
|
||||
// IDK Why, but collapsing catch blocks makes ProGuard generate invalid stackmap
|
||||
try {
|
||||
return Cipher.getInstance(algo);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
|
@ -206,6 +180,19 @@ private static Cipher newCipher(String algo) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param algo Cipher algo
|
||||
* @return Cipher instance
|
||||
* @throws SecurityException: JCE cannot authenticate the provider BC if BouncyCastle is in unsigned jar
|
||||
*/
|
||||
private static Cipher newBCCipher(String algo) {
|
||||
try {
|
||||
return Cipher.getInstance(algo, new BouncyCastleProvider());
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static MessageDigest newDigest(DigestAlgorithm algo) {
|
||||
VerifyHelper.verify(algo, a -> a != DigestAlgorithm.PLAIN, "PLAIN digest");
|
||||
|
@ -221,8 +208,8 @@ public static SecureRandom newRandom() {
|
|||
return new SecureRandom();
|
||||
}
|
||||
|
||||
private static Cipher newRSACipher(int mode, RSAKey key) {
|
||||
Cipher cipher = newCipher(RSA_CIPHER_ALGO);
|
||||
private static Cipher newECCipher(int mode, ECKey key) {
|
||||
Cipher cipher = newBCCipher(EC_CIPHER_ALGO);
|
||||
try {
|
||||
cipher.init(mode, (Key) key);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -231,35 +218,24 @@ private static Cipher newRSACipher(int mode, RSAKey key) {
|
|||
return cipher;
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public static Cipher newRSADecryptCipher(RSAPrivateKey key) {
|
||||
return newRSACipher(Cipher.DECRYPT_MODE, key);
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public static Cipher newRSAEncryptCipher(RSAPublicKey key) {
|
||||
return newRSACipher(Cipher.ENCRYPT_MODE, key);
|
||||
}
|
||||
|
||||
private static KeyFactory newRSAKeyFactory() {
|
||||
private static KeyFactory newECKeyFactory() {
|
||||
try {
|
||||
return KeyFactory.getInstance(RSA_ALGO);
|
||||
return KeyFactory.getInstance(EC_ALGO);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Signature newRSASignature() {
|
||||
private static Signature newECSignature() {
|
||||
try {
|
||||
return Signature.getInstance(RSA_SIGN_ALGO);
|
||||
return Signature.getInstance(EC_SIGN_ALGO);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Signature newRSASignSignature(RSAPrivateKey key) {
|
||||
Signature signature = newRSASignature();
|
||||
public static Signature newECSignSignature(ECPrivateKey key) {
|
||||
Signature signature = newECSignature();
|
||||
try {
|
||||
signature.initSign(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -269,8 +245,8 @@ public static Signature newRSASignSignature(RSAPrivateKey key) {
|
|||
}
|
||||
|
||||
|
||||
public static Signature newRSAVerifySignature(RSAPublicKey key) {
|
||||
Signature signature = newRSASignature();
|
||||
public static Signature newECVerifySignature(ECPublicKey key) {
|
||||
Signature signature = newECSignature();
|
||||
try {
|
||||
signature.initVerify(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -394,9 +370,8 @@ public static String randomUsername(Random random) {
|
|||
return VerifyHelper.verifyUsername(prefix + new String(chars) + suffix);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) {
|
||||
Signature signature = newRSASignSignature(privateKey);
|
||||
public static byte[] sign(byte[] bytes, ECPrivateKey privateKey) {
|
||||
Signature signature = newECSignSignature(privateKey);
|
||||
try {
|
||||
signature.update(bytes);
|
||||
return signature.sign();
|
||||
|
@ -405,23 +380,6 @@ public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) {
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] sign(InputStream input, RSAPrivateKey privateKey) throws IOException {
|
||||
Signature signature = newRSASignSignature(privateKey);
|
||||
updateSignature(input, signature);
|
||||
try {
|
||||
return signature.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] sign(Path path, RSAPrivateKey privateKey) throws IOException {
|
||||
try (InputStream input = IOHelper.newInput(path)) {
|
||||
return sign(input, privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toHex(byte[] bytes) {
|
||||
int offset = 0;
|
||||
|
@ -436,13 +394,12 @@ public static String toHex(byte[] bytes) {
|
|||
return new String(hex);
|
||||
}
|
||||
|
||||
|
||||
public static RSAPrivateKey toPrivateRSAKey(byte[] bytes) throws InvalidKeySpecException {
|
||||
return (RSAPrivateKey) newRSAKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes));
|
||||
public static ECPublicKey toPublicECKey(byte[] bytes) throws InvalidKeySpecException {
|
||||
return (ECPublicKey) newECKeyFactory().generatePublic(new X509EncodedKeySpec(bytes));
|
||||
}
|
||||
|
||||
public static RSAPublicKey toPublicRSAKey(byte[] bytes) throws InvalidKeySpecException {
|
||||
return (RSAPublicKey) newRSAKeyFactory().generatePublic(new X509EncodedKeySpec(bytes));
|
||||
public static ECPrivateKey toPrivateECKey(byte[] bytes) throws InvalidKeySpecException {
|
||||
return (ECPrivateKey) newECKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes));
|
||||
}
|
||||
|
||||
private static void updateSignature(InputStream input, Signature signature) throws IOException {
|
||||
|
@ -456,34 +413,41 @@ private static void updateSignature(InputStream input, Signature signature) thro
|
|||
}
|
||||
|
||||
|
||||
public static void verifySign(byte[] bytes, byte[] sign, RSAPublicKey publicKey) throws SignatureException {
|
||||
public static void verifySign(byte[] bytes, byte[] sign, ECPublicKey publicKey) throws SignatureException {
|
||||
if (!isValidSign(bytes, sign, publicKey))
|
||||
throw new SignatureException("Invalid sign");
|
||||
}
|
||||
|
||||
|
||||
public static void verifySign(InputStream input, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
||||
public static void verifySign(InputStream input, byte[] sign, ECPublicKey publicKey) throws SignatureException, IOException {
|
||||
if (!isValidSign(input, sign, publicKey))
|
||||
throw new SignatureException("Invalid stream sign");
|
||||
}
|
||||
|
||||
|
||||
public static void verifySign(Path path, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
||||
if (!isValidSign(path, sign, publicKey))
|
||||
throw new SignatureException(String.format("Invalid file sign: '%s'", path));
|
||||
}
|
||||
|
||||
|
||||
public static void verifySign(URL url, byte[] sign, RSAPublicKey publicKey) throws SignatureException, IOException {
|
||||
if (!isValidSign(url, sign, publicKey))
|
||||
throw new SignatureException(String.format("Invalid URL sign: '%s'", url));
|
||||
}
|
||||
|
||||
|
||||
public static String verifyToken(String token) {
|
||||
return VerifyHelper.verify(token, SecurityHelper::isValidToken, String.format("Invalid token: '%s'", token));
|
||||
}
|
||||
|
||||
public static Cipher newECDecryptCipher(ECPrivateKey privateKey)
|
||||
{
|
||||
try {
|
||||
return newECCipher(Cipher.DECRYPT_MODE, privateKey);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
public static Cipher newECEncryptCipher(ECPublicKey publicKey)
|
||||
{
|
||||
try {
|
||||
return newECCipher(Cipher.ENCRYPT_MODE, publicKey);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityHelper() {
|
||||
}
|
||||
|
||||
|
@ -521,6 +485,9 @@ public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
|
|||
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
|
||||
return cipher.doFinal(encrypted);
|
||||
}
|
||||
public static byte[] decrypt(String seed, byte[] encrypted) throws Exception {
|
||||
return decrypt( getRawKey(seed.getBytes()), encrypted);
|
||||
}
|
||||
|
||||
public static byte[] HexToByte(String hexString) {
|
||||
int len = hexString.length() / 2;
|
||||
|
|
|
@ -52,7 +52,7 @@ public ServerWrapper(Type type, Path configPath) {
|
|||
public boolean auth() {
|
||||
try {
|
||||
LauncherConfig cfg = Launcher.getConfig();
|
||||
AuthRequest request = new AuthRequest(config.login, SecurityHelper.newRSAEncryptCipher(cfg.publicKey).doFinal(IOHelper.encode(config.password)), config.auth_id, AuthRequest.ConnectTypes.SERVER);
|
||||
AuthRequest request = new AuthRequest(config.login, SecurityHelper.newECEncryptCipher(cfg.publicKey).doFinal(IOHelper.encode(config.password)), config.auth_id, AuthRequest.ConnectTypes.SERVER);
|
||||
permissions = request.request().permissions;
|
||||
ProfilesRequestEvent result = new ProfilesRequest().request();
|
||||
for (ClientProfile p : result.profiles) {
|
||||
|
@ -190,7 +190,7 @@ public void updateLauncherConfig() {
|
|||
|
||||
LauncherConfig cfg = null;
|
||||
try {
|
||||
cfg = new LauncherConfig(config.address, SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile)), new HashMap<>(), config.projectname);
|
||||
cfg = new LauncherConfig(config.address, SecurityHelper.toPublicECKey(IOHelper.read(publicKeyFile)), new HashMap<>(), config.projectname);
|
||||
cfg.isNettyEnabled = true;
|
||||
cfg.address = config.address;
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
|
|
Loading…
Reference in a new issue