[FEATURE] переход на EC ключи. Отказ от RSA

This commit is contained in:
Gravit 2019-10-16 16:38:44 +07:00
parent 897f799aac
commit ce28ac4057
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
23 changed files with 164 additions and 167 deletions

View file

@ -13,6 +13,8 @@
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
@ -220,9 +222,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
public LaunchServerRuntimeConfig runtime; public LaunchServerRuntimeConfig runtime;
public final RSAPublicKey publicKey; public final ECPublicKey publicKey;
public final RSAPrivateKey privateKey; public final ECPrivateKey privateKey;
// Launcher binary // Launcher binary
public final JARLauncherBinary launcherBinary; 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.dir = directories.dir;
this.env = env; this.env = env;
this.config = config; this.config = config;
@ -306,9 +308,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this)); modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this));
// Print keypair fingerprints // 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. // Load class bindings.
launcherEXEBinaryClass = defaultLauncherEXEBinaryClass; launcherEXEBinaryClass = defaultLauncherEXEBinaryClass;

View file

@ -1,6 +1,8 @@
package pro.gravit.launchserver; package pro.gravit.launchserver;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
@ -16,8 +18,8 @@ public class LaunchServerBuilder {
private LaunchServer.LaunchServerEnv env; private LaunchServer.LaunchServerEnv env;
private LaunchServerModulesManager modulesManager; private LaunchServerModulesManager modulesManager;
private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories(); private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
private RSAPublicKey publicKey; private ECPublicKey publicKey;
private RSAPrivateKey privateKey; private ECPrivateKey privateKey;
private LaunchServer.LaunchServerConfigManager launchServerConfigManager; private LaunchServer.LaunchServerConfigManager launchServerConfigManager;
public LaunchServerBuilder setConfig(LaunchServerConfig config) { public LaunchServerBuilder setConfig(LaunchServerConfig config) {
@ -55,12 +57,12 @@ public LaunchServerBuilder setDir(Path dir) {
return this; return this;
} }
public LaunchServerBuilder setPublicKey(RSAPublicKey publicKey) { public LaunchServerBuilder setPublicKey(ECPublicKey publicKey) {
this.publicKey = publicKey; this.publicKey = publicKey;
return this; return this;
} }
public LaunchServerBuilder setPrivateKey(RSAPrivateKey privateKey) { public LaunchServerBuilder setPrivateKey(ECPrivateKey privateKey) {
this.privateKey = privateKey; this.privateKey = privateKey;
return this; return this;
} }

View file

@ -7,9 +7,12 @@
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey; import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey; 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.Launcher;
import pro.gravit.launcher.hwid.HWIDProvider; import pro.gravit.launcher.hwid.HWIDProvider;
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.modules.events.PreConfigPhase;
@ -35,8 +38,11 @@
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import javax.crypto.Cipher;
public class LaunchServerStarter { public class LaunchServerStarter {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
JVMHelper.checkStackTrace(LaunchServerStarter.class); JVMHelper.checkStackTrace(LaunchServerStarter.class);
JVMHelper.verifySystemProperties(LaunchServer.class, true); JVMHelper.verifySystemProperties(LaunchServer.class, true);
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log")); LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
@ -51,8 +57,8 @@ public static void main(String[] args) throws Exception {
Path configFile, runtimeConfigFile; Path configFile, runtimeConfigFile;
Path publicKeyFile =dir.resolve("public.key"); Path publicKeyFile =dir.resolve("public.key");
Path privateKeyFile = dir.resolve("private.key"); Path privateKeyFile = dir.resolve("private.key");
RSAPublicKey publicKey; ECPublicKey publicKey;
RSAPrivateKey privateKey; ECPrivateKey privateKey;
LaunchServerRuntimeConfig runtimeConfig; LaunchServerRuntimeConfig runtimeConfig;
LaunchServerConfig config; LaunchServerConfig config;
@ -84,19 +90,17 @@ public static void main(String[] args) throws Exception {
LogHelper.warning("JLine2 isn't in classpath, using std"); LogHelper.warning("JLine2 isn't in classpath, using std");
} }
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) { if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
LogHelper.info("Reading RSA keypair"); LogHelper.info("Reading EC keypair");
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile)); publicKey = SecurityHelper.toPublicECKey(IOHelper.read(publicKeyFile));
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile)); privateKey = SecurityHelper.toPrivateECKey(IOHelper.read(privateKeyFile));
if (!publicKey.getModulus().equals(privateKey.getModulus()))
throw new IOException("Private and public key modulus mismatch");
} else { } else {
LogHelper.info("Generating RSA keypair"); LogHelper.info("Generating EC keypair");
KeyPair pair = SecurityHelper.genRSAKeyPair(); KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
publicKey = (RSAPublicKey) pair.getPublic(); publicKey = (ECPublicKey) pair.getPublic();
privateKey = (RSAPrivateKey) pair.getPrivate(); privateKey = (ECPrivateKey) pair.getPrivate();
// Write key pair list // Write key pair list
LogHelper.info("Writing RSA keypair list"); LogHelper.info("Writing EC keypair list");
IOHelper.write(publicKeyFile, publicKey.getEncoded()); IOHelper.write(publicKeyFile, publicKey.getEncoded());
IOHelper.write(privateKeyFile, privateKey.getEncoded()); IOHelper.write(privateKeyFile, privateKey.getEncoded());
} }

View file

@ -65,6 +65,10 @@ public void setAddress(String address) {
setStringField("address", address); setStringField("address", address);
} }
public void setPasswordEncryptKey(String pass) {
setStringField("passwordEncryptKey", pass);
}
public void setProjectName(String name) { public void setProjectName(String name) {
setStringField("projectname", name); setStringField("projectname", name);
} }

View file

@ -134,6 +134,7 @@ public Path process(Path inputJar) throws IOException {
launcherConfigurator.setGuardType(server.config.launcher.guardType); launcherConfigurator.setGuardType(server.config.launcher.guardType);
launcherConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava); launcherConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava);
launcherConfigurator.setEnv(server.config.env); launcherConfigurator.setEnv(server.config.env);
launcherConfigurator.setPasswordEncryptKey(server.runtime.passwordEncryptKey);
String launcherSalt = SecurityHelper.randomStringToken(); String launcherSalt = SecurityHelper.randomStringToken();
byte[] launcherSecureHash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256, byte[] launcherSecureHash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt)); server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));

View file

@ -21,6 +21,6 @@ public String getUsageDescription() {
@Override @Override
public void invoke(String... args) throws Exception { 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));
} }
} }

View file

@ -4,18 +4,18 @@
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
public class LaunchServerRuntimeConfig { public class LaunchServerRuntimeConfig {
public String clientToken; public String passwordEncryptKey;
public String oemUnlockKey; public String oemUnlockKey;
public String registerApiKey; public String registerApiKey;
public String clientCheckSecret; public String clientCheckSecret;
public void verify() { 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(); } if (clientCheckSecret == null) { LogHelper.warning("[RuntimeConfig] clientCheckSecret must not be null"); clientCheckSecret = SecurityHelper.randomStringToken(); }
} }
public void reset() { public void reset() {
clientToken = SecurityHelper.randomStringToken(); passwordEncryptKey = SecurityHelper.randomStringToken();
registerApiKey = SecurityHelper.randomStringToken(); registerApiKey = SecurityHelper.randomStringToken();
clientCheckSecret = SecurityHelper.randomStringToken(); clientCheckSecret = SecurityHelper.randomStringToken();
} }

View file

@ -14,7 +14,7 @@
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; 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.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.hwid.HWIDException; 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"); AuthProvider.authError("Don't skip Launcher Update");
return; return;
} }
if(password instanceof AuthRSAPassword) if(password instanceof AuthECPassword)
{ {
try { try {
password = new AuthPlainPassword(IOHelper.decode(SecurityHelper.newRSADecryptCipher(server.privateKey). password = new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
doFinal(((AuthRSAPassword) password).password))); , ((AuthECPassword) password).password)));
} catch (IllegalBlockSizeException | BadPaddingException ignored) { } catch (IllegalBlockSizeException | BadPaddingException ignored) {
throw new AuthException("Password decryption error"); throw new AuthException("Password decryption error");
} }

View file

@ -3,6 +3,9 @@
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.KeyPair; 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.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
@ -35,9 +38,9 @@ public static void prepare() throws Exception
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
LaunchServerRuntimeConfig runtimeConfig = new LaunchServerRuntimeConfig(); LaunchServerRuntimeConfig runtimeConfig = new LaunchServerRuntimeConfig();
LaunchServerBuilder builder = new LaunchServerBuilder(); LaunchServerBuilder builder = new LaunchServerBuilder();
KeyPair pair = SecurityHelper.genRSAKeyPair(); KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom());
RSAPublicKey publicKey = (RSAPublicKey) pair.getPublic(); ECPublicKey publicKey = (ECPublicKey) pair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) pair.getPrivate(); ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
builder.setDir(dir) builder.setDir(dir)
.setEnv(LaunchServer.LaunchServerEnv.TEST) .setEnv(LaunchServer.LaunchServerEnv.TEST)
.setConfig(config) .setConfig(config)

View file

@ -50,6 +50,7 @@ task javadocJar(type: Jar) {
pack project(':LauncherAuthlib') pack project(':LauncherAuthlib')
bundle 'com.github.oshi:oshi-core:3.13.0' bundle 'com.github.oshi:oshi-core:3.13.0'
bundle 'org.apache.httpcomponents:httpclient:4.5.7' 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 'io.netty:netty-codec-http:4.1.36.Final'
pack 'org.ow2.asm:asm-tree:7.1' pack 'org.ow2.asm:asm-tree:7.1'
} }

View file

@ -155,7 +155,7 @@ var settingsOverlay = {
}, },
setPassword: function(password) { setPassword: function(password) {
var encrypted = SecurityHelper.newRSAEncryptCipher(Launcher.getConfig().publicKey).doFinal(IOHelper.encode(password)); var encrypted = FunctionalBridge.encryptPassword(password);
return encrypted; return encrypted;
}, },

View file

@ -1,12 +1,11 @@
package pro.gravit.launcher; package pro.gravit.launcher;
import java.security.*;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import pro.gravit.launcher.client.ClientModuleManager; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import pro.gravit.launcher.client.DirBridge; import pro.gravit.launcher.client.*;
import pro.gravit.launcher.client.FunctionalBridge;
import pro.gravit.launcher.client.LauncherUpdateController;
import pro.gravit.launcher.client.events.ClientEngineInitPhase; import pro.gravit.launcher.client.events.ClientEngineInitPhase;
import pro.gravit.launcher.client.events.ClientPreGuiPhase; import pro.gravit.launcher.client.events.ClientPreGuiPhase;
import pro.gravit.launcher.guard.LauncherGuardManager; import pro.gravit.launcher.guard.LauncherGuardManager;
@ -22,10 +21,9 @@
import pro.gravit.launcher.request.auth.RestoreSessionRequest; import pro.gravit.launcher.request.auth.RestoreSessionRequest;
import pro.gravit.launcher.request.update.UpdateRequest; import pro.gravit.launcher.request.update.UpdateRequest;
import pro.gravit.launcher.request.websockets.StandartClientWebSocketService; import pro.gravit.launcher.request.websockets.StandartClientWebSocketService;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.*;
import pro.gravit.utils.helper.EnvHelper;
import pro.gravit.utils.helper.JVMHelper; import javax.crypto.Cipher;
import pro.gravit.utils.helper.LogHelper;
public class LauncherEngine { public class LauncherEngine {
@ -36,6 +34,7 @@ public static void main(String... args) throws Throwable {
//if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set"); //if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set");
LogHelper.printVersion("Launcher"); LogHelper.printVersion("Launcher");
LogHelper.printLicense("Launcher"); LogHelper.printLicense("Launcher");
Security.addProvider(new BouncyCastleProvider());
LauncherEngine.modulesManager = new ClientModuleManager(); LauncherEngine.modulesManager = new ClientModuleManager();
LauncherConfig.getAutogenConfig().initModules(); LauncherConfig.getAutogenConfig().initModules();

View file

@ -1,11 +1,13 @@
package pro.gravit.launcher.client; package pro.gravit.launcher.client;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherAPI; import pro.gravit.launcher.LauncherAPI;
import pro.gravit.launcher.api.AuthService; import pro.gravit.launcher.api.AuthService;
import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.events.request.AuthRequestEvent;
@ -19,7 +21,9 @@
import pro.gravit.launcher.managers.HasherStore; import pro.gravit.launcher.managers.HasherStore;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.utils.Version; import pro.gravit.utils.Version;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
public class FunctionalBridge { public class FunctionalBridge {
@LauncherAPI @LauncherAPI
@ -133,4 +137,9 @@ public static String getLauncherVersion() {
Version.BUILD Version.BUILD
); );
} }
@LauncherAPI
public static byte[] encryptPassword(String string) throws Exception {
byte[] encode = IOHelper.encode(string);
return SecurityHelper.encrypt(Launcher.getConfig().passwordEncryptKey, encode);
}
} }

View file

@ -76,7 +76,7 @@ public void addCustomEnv(ClientLauncherContext context) {
LauncherConfig config = Launcher.getConfig(); LauncherConfig config = Launcher.getConfig();
env.put("GUARD_BRIDGE", GravitGuardBridge.class.getName()); env.put("GUARD_BRIDGE", GravitGuardBridge.class.getName());
env.put("GUARD_USERNAME", context.playerProfile.username); env.put("GUARD_USERNAME", context.playerProfile.username);
env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16)); //env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16));
env.put("GUARD_PROJECTNAME", config.projectname); env.put("GUARD_PROJECTNAME", config.projectname);
if (protectToken != null) if (protectToken != null)
env.put("GUARD_TOKEN", protectToken); env.put("GUARD_TOKEN", protectToken);

View file

@ -63,7 +63,7 @@ public void addCustomEnv(ClientLauncherContext context) {
env.put("JAVA_HOME", System.getProperty("java.home")); env.put("JAVA_HOME", System.getProperty("java.home"));
LauncherConfig config = Launcher.getConfig(); LauncherConfig config = Launcher.getConfig();
env.put("GUARD_USERNAME", context.playerProfile.username); env.put("GUARD_USERNAME", context.playerProfile.username);
env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16)); //env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16));
env.put("GUARD_PROJECTNAME", config.projectname); env.put("GUARD_PROJECTNAME", config.projectname);
if (protectToken != null) if (protectToken != null)
env.put("GUARD_TOKEN", protectToken); env.put("GUARD_TOKEN", protectToken);

View file

@ -12,6 +12,7 @@ public class AutogenConfig {
public String guardLicenseEncryptKey; public String guardLicenseEncryptKey;
public String secureCheckHash; public String secureCheckHash;
public String secureCheckSalt; public String secureCheckSalt;
public String passwordEncryptKey;
public int env; public int env;
public boolean isWarningMissArchJava; public boolean isWarningMissArchJava;
// 0 - Dev (дебаг включен по умолчанию, все сообщения) // 0 - Dev (дебаг включен по умолчанию, все сообщения)

View file

@ -1,6 +1,8 @@
package pro.gravit.launcher; package pro.gravit.launcher;
import java.io.IOException; import java.io.IOException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.Collections; import java.util.Collections;
@ -31,7 +33,7 @@ public static AutogenConfig getAutogenConfig() {
public String secretKeyClient; public String secretKeyClient;
public String oemUnlockKey; public String oemUnlockKey;
@LauncherAPI @LauncherAPI
public final RSAPublicKey publicKey; public final ECPublicKey publicKey;
@LauncherAPI @LauncherAPI
public final Map<String, byte[]> runtime; public final Map<String, byte[]> runtime;
@ -46,12 +48,14 @@ public static AutogenConfig getAutogenConfig() {
public final String secureCheckHash; public final String secureCheckHash;
public final String secureCheckSalt; public final String secureCheckSalt;
public final String passwordEncryptKey;
@LauncherAPI @LauncherAPI
public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException { 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; secureCheckHash = config.secureCheckHash;
secureCheckSalt = config.secureCheckSalt; secureCheckSalt = config.secureCheckSalt;
passwordEncryptKey = config.passwordEncryptKey;
projectname = config.projectname; projectname = config.projectname;
clientPort = config.clientPort; clientPort = config.clientPort;
secretKeyClient = config.secretKeyClient; secretKeyClient = config.secretKeyClient;
@ -84,7 +88,7 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
} }
@LauncherAPI @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.address = address;
this.publicKey = Objects.requireNonNull(publicKey, "publicKey"); this.publicKey = Objects.requireNonNull(publicKey, "publicKey");
this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime)); this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime));
@ -99,10 +103,11 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
environment = LauncherEnvironment.STD; environment = LauncherEnvironment.STD;
secureCheckSalt = null; secureCheckSalt = null;
secureCheckHash = null; secureCheckHash = null;
passwordEncryptKey = null;
} }
@LauncherAPI @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.address = address;
this.publicKey = Objects.requireNonNull(publicKey, "publicKey"); this.publicKey = Objects.requireNonNull(publicKey, "publicKey");
this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime)); this.runtime = Collections.unmodifiableMap(new HashMap<>(runtime));
@ -117,6 +122,7 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
environment = LauncherEnvironment.STD; environment = LauncherEnvironment.STD;
secureCheckSalt = null; secureCheckSalt = null;
secureCheckHash = null; secureCheckHash = null;
passwordEncryptKey = null;
} }
@Override @Override

View file

@ -6,7 +6,7 @@
import pro.gravit.launcher.hwid.HWID; import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; 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.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.VerifyHelper; import pro.gravit.utils.helper.VerifyHelper;
@ -48,7 +48,7 @@ public enum ConnectTypes {
@LauncherAPI @LauncherAPI
public AuthRequest(String login, byte[] password, HWID hwid) { public AuthRequest(String login, byte[] password, HWID hwid) {
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty"); 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.hwid = hwid;
customText = ""; customText = "";
auth_id = ""; auth_id = "";
@ -59,7 +59,7 @@ public AuthRequest(String login, byte[] password, HWID hwid) {
@LauncherAPI @LauncherAPI
public AuthRequest(String login, byte[] password, HWID hwid, String auth_id) { 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.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.hwid = hwid;
this.auth_id = auth_id; this.auth_id = auth_id;
customText = ""; customText = "";
@ -70,7 +70,7 @@ public AuthRequest(String login, byte[] password, HWID hwid, String auth_id) {
@LauncherAPI @LauncherAPI
public AuthRequest(String login, byte[] password, HWID hwid, String customText, String auth_id) { 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.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.hwid = hwid;
this.auth_id = auth_id; this.auth_id = auth_id;
this.customText = customText; 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) { public AuthRequest(String login, byte[] encryptedPassword, String auth_id, ConnectTypes authType) {
this.login = login; this.login = login;
this.password = new AuthRSAPassword(encryptedPassword.clone()); this.password = new AuthECPassword(encryptedPassword.clone());
this.auth_id = auth_id; this.auth_id = auth_id;
this.authType = authType; this.authType = authType;
this.hwid = null; this.hwid = null;
@ -106,7 +106,7 @@ public String getType() {
public static void registerProviders() { public static void registerProviders() {
if(!registerProviders) { if(!registerProviders) {
providers.register("plain", AuthPlainPassword.class); providers.register("plain", AuthPlainPassword.class);
providers.register("rsa", AuthRSAPassword.class); providers.register("rsa", AuthECPassword.class);
registerProviders = true; registerProviders = true;
} }
} }

View file

@ -3,11 +3,11 @@
import pro.gravit.launcher.LauncherNetworkAPI; import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
public class AuthRSAPassword implements AuthRequest.AuthPasswordInterface { public class AuthECPassword implements AuthRequest.AuthPasswordInterface {
@LauncherNetworkAPI @LauncherNetworkAPI
public final byte[] password; public final byte[] password;
public AuthRSAPassword(byte[] password) { public AuthECPassword(byte[] password) {
this.password = password; this.password = password;
} }

View file

@ -6,6 +6,7 @@
compileOnly 'org.jline:jline:3.11.0' compileOnly 'org.jline:jline:3.11.0'
compileOnly 'org.jline:jline-reader:3.11.0' compileOnly 'org.jline:jline-reader:3.11.0'
compileOnly 'org.jline:jline-terminal: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' compile 'com.google.code.gson:gson:2.8.5'
testCompile 'org.junit.jupiter:junit-jupiter:5.4.1' testCompile 'org.junit.jupiter:junit-jupiter:5.4.1'
} }

View file

@ -4,19 +4,9 @@
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.InvalidKeyException; import java.security.*;
import java.security.Key; import java.security.interfaces.*;
import java.security.KeyFactory; import java.security.spec.ECGenParameterSpec;
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.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
@ -30,6 +20,8 @@
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEIESCipher;
import pro.gravit.launcher.LauncherAPI; import pro.gravit.launcher.LauncherAPI;
public final class SecurityHelper { 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 // Algorithm size constants
public static final int TOKEN_LENGTH = 16; public static final int TOKEN_LENGTH = 16;
@ -144,25 +139,19 @@ public static byte[] digest(DigestAlgorithm algo, URL url) throws IOException {
} }
} }
public static KeyPair genECKeyPair(SecureRandom random) {
public static KeyPair genRSAKeyPair() {
return genRSAKeyPair(newRandom());
}
public static KeyPair genRSAKeyPair(SecureRandom random) {
try { try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGO); KeyPairGenerator generator = KeyPairGenerator.getInstance(EC_ALGO);
generator.initialize(RSA_KEY_LENGTH_BITS, random); generator.initialize(new ECGenParameterSpec(EC_CURVE), random);
return generator.genKeyPair(); return generator.genKeyPair();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new InternalError(e); throw new InternalError(e);
} }
} }
public static boolean isValidSign(byte[] bytes, byte[] sign, RSAPublicKey publicKey) throws SignatureException { public static boolean isValidSign(byte[] bytes, byte[] sign, ECPublicKey publicKey) throws SignatureException {
Signature signature = newRSAVerifySignature(publicKey); Signature signature = newECVerifySignature(publicKey);
try { try {
signature.update(bytes); signature.update(bytes);
} catch (SignatureException e) { } 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 { public static boolean isValidSign(InputStream input, byte[] sign, ECPublicKey publicKey) throws IOException, SignatureException {
Signature signature = newRSAVerifySignature(publicKey); Signature signature = newECVerifySignature(publicKey);
updateSignature(input, signature); updateSignature(input, signature);
return signature.verify(sign); 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) { public static boolean isValidToken(CharSequence token) {
return token.length() == TOKEN_STRING_LENGTH && token.chars().allMatch(ch -> HEX.indexOf(ch) >= 0); return token.length() == TOKEN_STRING_LENGTH && token.chars().allMatch(ch -> HEX.indexOf(ch) >= 0);
} }
private static Cipher newCipher(String algo) { private static Cipher newCipher(String algo) {
// IDK Why, but collapsing catch blocks makes ProGuard generate invalid stackmap
try { try {
return Cipher.getInstance(algo); return Cipher.getInstance(algo);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) { } 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) { public static MessageDigest newDigest(DigestAlgorithm algo) {
VerifyHelper.verify(algo, a -> a != DigestAlgorithm.PLAIN, "PLAIN digest"); VerifyHelper.verify(algo, a -> a != DigestAlgorithm.PLAIN, "PLAIN digest");
@ -221,8 +208,8 @@ public static SecureRandom newRandom() {
return new SecureRandom(); return new SecureRandom();
} }
private static Cipher newRSACipher(int mode, RSAKey key) { private static Cipher newECCipher(int mode, ECKey key) {
Cipher cipher = newCipher(RSA_CIPHER_ALGO); Cipher cipher = newBCCipher(EC_CIPHER_ALGO);
try { try {
cipher.init(mode, (Key) key); cipher.init(mode, (Key) key);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
@ -231,35 +218,24 @@ private static Cipher newRSACipher(int mode, RSAKey key) {
return cipher; return cipher;
} }
@LauncherAPI private static KeyFactory newECKeyFactory() {
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() {
try { try {
return KeyFactory.getInstance(RSA_ALGO); return KeyFactory.getInstance(EC_ALGO);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new InternalError(e); throw new InternalError(e);
} }
} }
private static Signature newRSASignature() { private static Signature newECSignature() {
try { try {
return Signature.getInstance(RSA_SIGN_ALGO); return Signature.getInstance(EC_SIGN_ALGO);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new InternalError(e); throw new InternalError(e);
} }
} }
public static Signature newECSignSignature(ECPrivateKey key) {
public static Signature newRSASignSignature(RSAPrivateKey key) { Signature signature = newECSignature();
Signature signature = newRSASignature();
try { try {
signature.initSign(key); signature.initSign(key);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
@ -269,8 +245,8 @@ public static Signature newRSASignSignature(RSAPrivateKey key) {
} }
public static Signature newRSAVerifySignature(RSAPublicKey key) { public static Signature newECVerifySignature(ECPublicKey key) {
Signature signature = newRSASignature(); Signature signature = newECSignature();
try { try {
signature.initVerify(key); signature.initVerify(key);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
@ -394,9 +370,8 @@ public static String randomUsername(Random random) {
return VerifyHelper.verifyUsername(prefix + new String(chars) + suffix); return VerifyHelper.verifyUsername(prefix + new String(chars) + suffix);
} }
public static byte[] sign(byte[] bytes, ECPrivateKey privateKey) {
public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) { Signature signature = newECSignSignature(privateKey);
Signature signature = newRSASignSignature(privateKey);
try { try {
signature.update(bytes); signature.update(bytes);
return signature.sign(); 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) { public static String toHex(byte[] bytes) {
int offset = 0; int offset = 0;
@ -436,13 +394,12 @@ public static String toHex(byte[] bytes) {
return new String(hex); return new String(hex);
} }
public static ECPublicKey toPublicECKey(byte[] bytes) throws InvalidKeySpecException {
public static RSAPrivateKey toPrivateRSAKey(byte[] bytes) throws InvalidKeySpecException { return (ECPublicKey) newECKeyFactory().generatePublic(new X509EncodedKeySpec(bytes));
return (RSAPrivateKey) newRSAKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes));
} }
public static RSAPublicKey toPublicRSAKey(byte[] bytes) throws InvalidKeySpecException { public static ECPrivateKey toPrivateECKey(byte[] bytes) throws InvalidKeySpecException {
return (RSAPublicKey) newRSAKeyFactory().generatePublic(new X509EncodedKeySpec(bytes)); return (ECPrivateKey) newECKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(bytes));
} }
private static void updateSignature(InputStream input, Signature signature) throws IOException { 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)) if (!isValidSign(bytes, sign, publicKey))
throw new SignatureException("Invalid sign"); 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)) if (!isValidSign(input, sign, publicKey))
throw new SignatureException("Invalid stream sign"); 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) { public static String verifyToken(String token) {
return VerifyHelper.verify(token, SecurityHelper::isValidToken, String.format("Invalid token: '%s'", 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() { private SecurityHelper() {
} }
@ -521,6 +485,9 @@ public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, sKeySpec); cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
return cipher.doFinal(encrypted); 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) { public static byte[] HexToByte(String hexString) {
int len = hexString.length() / 2; int len = hexString.length() / 2;

View file

@ -52,7 +52,7 @@ public ServerWrapper(Type type, Path configPath) {
public boolean auth() { public boolean auth() {
try { try {
LauncherConfig cfg = Launcher.getConfig(); 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; permissions = request.request().permissions;
ProfilesRequestEvent result = new ProfilesRequest().request(); ProfilesRequestEvent result = new ProfilesRequest().request();
for (ClientProfile p : result.profiles) { for (ClientProfile p : result.profiles) {
@ -190,7 +190,7 @@ public void updateLauncherConfig() {
LauncherConfig cfg = null; LauncherConfig cfg = null;
try { 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.isNettyEnabled = true;
cfg.address = config.address; cfg.address = config.address;
} catch (InvalidKeySpecException | IOException e) { } catch (InvalidKeySpecException | IOException e) {

@ -1 +1 @@
Subproject commit 960e178bc658835dc8aff93ee51e10e52bc7afb5 Subproject commit a770ac083f9e940122f888fa6ffd9e0485d3292b