1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-12-23 00:08:51 -05:00

Experiment with PGP signed messages

This commit is contained in:
Reinhard Pointner 2018-06-10 16:16:51 +07:00
parent bb21d86ec4
commit 204e1e22c6
6 changed files with 66 additions and 30 deletions

View File

@ -2,10 +2,12 @@ package net.filebot;
import static java.nio.charset.StandardCharsets.*;
import static java.util.stream.Collectors.*;
import static net.filebot.util.FileUtilities.*;
import static net.filebot.util.JsonUtilities.*;
import static net.filebot.util.RegularExpressions.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.time.Instant;
@ -25,39 +27,38 @@ import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import net.filebot.util.ByteBufferOutputStream;
import net.filebot.util.SystemProperty;
import net.filebot.web.WebRequest;
public class License implements Serializable {
private byte[] bytes;
private String product;
private long id;
private long expires;
private Exception error;
public License(byte[] bytes) {
this.bytes = bytes;
try {
// verify and get clear signed content
Map<String, String> properties = getProperties();
Map<String, String> properties = getProperties(bytes);
this.product = properties.get("Product");
this.id = Long.parseLong(properties.get("Order"));
this.expires = LocalDate.parse(properties.get("Valid-Until"), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay(ZoneOffset.UTC).plusDays(1).minusSeconds(1).toInstant().toEpochMilli();
// verify license online
verifyLicense();
verifyLicense(bytes);
} catch (Exception e) {
error = e;
}
}
public Map<String, String> getProperties() throws Exception {
return NEWLINE.splitAsStream(verifyClearSignMessage()).map(s -> s.split(": ", 2)).collect(toMap(a -> a[0], a -> a[1]));
private Map<String, String> getProperties(byte[] bytes) throws Exception {
return NEWLINE.splitAsStream(verifyClearSignMessage(bytes)).map(s -> s.split(": ", 2)).collect(toMap(a -> a[0], a -> a[1]));
}
public String verifyClearSignMessage() throws Exception {
private String verifyClearSignMessage(byte[] bytes) throws Exception {
ArmoredInputStream armoredInput = new ArmoredInputStream(new ByteArrayInputStream(bytes));
// read content
@ -90,7 +91,7 @@ public class License implements Serializable {
return clearSignMessage;
}
private void verifyLicense() throws Exception {
private void verifyLicense(byte[] bytes) throws Exception {
Cache cache = CacheManager.getInstance().getCache("license", CacheType.Persistent);
Object json = cache.json(id, i -> new URL("https://license.filebot.net/verify/" + i)).fetch((url, modified) -> WebRequest.post(url, bytes, "application/octet-stream", null)).expire(Cache.ONE_MONTH).get();
@ -111,7 +112,26 @@ public class License implements Serializable {
@Override
public String toString() {
return String.format("License %s (Valid-Until: %s)", id, Instant.ofEpochMilli(expires).atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_LOCAL_DATE));
return String.format("%s License %s (Valid-Until: %s)", product, id, Instant.ofEpochMilli(expires).atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_LOCAL_DATE));
}
public static final SystemProperty<File> FILE = SystemProperty.of("net.filebot.license", File::new, ApplicationFolder.AppData.resolve("license.txt"));
public static final MemoizedResource<License> INSTANCE = Resource.lazy(() -> new License(readFile(FILE.get())));
public static License configure(File file) throws Exception {
byte[] bytes = readFile(file);
// check if license file is valid and not expired
License license = new License(bytes);
license.check();
// write to default license file path
writeFile(bytes, FILE.get());
// clear memoized instance and reload on next access
INSTANCE.clear();
return license;
}
}

View File

@ -1,13 +1,9 @@
package net.filebot;
import static net.filebot.Settings.*;
import static net.filebot.platform.windows.WinAppUtilities.*;
import static net.filebot.util.FileUtilities.*;
import java.io.File;
import net.filebot.util.SystemProperty;
public enum LicenseModel {
MicrosoftStore {
@ -32,13 +28,10 @@ public enum LicenseModel {
PGPSignedMessage {
public final SystemProperty<File> LICENSE_FILE = SystemProperty.of("net.filebot.license", File::new, ApplicationFolder.AppData.resolve("license.txt"));
public final MemoizedResource<License> LICENSE = Resource.lazy(() -> new License(readFile(LICENSE_FILE.get())));
@Override
public void check() throws LicenseError {
try {
LICENSE.get().check();
License.INSTANCE.get().check();
} catch (Exception e) {
throw new LicenseError(e.getMessage());
}
@ -47,13 +40,4 @@ public enum LicenseModel {
public abstract void check() throws LicenseError;
public static LicenseModel get() {
if (isUWP())
return MicrosoftStore;
if (isMacSandbox())
return MacAppStore;
return PGPSignedMessage;
}
}

View File

@ -162,11 +162,23 @@ public class Main {
SwingEventBus.getInstance().post(new FileTransferable(files));
}
// import license if started with license file
if (LicenseModel.PGPSignedMessage == LICENSE) {
args.getLicenseFile().ifPresent(f -> {
try {
License license = License.configure(f);
log.info(license + " has been activated.");
} catch (Throwable e) {
log.severe("License Error: " + e.getMessage());
}
});
}
// JavaFX is used for ProgressMonitor and GettingStartedDialog
try {
initJavaFX();
} catch (Throwable e) {
log.log(Level.SEVERE, "Failed to initialize JavaFX. Please install JavaFX.", e);
log.log(Level.SEVERE, "Failed to initialize JavaFX", e);
}
// check if application help should be shown

View File

@ -25,7 +25,7 @@ import net.filebot.util.PreferencesMap.StringAdapter;
public final class Settings {
public static final LicenseModel LICENSE = LicenseModel.get();
public static final LicenseModel LICENSE = getLicenseModel();
public static String getApplicationName() {
return getApplicationProperty("application.name");
@ -137,6 +137,15 @@ public final class Settings {
return Runtime.getRuntime().availableProcessors();
}
public static LicenseModel getLicenseModel() {
if (isUWP())
return LicenseModel.MicrosoftStore;
if (isMacSandbox())
return LicenseModel.MacAppStore;
return LicenseModel.PGPSignedMessage;
}
public static String getAppStoreName() {
if (isMacApp())
return "Mac App Store";

View File

@ -139,6 +139,9 @@ public class ArgumentBean {
@Option(name = "-help", usage = "Print this help message")
public boolean help = false;
@Option(name = "--license", usage = "Import license file", metaVar = "file")
public String license = null;
@Option(name = "--def", usage = "Define script variables", handler = BindingsHandler.class)
public Map<String, String> defines = new LinkedHashMap<String, String>();
@ -348,6 +351,10 @@ public class ArgumentBean {
}).orElseThrow(error("Illegal mode", mode));
}
public Optional<File> getLicenseFile() {
return optional(license).map(File::new);
}
private final String[] args;
public ArgumentBean(String... args) throws CmdLineException {

View File

@ -225,6 +225,10 @@ public final class FileUtilities {
return destination;
}
public static File writeFile(byte[] data, File destination) throws IOException {
return Files.write(destination.toPath(), data, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE).toFile();
}
public static Reader createTextReader(InputStream in, boolean guess, Charset declaredEncoding) throws IOException {
byte head[] = new byte[BOM.SIZE];
in.mark(head.length);