[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.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;

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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);
}

View file

@ -134,6 +134,7 @@ public Path process(Path inputJar) throws IOException {
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));

View file

@ -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));
}
}

View file

@ -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();
}

View file

@ -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");
}

View file

@ -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)

View file

@ -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'
}

View file

@ -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;
},

View file

@ -1,12 +1,11 @@
package pro.gravit.launcher;
import java.security.*;
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 +21,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 {
@ -36,6 +34,7 @@ public static void main(String... args) throws Throwable {
//if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set");
LogHelper.printVersion("Launcher");
LogHelper.printLicense("Launcher");
Security.addProvider(new BouncyCastleProvider());
LauncherEngine.modulesManager = new ClientModuleManager();
LauncherConfig.getAutogenConfig().initModules();

View file

@ -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);
}
}

View file

@ -76,7 +76,7 @@ public void addCustomEnv(ClientLauncherContext context) {
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_PUBLICKEY", config.publicKey.getModulus().toString(16));
env.put("GUARD_PROJECTNAME", config.projectname);
if (protectToken != null)
env.put("GUARD_TOKEN", protectToken);

View file

@ -63,7 +63,7 @@ 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_PUBLICKEY", config.publicKey.getModulus().toString(16));
env.put("GUARD_PROJECTNAME", config.projectname);
if (protectToken != null)
env.put("GUARD_TOKEN", protectToken);

View file

@ -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 (дебаг включен по умолчанию, все сообщения)

View file

@ -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

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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'
}

View file

@ -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;

View file

@ -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) {

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