diff --git a/pom.xml b/pom.xml
index e96f916..b38032a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,7 +57,7 @@
eu.m724
jarupdater
- 0.1.7
+ 0.1.10
diff --git a/src/main/java/eu/m724/giants/GiantsPlugin.java b/src/main/java/eu/m724/giants/GiantsPlugin.java
index 8b20c64..aed8ad7 100644
--- a/src/main/java/eu/m724/giants/GiantsPlugin.java
+++ b/src/main/java/eu/m724/giants/GiantsPlugin.java
@@ -1,8 +1,8 @@
package eu.m724.giants;
-import eu.m724.giants.updater.JarVerifier;
import eu.m724.giants.updater.PluginUpdater;
import eu.m724.giants.updater.UpdateCommand;
+import eu.m724.jarupdater.verify.VerificationException;
import org.bstats.bukkit.Metrics;
import org.bukkit.Location;
import org.bukkit.command.CommandExecutor;
@@ -28,33 +28,41 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor {
configuration.load();
- UpdateCommand updateCommand = null;
- if (configuration.updater != null) {
- PluginUpdater updater = PluginUpdater.build(this, getFile(), configuration.updater);
- updater.initNotifier();
- updateCommand = new UpdateCommand(updater);
- }
-
- getCommand("giants").setExecutor(new GiantsCommand(this, configuration, updateCommand));
-
giantProcessor.start();
+ // bStats
new Metrics(this, 14131);
- try (InputStream keyInputStream = getResource("verifies_downloaded_jars.pem")) {
- JarVerifier.verifyWithRsaKey(
- getFile().getPath().replace(".paper-remapped/", ""), // paper remapping removes data from manifest
- keyInputStream
- );
- } catch (IOException e) {
- getLogger().warning(e.getMessage());
- getLogger().warning("Failed checking JAR signature. This is not important right now, but it usually indicates future problems. If this persists, re-download the JAR from SpigotMC.");
- } catch (JarVerifier.VerificationException e) {
- getLogger().warning(e.getMessage());
- getLogger().warning("Plugin JAR is of invalid signature. If this persists, re-download the JAR from SpigotMC.");
- getLogger().warning("Did you update from 2.0.7? If yes, you must re-download 2.0.9+ from SpigotMC, then delete plugins/.paper-remapped");
+ // updater
+ PluginUpdater updater = null;
+
+ if (configuration.updater != null) {
+ try (InputStream keyInputStream = getResource("verifies_downloaded_jars.pem")) {
+ updater = PluginUpdater.build(this, getFile(), configuration.updater, keyInputStream);
+ } catch (IOException e) {
+ e.printStackTrace();
+ getLogger().severe("Failed to load updater");
+ }
+
+ if (updater != null) {
+ try {
+ updater.verifyJar(
+ getFile().getPath().replace(".paper-remapped/", "") // paper remapping removes data from manifest
+ );
+ } catch (VerificationException e) {
+ getLogger().warning(e.getMessage());
+ getLogger().warning("Plugin JAR is of invalid signature. If this persists, re-download the JAR from SpigotMC.");
+ getLogger().warning("Did you update from 2.0.7? If yes, you must re-download 2.0.9+ from SpigotMC, then delete plugins/.paper-remapped");
+
+ }
+ updater.initNotifier();
+ }
}
+ UpdateCommand updateCommand = new UpdateCommand(updater);
+
+ getCommand("giants").setExecutor(new GiantsCommand(this, configuration, updateCommand));
+
/* bStats is optional. not anymore
try {
Class> clazz = Class.forName("eu.m724.giants.bukkit.Metrics");
diff --git a/src/main/java/eu/m724/giants/updater/JarVerifier.java b/src/main/java/eu/m724/giants/updater/JarVerifier.java
deleted file mode 100644
index 21086f3..0000000
--- a/src/main/java/eu/m724/giants/updater/JarVerifier.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package eu.m724.giants.updater;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.CodeSigner;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-
-public class JarVerifier {
- /**
- * Loads an RSA public key from a PEM file
- *
- * @param keyInputStream inputStream of the public key file
- * @return {@link RSAPublicKey} instance of the public key
- * @throws IOException if reading the key input stream failed
- */
- private static RSAPublicKey loadPublicKey(InputStream keyInputStream) throws IOException {
- // Read the key file
- String keyContent = new String(keyInputStream.readAllBytes());
-
- // Remove PEM headers and newlines
- keyContent = keyContent.replace("-----BEGIN PUBLIC KEY-----", "")
- .replace("-----END PUBLIC KEY-----", "")
- .replaceAll("\\s+", "");
-
- // Decode the key
- byte[] keyBytes = Base64.getDecoder().decode(keyContent);
-
- // Create public key specification
- X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
-
- // Generate public key
- try {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- return (RSAPublicKey) kf.generatePublic(spec);
- } catch (GeneralSecurityException e) {
- // because this shouldn't happen
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Verifies if a JAR file's signature matches an RSA public key
- *
- * @param jarPath the path of the JAR file
- * @param keyInputStream inputStream of the public key file
- * @throws VerificationException if verification failed
- */
- public static void verifyWithRsaKey(String jarPath, InputStream keyInputStream) throws VerificationException {
- try {
- // Load the RSA public key
- RSAPublicKey publicKey = loadPublicKey(keyInputStream);
-
- // Open the JAR file
- try (JarFile jarFile = new JarFile(jarPath, true)) {
- byte[] buffer = new byte[8192];
- Enumeration entries = jarFile.entries();
-
- // Get manifest to check signature files
- Manifest manifest = jarFile.getManifest();
- if (manifest == null) {
- throw new VerificationException("JAR has no manifest");
- }
-
- while (entries.hasMoreElements()) {
- JarEntry entry = entries.nextElement();
-
- if (entry.isDirectory() || entry.getName().startsWith("META-INF/")) {
- continue;
- }
-
- int bytesRead = 0;
- // Read entry to trigger signature verification
- try (InputStream is = jarFile.getInputStream(entry)) {
- while ((bytesRead += is.read(buffer)) != -1) {
- if (bytesRead > 1024 * 1024 * 100) { // unusual for a file to have >100 MiB
- throw new IOException("File too large: " + entry.getName());
- }
- }
- }
-
- // Get signers for this entry
- CodeSigner[] signers = entry.getCodeSigners();
- if (signers == null || signers.length == 0) {
- throw new VerificationException("Unsigned entry: " + entry.getName());
- }
-
- // Check if any signer's public key matches our RSA key
- boolean keyMatch = false;
- List signerPublicKeys = new ArrayList<>();
-
- for (CodeSigner signer : signers) {
- for (Certificate cert : signer.getSignerCertPath().getCertificates()) {
- PublicKey certPublicKey = cert.getPublicKey();
- if (certPublicKey instanceof RSAPublicKey) {
- RSAPublicKey rsaKey = (RSAPublicKey) certPublicKey;
- signerPublicKeys.add(Base64.getEncoder().encodeToString(rsaKey.getEncoded()));
- if (rsaKey.getModulus().equals(publicKey.getModulus()) &&
- rsaKey.getPublicExponent().equals(publicKey.getPublicExponent())) {
- keyMatch = true;
- break;
- }
- }
- }
- if (keyMatch) break;
- }
-
- if (!keyMatch) {
- throw new VerificationException("Entry " + entry.getName() + " signed with " + String.join(", ", signerPublicKeys) + ", none of which match " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
- }
- }
- }
-
- } catch (IOException e) {
- throw new VerificationException("Verification error: " + e.getMessage(), e);
- }
- }
-
- public static class VerificationException extends Exception {
- public VerificationException(String message) {
- super(message);
- }
-
- public VerificationException(String message, Exception exception) {
- super(message, exception);
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/giants/updater/PluginUpdater.java b/src/main/java/eu/m724/giants/updater/PluginUpdater.java
index 9af7f19..1891503 100644
--- a/src/main/java/eu/m724/giants/updater/PluginUpdater.java
+++ b/src/main/java/eu/m724/giants/updater/PluginUpdater.java
@@ -7,55 +7,41 @@ import eu.m724.jarupdater.live.GiteaMetadataDAO;
import eu.m724.jarupdater.live.MetadataDAO;
import eu.m724.jarupdater.live.MetadataFacade;
import eu.m724.jarupdater.updater.Updater;
+import eu.m724.jarupdater.verify.SignatureVerifier;
+import eu.m724.jarupdater.verify.VerificationException;
+import eu.m724.jarupdater.verify.Verifier;
import org.bukkit.plugin.Plugin;
import java.io.File;
-import java.nio.file.NoSuchFileException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
+import java.io.IOException;
+import java.io.InputStream;
public class PluginUpdater extends Updater {
private final Plugin plugin;
boolean updatePending = false;
- private PluginUpdater(Environment environment, MetadataFacade metadataProvider, Downloader downloader, Plugin plugin) {
- super(environment, metadataProvider, downloader);
+ private PluginUpdater(Environment environment, MetadataFacade metadataProvider, Downloader downloader, Verifier verifier, Plugin plugin) {
+ super(environment, metadataProvider, downloader, verifier);
this.plugin = plugin;
}
- public static PluginUpdater build(Plugin plugin, File file, String channel) {
+ public static PluginUpdater build(Plugin plugin, File file, String channel, InputStream keyInputStream) throws IOException {
Environment environment = new PluginEnvironment(plugin, channel, file.toPath());
MetadataDAO metadataDAO = new GiteaMetadataDAO("https://git.m724.eu/Minecon724/giants-metadata", "master");
MetadataFacade metadataFacade = new MetadataFacade(environment, metadataDAO);
Downloader downloader = new SimpleDownloader("giants");
+ SignatureVerifier verifier = new SignatureVerifier();
+ verifier.loadPublicKey(keyInputStream);
- return new PluginUpdater(environment, metadataFacade, downloader, plugin);
+ return new PluginUpdater(environment, metadataFacade, downloader, verifier, plugin);
+ }
+
+ public void verifyJar(String jarPath) throws VerificationException {
+ verifier.verify(jarPath);
}
public void initNotifier() {
UpdateNotifier updateNotifier = new UpdateNotifier(plugin, this, (version) -> {});
updateNotifier.register();
}
-
- @Override
- public CompletableFuture installLatestVersion() throws NoSuchFileException {
- return installLatestVersion(true);
- }
-
- public CompletableFuture installLatestVersion(boolean verify) throws NoSuchFileException {
- if (this.downloaded == null) {
- throw new NoSuchFileException("Download it first");
- } else {
- return this.downloaded.thenCompose((file) -> {
- if (verify) {
- try {
- JarVerifier.verifyWithRsaKey(file.getPath(), plugin.getResource("verifies_downloaded_jars.pem"));
- } catch (JarVerifier.VerificationException e) {
- throw new CompletionException(e);
- }
- }
- return this.downloader.install(file, this.environment.getRunningJarFilePath().toFile());
- });
- }
- }
}
diff --git a/src/main/java/eu/m724/giants/updater/UpdateCommand.java b/src/main/java/eu/m724/giants/updater/UpdateCommand.java
index a6fcd2d..f0ff445 100644
--- a/src/main/java/eu/m724/giants/updater/UpdateCommand.java
+++ b/src/main/java/eu/m724/giants/updater/UpdateCommand.java
@@ -38,6 +38,11 @@ public class UpdateCommand {
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (updater == null) {
+ sender.sendMessage("Updater is disabled");
+ return true;
+ }
+
sender.sendMessage("Please wait...");
sender.sendMessage("Channel: " + updater.getEnvironment().getChannel());