diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index a891822f..a1d7de7b 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -13,6 +13,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPrivateKey; @@ -274,14 +275,16 @@ public static class LaunchServerDirectories public Path updatesDir; public Path profilesDir; public Path dir; + public Path trustStore; public void collect() { if(updatesDir == null) updatesDir = dir.resolve("updates"); if(profilesDir == null) profilesDir = dir.resolve("profiles"); + if(trustStore == null) trustStore = dir.resolve("truststore"); } } - public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey 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, CertificateManager certificateManager) throws IOException { this.dir = directories.dir; this.env = env; this.config = config; @@ -293,6 +296,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La this.privateKey = privateKey; this.commandHandler = commandHandler; this.runtime = runtimeConfig; + this.certificateManager = certificateManager; taskPool = new Timer("Timered task worker thread", true); launcherLibraries = dir.resolve("launcher-libraries"); launcherLibrariesCompile = dir.resolve("launcher-libraries-compile"); @@ -331,7 +335,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La reconfigurableManager = new ReconfigurableManager(); authHookManager = new AuthHookManager(); configManager = new ConfigManager(); - certificateManager = new CertificateManager(); //Generate or set new Certificate API certificateManager.orgName = config.projectName; if(config.certificate != null && config.certificate.enabled) diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerBuilder.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerBuilder.java index 030e1633..3b21bea1 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerBuilder.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerBuilder.java @@ -8,6 +8,7 @@ import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; +import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.utils.command.CommandHandler; @@ -20,6 +21,7 @@ public class LaunchServerBuilder { private LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories(); private ECPublicKey publicKey; private ECPrivateKey privateKey; + private CertificateManager certificateManager; private LaunchServer.LaunchServerConfigManager launchServerConfigManager; public LaunchServerBuilder setConfig(LaunchServerConfig config) { @@ -101,6 +103,11 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) { } }; } - return new LaunchServer(directories, env, config, runtimeConfig, launchServerConfigManager, modulesManager, publicKey, privateKey, commandHandler); + return new LaunchServer(directories, env, config, runtimeConfig, launchServerConfigManager, modulesManager, publicKey, privateKey, commandHandler, certificateManager); + } + + public LaunchServerBuilder setCertificateManager(CertificateManager certificateManager) { + this.certificateManager = certificateManager; + return this; } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java index 040466bd..a35e2116 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java @@ -9,6 +9,7 @@ import java.security.KeyPair; import java.security.SecureRandom; import java.security.Security; +import java.security.cert.CertificateException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; @@ -27,6 +28,7 @@ import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.dao.provider.DaoProvider; +import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.LaunchServerGsonManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.socket.WebSocketService; @@ -37,12 +39,12 @@ import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.SecurityHelper; +import pro.gravit.utils.verify.LauncherTrustManager; 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")); @@ -52,18 +54,24 @@ public static void main(String[] args) throws Exception { LogHelper.error("StarterAgent is not started!"); LogHelper.error("You should add to JVM options this option: `-javaagent:LaunchServer.jar`"); } - Path dir = IOHelper.WORKING_DIR; Path configFile, runtimeConfigFile; Path publicKeyFile =dir.resolve("public.key"); Path privateKeyFile = dir.resolve("private.key"); ECPublicKey publicKey; ECPrivateKey privateKey; + Security.addProvider(new BouncyCastleProvider()); + CertificateManager certificateManager = new CertificateManager(); + try { + certificateManager.readTrustStore(dir.resolve("truststore")); + } catch (CertificateException e) { + throw new IOException(e); + } LaunchServerRuntimeConfig runtimeConfig; LaunchServerConfig config; LaunchServer.LaunchServerEnv env = LaunchServer.LaunchServerEnv.PRODUCTION; - LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(dir.resolve("modules"), dir.resolve("config")); + LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(dir.resolve("modules"), dir.resolve("config"), certificateManager.trustManager); modulesManager.autoload(); modulesManager.initModules(null); registerAll(); @@ -173,6 +181,7 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOExcept .setConfig(config) .setModulesManager(modulesManager) .setLaunchServerConfigManager(launchServerConfigManager) + .setCertificateManager(certificateManager) .build(); server.run(); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/CertificateManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/CertificateManager.java index 88620405..c6fafca8 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/CertificateManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/CertificateManager.java @@ -1,25 +1,31 @@ package pro.gravit.launchserver.manangers; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import java.io.*; import java.math.BigInteger; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x500.X500Name; @@ -43,6 +49,7 @@ import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.SecurityHelper; +import pro.gravit.utils.verify.LauncherTrustManager; public class CertificateManager { public X509CertificateHolder ca; @@ -51,9 +58,7 @@ public class CertificateManager { public X509CertificateHolder server; public AsymmetricKeyParameter serverKey; - - //public X509CertificateHolder server; - //public AsymmetricKeyParameter serverKey; + public LauncherTrustManager trustManager; public int validDays = 60; public int minusHours = 6; @@ -172,4 +177,34 @@ public X509CertificateHolder readCertificate(Reader reader) throws IOException { } return ret; } + + public void readTrustStore(Path dir) throws IOException, CertificateException { + if(!IOHelper.isDir(dir)) + { + Files.createDirectories(dir); + try(OutputStream outputStream = IOHelper.newOutput(dir.resolve("GravitCentralRootCA.crt")); + InputStream inputStream = IOHelper.newInput(IOHelper.getResourceURL("pro/gravit/launchserver/defaults/GravitCentralRootCA.crt"))) + { + IOHelper.transfer(inputStream, outputStream); + } + } + List certificates = new ArrayList<>(); + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + IOHelper.walk(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if(file.toFile().getName().endsWith(".crt")) + { + try(InputStream inputStream = IOHelper.newInput(file)) + { + certificates.add((X509Certificate) certFactory.generateCertificate(inputStream)); + } catch (CertificateException e) { + throw new IOException(e); + } + } + return super.visitFile(file, attrs); + } + }, false); + trustManager = new LauncherTrustManager(certificates.toArray(new X509Certificate[0])); + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/modules/impl/LaunchServerModulesManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/modules/impl/LaunchServerModulesManager.java index 0cc68190..24558e99 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/modules/impl/LaunchServerModulesManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/modules/impl/LaunchServerModulesManager.java @@ -7,12 +7,14 @@ import pro.gravit.launcher.modules.LauncherModuleInfo; import pro.gravit.launcher.modules.impl.SimpleModuleManager; import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.utils.helper.LogHelper; +import pro.gravit.utils.verify.LauncherTrustManager; public class LaunchServerModulesManager extends SimpleModuleManager { public LaunchServerCoreModule coreModule; - public LaunchServerModulesManager(Path modulesDir, Path configDir) { - super(modulesDir, configDir); + public LaunchServerModulesManager(Path modulesDir, Path configDir, LauncherTrustManager trustManager) { + super(modulesDir, configDir, trustManager); coreModule = new LaunchServerCoreModule(); modules.add(coreModule); } diff --git a/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/GravitCentralRootCA.crt b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/GravitCentralRootCA.crt new file mode 100644 index 00000000..c683d07b --- /dev/null +++ b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/GravitCentralRootCA.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java b/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java index d19ab520..31f9d75d 100644 --- a/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java +++ b/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java @@ -16,6 +16,7 @@ import pro.gravit.launcher.Launcher; import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; +import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.LaunchServerGsonManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.utils.command.StdCommandHandler; @@ -32,7 +33,7 @@ public class StartLaunchServerTest { @BeforeAll public static void prepare() throws Exception { - LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir); + LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir, null); LaunchServerConfig config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST); Launcher.gsonManager = new LaunchServerGsonManager(modulesManager); Launcher.gsonManager.initGson(); @@ -47,6 +48,7 @@ public static void prepare() throws Exception .setRuntimeConfig(runtimeConfig) .setPublicKey(publicKey) .setPrivateKey(privateKey) + .setCertificateManager(new CertificateManager()) .setLaunchServerConfigManager(new LaunchServer.LaunchServerConfigManager() { @Override public LaunchServerConfig readConfig() throws IOException { diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java index e8285f4d..08c9d1c3 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java @@ -7,6 +7,12 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -22,7 +28,9 @@ import pro.gravit.utils.PublicURLClassLoader; import pro.gravit.utils.Version; import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.LogHelper; +import pro.gravit.utils.verify.LauncherTrustManager; public class SimpleModuleManager implements LauncherModulesManager { protected final List modules = new ArrayList<>(); @@ -30,6 +38,7 @@ public class SimpleModuleManager implements LauncherModulesManager { protected final SimpleModuleContext context; protected final ModulesConfigManager modulesConfigManager; protected final Path modulesDir; + protected final LauncherTrustManager trustManager; protected LauncherInitContext initContext; protected PublicURLClassLoader classLoader = new PublicURLClassLoader(new URL[]{}); @@ -113,6 +122,13 @@ public SimpleModuleManager(Path modulesDir, Path configDir) { modulesConfigManager = new SimpleModulesConfigManager(configDir); context = new SimpleModuleContext(this, modulesConfigManager); this.modulesDir = modulesDir; + this.trustManager = null; + } + public SimpleModuleManager(Path modulesDir, Path configDir, LauncherTrustManager trustManager) { + modulesConfigManager = new SimpleModulesConfigManager(configDir); + context = new SimpleModuleContext(this, modulesConfigManager); + this.modulesDir = modulesDir; + this.trustManager = trustManager; } @Override @@ -142,7 +158,10 @@ public LauncherModule loadModule(Path file) throws IOException { return null; } classLoader.addURL(file.toUri().toURL()); - LauncherModule module = (LauncherModule) Class.forName(moduleClass, true, classLoader).newInstance(); + @SuppressWarnings("unchecked cast") + Class clazz = (Class) Class.forName(moduleClass, false, classLoader); + checkModuleClass(clazz); + LauncherModule module = clazz.newInstance(); loadModule(module); return module; } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { @@ -152,6 +171,23 @@ public LauncherModule loadModule(Path file) throws IOException { } } + protected void checkModuleClass(Class clazz) throws SecurityException + { + if(trustManager == null) return; + X509Certificate[] certificates = JVMHelper.getCertificates(clazz); + if(certificates == null) + { + LogHelper.warning("Module class %s not signed", clazz.getName()); + } + try { + trustManager.checkCertificate(certificates, (c,s) -> { + + }); + } catch (CertificateException | NoSuchProviderException | NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { + throw new SecurityException(e); + } + } + @Override public LauncherModule getModule(String name) { for(LauncherModule module : modules) diff --git a/LauncherCore/src/main/java/pro/gravit/utils/helper/JVMHelper.java b/LauncherCore/src/main/java/pro/gravit/utils/helper/JVMHelper.java index e65b913d..934e8518 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/helper/JVMHelper.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/helper/JVMHelper.java @@ -114,7 +114,7 @@ public static URL[] getClassPathURL() { public static X509Certificate[] getCertificates(Class clazz) { Object[] signers = clazz.getSigners(); - if(signers == null) return new X509Certificate[] {}; + if(signers == null) return null; return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new); } diff --git a/LauncherCore/src/main/java/pro/gravit/utils/verify/LauncherTrustManager.java b/LauncherCore/src/main/java/pro/gravit/utils/verify/LauncherTrustManager.java new file mode 100644 index 00000000..2e1debbb --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/verify/LauncherTrustManager.java @@ -0,0 +1,88 @@ +package pro.gravit.utils.verify; + +import pro.gravit.utils.helper.LogHelper; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class LauncherTrustManager { + private final X509Certificate[] trustSigners; + private final List trustCache = new ArrayList<>(); + + public LauncherTrustManager(X509Certificate[] trustSigners) { + this.trustSigners = trustSigners; + } + public LauncherTrustManager(byte[][] encodedCertificate) throws CertificateException { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + trustSigners = Arrays.stream(encodedCertificate).map((cert) -> { + try(InputStream input = new ByteArrayInputStream(cert)) + { + return (X509Certificate) certFactory.generateCertificate(input); + } catch (IOException | CertificateException e) { + LogHelper.error(e); + return null; + } + }).toArray(X509Certificate[]::new); + } + public interface CertificateChecker + { + void check(X509Certificate cert, X509Certificate signer) throws CertificateException, SecurityException; + } + + public void checkCertificate(X509Certificate[] certs, CertificateChecker checker) throws CertificateException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { + if(certs == null) throw new SecurityException("Object not signed"); + for(int i=0;i< certs.length;++i) + { + X509Certificate cert = certs[i]; + if(trustCache.contains(cert)) + { + //Добавляем в кеш все проверенные сертификаты + for(int j=0;j < i;++j) + trustCache.add(certs[j]); + return; + } + X509Certificate signer = (i+1 < certs.length) ? certs[i+1] : null; + cert.checkValidity(); + if(signer != null) + { + cert.verify(signer.getPublicKey()); + } + else + { + if(!isTrusted(cert)) + { + throw new CertificateException(String.format("Certificate %s is not signed by a trusted signer", cert.getSubjectDN().getName())); + } + } + checker.check(cert, signer); + } + Collections.addAll(trustCache, certs); + } + public boolean isTrusted(X509Certificate certificate) throws CertificateEncodingException { + //Java API не дает возможности вызвать getFingerprint + //Oracle использует хак с кастом к sun.security.x509.X509CertImpl для проверки равенства сертификатов + //Мы пойдем более медленным путем + for(X509Certificate cert : trustSigners) + { + if(cert.getSerialNumber().equals(certificate.getSerialNumber()) //Проверка serialNumber (быстро) + && Arrays.equals(cert.getEncoded(), certificate.getEncoded())) //Полная проверка (медленно) + { + return true; + } + } + return false; + } +}