From 0b344e20b638f3e185afb127770cbc8be4a360d2 Mon Sep 17 00:00:00 2001 From: hypherionmc Date: Sat, 6 Apr 2024 22:31:14 +0200 Subject: [PATCH] [Refactor] Use GPG instead of using legacy java keystores --- build.gradle | 4 + gradle.properties | 5 +- readme.md | 91 ++++---- .../plugin/KeyMasterGradleExtension.java | 35 +++ .../plugin/KeyMasterGradlePlugin.java | 9 +- .../keymaster/tasks/SignJarTask.java | 205 +++++++++++------- .../keymaster/utils/PluginUtils.java | 135 ++++++++++++ testproject/build.gradle | 38 ++-- testproject/dummystore.jks | Bin 2238 -> 0 bytes 9 files changed, 376 insertions(+), 146 deletions(-) create mode 100644 src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradleExtension.java create mode 100644 src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java delete mode 100644 testproject/dummystore.jks diff --git a/build.gradle b/build.gradle index 1aabf3a..e99f6d0 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,10 @@ dependencies { implementation gradleApi() shadeMe "org.apache.commons:commons-lang3:${commons_lang}" + shadeMe "org.bouncycastle:bcprov-jdk18on:${bouncy}" + shadeMe "org.bouncycastle:bcpg-jdk18on:${bouncy}" + shadeMe "commons-io:commons-io:${commons_io}" + shadeMe "commons-codec:commons-codec:${commons_codec}" compileOnly "org.projectlombok:lombok:${lombok}" annotationProcessor "org.projectlombok:lombok:${lombok}" diff --git a/gradle.properties b/gradle.properties index abbaea7..c9c9482 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,7 @@ version_patch=0 # Dependencies lombok=1.18.30 -commons_lang=3.14.0 \ No newline at end of file +commons_lang=3.14.0 +commons_io=2.16.0 +commons_codec=1.16.1 +bouncy=1.77 \ No newline at end of file diff --git a/readme.md b/readme.md index 2e1f3ec..6125084 100644 --- a/readme.md +++ b/readme.md @@ -6,12 +6,19 @@ A Simple Gradle plugin to help you sign your jars. ### Getting Started -To get started, you will need keystore. If you already have this, you can skip this part. +To get started, you will a GPG key. If you already have this, you can skip it. -In a terminal, or in command line, run the following command: +In a terminal, or in command line, run the following commands: ```bash -keytool -genkey -alias YOUR_ALIAS_HERE -keyalg RSA -keysize 2048 -keystore keystore.jks +# generate the keys +gpg --gen-key + +#export the private key with the specified id to a file +gpg --output {private key file name and path} --armor --export-secret-keys {key-id} + +#export the public key with the specified id to a file +gpg --output {public key file name and path} --armor --export {key-id} ``` Answer the required questions, and your file will be generated once completed. @@ -53,39 +60,35 @@ Finally, add the following to `build.gradle` file: ```groovy import dev.firstdark.keymaster.tasks.SignJarTask +// This is optional. These values can be configured on the task +keymaster { + // GPG Password + gpgPassword = "123456" + // GPG Key file, or String. + gpgKey = System.getenv("GPG_KEY") + // Generate a .sig file for signed jars, to be used for verification + generateSignature = true +} + // Register a custom task to sign your jar tasks.register('signJar', SignJarTask) { // Depend on the task used to build your project dependsOn jar - + // The input artifact. This can be a Task, File or File Name artifactInput = jar - + // Optional. Set the output name of the signed jar. This defaults to the artifactInput file name, and will overwrite it outputFileName = "testsign" - // The password of your key - keyPass = "123456" - - // Your key alias - keyStoreAlias = "testalias" - - // Your keystore password - keyStorePass = "123456" - - // Your keystore location - keyStore = "/home/hypherionsa/dummystore.jks" -} + // GPG Private key file or string. Not required when the extension is used + gpgKey = System.getenv("GPG_KEY") -// Example of signing another jar -tasks.register('signDummyJar', SignJarTask) { - dependsOn createDummyJar - artifactInput = createDummyJar + // GPG Private Key password. Not required when extension is used + gpgPassword = "123456" - keyPass = "123456" - keyStoreAlias = "testalias" - keyStorePass = "123456" - keyStore = "/home/hypherionsa/dummystore.jks" + // Should the task generate a .sig file. Defaults to true, and not required when extension is used + generateSignature = false } ``` @@ -126,8 +129,18 @@ Finally, add the following to `build.gradle.kts` file: import dev.firstdark.keymaster.tasks.SignJarTask import org.gradle.kotlin.dsl.register +// This is optional. These values can be configured on the task +extensions.configure("keymaster") { + // GPG Password + gpgPassword = "123456" + // GPG Key file, or String. + gpgKey = System.getenv("GPG_KEY") + // Generate a .sig file for signed jars, to be used for verification + generateSignature = true +} + // Register a custom task to sign your jar -val signJar by tasks.register("signJar") { +tasks.register("signJar", SignJarTask::class) { // Depend on the task used to build your project dependsOn(tasks.jar) @@ -137,28 +150,14 @@ val signJar by tasks.register("signJar") { // Optional. Set the output name of the signed jar. This defaults to the artifactInput file name, and will overwrite it outputFileName = "testsign" - // The password of your key - keyPass = "123456" + // GPG Private key file or string. Not required when the extension is used + gpgKey = System.getenv("GPG_KEY") - // Your key alias - keyStoreAlias = "testalias" + // GPG Private Key password. Not required when extension is used + gpgPassword = "123456" - // Your keystore password - keyStorePass = "123456" - - // Your keystore location - keyStore = "/home/hypherionsa/dummystore.jks" -} - -// Example of signing another jar -val signDummyJar by tasks.register("signDummyJar") { - dependsOn(tasks.createDummyJar) - artifactInput = tasks.createDummyJar - - keyPass = "123456" - keyStoreAlias = "testalias" - keyStorePass = "123456" - keyStore = "/home/hypherionsa/dummystore.jks" + // Should the task generate a .sig file. Defaults to true, and not required when extension is used + generateSignature = false } ``` diff --git a/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradleExtension.java b/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradleExtension.java new file mode 100644 index 0000000..02c118f --- /dev/null +++ b/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradleExtension.java @@ -0,0 +1,35 @@ +/* + * This file is part of KeyMaster, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 HypherionSA and Contributors + * + */ +package dev.firstdark.keymaster.plugin; + +import lombok.Getter; +import lombok.Setter; +import org.gradle.api.Project; +import org.gradle.api.provider.Property; + +/** + * @author HypherionSA + * Plugin Extension for sharing common configs between multiple tasks + */ +@Getter +@Setter +public class KeyMasterGradleExtension { + + // Default properties. These are overridden by the values specified on the task + private final Property gpgKey; + private final Property gpgPassword; + private final Property generateSignature; + private final Property outputDirectory; + + public KeyMasterGradleExtension(Project project) { + this.gpgKey = project.getObjects().property(String.class); + this.gpgPassword = project.getObjects().property(String.class); + this.generateSignature = project.getObjects().property(Boolean.class).convention(true); + this.outputDirectory = project.getObjects().property(String.class).convention(project.getBuildDir() + "/libs"); + } + +} diff --git a/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradlePlugin.java b/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradlePlugin.java index 7d2637f..d299ddd 100644 --- a/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradlePlugin.java +++ b/src/main/java/dev/firstdark/keymaster/plugin/KeyMasterGradlePlugin.java @@ -1,3 +1,9 @@ +/* + * This file is part of KeyMaster, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 HypherionSA and Contributors + * + */ package dev.firstdark.keymaster.plugin; import org.gradle.api.Plugin; @@ -6,12 +12,13 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA - * Main plugin class. Mostly a dummy for this plugin + * Main plugin class. */ public class KeyMasterGradlePlugin implements Plugin { @Override public void apply(@NotNull Project target) { target.getLogger().info("KeyMaster Plugin is activated"); + target.getExtensions().create("keymaster", KeyMasterGradleExtension.class); } } diff --git a/src/main/java/dev/firstdark/keymaster/tasks/SignJarTask.java b/src/main/java/dev/firstdark/keymaster/tasks/SignJarTask.java index 0594252..a4d8927 100644 --- a/src/main/java/dev/firstdark/keymaster/tasks/SignJarTask.java +++ b/src/main/java/dev/firstdark/keymaster/tasks/SignJarTask.java @@ -1,21 +1,33 @@ +/* + * This file is part of KeyMaster, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 HypherionSA and Contributors + * + */ package dev.firstdark.keymaster.tasks; +import dev.firstdark.keymaster.plugin.KeyMasterGradleExtension; +import dev.firstdark.keymaster.utils.PluginUtils; import lombok.Setter; -import org.apache.commons.lang3.StringUtils; -import org.gradle.api.Project; -import org.gradle.api.provider.Provider; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.gradle.api.GradleException; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.bundling.AbstractArchiveTask; import org.gradle.jvm.tasks.Jar; +import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.HashMap; -import java.util.Map; +import java.security.Security; + +import static dev.firstdark.keymaster.utils.PluginUtils.isNullOrBlank; +import static dev.firstdark.keymaster.utils.PluginUtils.resolveFile; /** * @author HypherionSA @@ -31,14 +43,20 @@ public class SignJarTask extends Jar { private String outputFileName = "signed.jar"; // KeyStore values - private String keyStorePass; - private String keyStore; - private String keyStoreAlias; - private String keyPass; + private String gpgKey; + private String gpgPassword; + private Boolean generateSignature = true; // Set the output directory. Defaults to build/libs private String outputDirectory = getProject().getBuildDir() + "/libs"; + @Nullable + private final KeyMasterGradleExtension extension; + + public SignJarTask() { + extension = getProject().getExtensions().findByType(KeyMasterGradleExtension.class); + } + @Input public Object getArtifactInput() { return resolveFile(getProject(), this.artifactInput); @@ -50,28 +68,40 @@ public class SignJarTask extends Jar { } @Input - public String getKeyStorePass() { - return this.keyStorePass; + @Optional + @Nullable + public String getGpgKey() { + if (!isNullOrBlank(gpgKey)) + return gpgKey; + + if (extension != null && !isNullOrBlank(extension.getGpgKey().getOrNull())) + return extension.getGpgKey().get(); + + return null; } @Input - public String getKeyStore() { - return this.keyStore; - } + @Optional + @Nullable + public String getGpgPassword() { + if (!isNullOrBlank(gpgPassword)) + return gpgPassword; - @Input - public String getKeyStoreAlias() { - return this.keyStoreAlias; - } + if (extension != null && !isNullOrBlank(extension.getGpgPassword().getOrNull())) + return extension.getGpgPassword().get(); - @Input - public String getKeyPass() { - return this.keyPass; + return null; } @Input public String getOutputDirectory() { - return this.outputDirectory; + if (!isNullOrBlank(outputDirectory)) + return outputDirectory; + + if (extension != null && !isNullOrBlank(extension.getOutputDirectory().getOrNull())) + return extension.getOutputDirectory().get(); + + return outputDirectory; } @OutputFile @@ -79,6 +109,14 @@ public class SignJarTask extends Jar { return new File(outputDirectory, outputFileName); } + @Input + public Boolean getGenerateSignature() { + if (extension != null && extension.getGenerateSignature().isPresent()) + return extension.getGenerateSignature().get(); + + return generateSignature; + } + /** * Main Task Logic */ @@ -86,14 +124,14 @@ public class SignJarTask extends Jar { @TaskAction public void doTask() { // Check that input is supplied - if (artifactInput == null) { + if (getArtifactInput() == null) { getProject().getLogger().error("Input cannot be null!"); return; } // Check that all the required values are supplied - if (isNullOrBlank(keyPass) || isNullOrBlank(keyStore) || isNullOrBlank(keyStoreAlias) || isNullOrBlank(keyStorePass)) { - getLogger().error("Please provide all required parameters: keyStore, keyStoreAlias, keyStorePass, keyPass"); + if (isNullOrBlank(getGpgKey()) || isNullOrBlank(getGpgPassword())) { + getLogger().error("Please provide all required parameters: keyStore, keyPass"); return; } @@ -101,10 +139,11 @@ public class SignJarTask extends Jar { File tempDir = new File(getProject().getBuildDir(), "signing"); tempDir.mkdirs(); + // Try to sign the jar try { - processArtifact(artifactInput, tempDir); + processArtifact(getArtifactInput(), tempDir); } catch (Exception e) { - getLogger().error("Failed to sign artifact {}", artifactInput, e); + getLogger().error("Failed to sign artifact {}", getArtifactInput(), e); } // Remove the temp working dir @@ -118,36 +157,35 @@ public class SignJarTask extends Jar { * @throws IOException This is mostly thrown when a file copy error occurs */ @SuppressWarnings("ResultOfMethodCallIgnored") - private void processArtifact(Object input, File tempDir) throws IOException { + private void processArtifact(Object input, File tempDir) throws IOException, PGPException { // Set up the input file File inputFile = resolveFile(getProject(), input); // Check if the output file is specified. If not, default to the input file - outputFileName = outputFileName.equalsIgnoreCase("signed.jar") ? inputFile.getName() : outputFileName + ".jar"; + outputFileName = getOutputFileName().equalsIgnoreCase("signed.jar") ? inputFile.getName() : getOutputFileName() + ".jar"; // Copy the original input file to the temporary processing folder File tempInput = new File(tempDir, inputFile.getName()); Files.copy(inputFile.toPath(), tempInput.toPath(), StandardCopyOption.REPLACE_EXISTING); // Create a temporary output jar - File tempOutput = new File(tempDir, outputFileName); + File tempOutput = new File(tempDir, getOutputFileName()); + File sigTempFile = new File(tempDir, tempOutput.getName() + ".sig"); + File outputSigFile = new File(getOutputFile().getParentFile(), getOutputFile().getName() + ".sig"); - // Configure the jar signing - Map map = new HashMap<>(); - map.put("alias", keyStoreAlias); - map.put("storePass", keyStorePass); - map.put("jar", tempInput.getAbsolutePath()); - map.put("signedJar", tempOutput.getAbsolutePath()); - map.put("keypass", keyPass); - map.put("keyStore", resolveFile(getProject(), keyStore).getAbsolutePath()); - - // SIGN IT - getProject().getAnt().invokeMethod("signjar", map); + // Sign the damn thing + signGPG(tempInput, sigTempFile, tempOutput); // Copy the signed jar to the libs folder Files.copy(tempOutput.toPath(), getOutputFile().toPath(), StandardCopyOption.REPLACE_EXISTING); - getProject().getLogger().lifecycle("Signed " + getOutputFile().getName() + " successfully"); + // Copy Signature File + if (getGenerateSignature()) { + Files.copy(sigTempFile.toPath(), outputSigFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + sigTempFile.delete(); + } + + getProject().getLogger().lifecycle("Signed {} successfully", getOutputFile().getName()); // Cleanup the temporary files tempOutput.delete(); @@ -155,45 +193,58 @@ public class SignJarTask extends Jar { } /** - * Helper method to check if a supplied string is null or empty - * @param s The string to test - * @return True if null or empty + * Main Signing logic. This handles reading the GPG keys, and doing the actual signing + * @param inputFile The jar to be signed + * @param signatureFile The GPG private key file or string + * @param signedOutputFile The output, signed jar file + * @throws IOException Thrown when a file error occurs + * @throws PGPException Thrown when a signature error occurs */ - private boolean isNullOrBlank(String s) { - if (s == null) - return true; + private void signGPG(File inputFile, File signatureFile, File signedOutputFile) throws IOException, PGPException { + // Load Bouncy Castle + BouncyCastleProvider provider = new BouncyCastleProvider(); + Security.addProvider(provider); - return StringUtils.isBlank(s); - } - - /** - * Resolve an Object to a File - * @param project The project the file potentially belongs to - * @param obj The object to process - * @return A File object, ready to use - */ - private File resolveFile(Project project, Object obj) { - if (obj == null) { - throw new NullPointerException("Null Path"); + // Load the GPG private key + byte[] keyBytes = PluginUtils.resolvePrivateKey(getGpgKey()); + if (keyBytes == null) { + throw new GradleException("Could not read GPG private key. Signing will fail"); } - if (obj instanceof Provider) { - Provider p = (Provider) obj; - obj = p.get(); - } + // Process the private key + try (ByteArrayInputStream keyInputStream = new ByteArrayInputStream(keyBytes); + FileOutputStream sigOutputStream = new FileOutputStream(signatureFile); + FileOutputStream signedOutputStream = new FileOutputStream(signedOutputFile)) { - if (obj instanceof File) { - return (File) obj; - } + PGPSecretKey secretKey = PluginUtils.readSecretKey(keyInputStream); + PGPPrivateKey privateKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(getGpgPassword().toCharArray())); - if (obj instanceof AbstractArchiveTask) { - return ((AbstractArchiveTask)obj).getArchiveFile().get().getAsFile(); - } + PGPSignatureGenerator signature = new PGPSignatureGenerator( + new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1) + .setProvider(provider)); + signature.init(PGPSignature.BINARY_DOCUMENT, privateKey); - if (obj instanceof String) { - return new File(obj.toString()); - } + // Write signature to output .sig file + if (getGenerateSignature()) { + try (FileInputStream inputStream = new FileInputStream(inputFile)) { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + signature.update(buffer, 0, bytesRead); + } - return project.file(obj); + signature.generate().encode(sigOutputStream); + } + } + + // Copy the signed content to the output signed jar file + try (FileInputStream inputStream = new FileInputStream(inputFile)) { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + signedOutputStream.write(buffer, 0, bytesRead); + } + } + } } } diff --git a/src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java b/src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java new file mode 100644 index 0000000..ec48dec --- /dev/null +++ b/src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java @@ -0,0 +1,135 @@ +/* + * This file is part of KeyMaster, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 HypherionSA and Contributors + * + */ +package dev.firstdark.keymaster.utils; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; + +/** + * @author HypherionSA + * Helper Methods used in the plugin + */ +public class PluginUtils { + + /** + * Helper method to load PGP secrets from the user specified input + * @param input The InputStream of the file/string to process + * @return The signing key + * @throws IOException Thrown when a file error occurs + * @throws PGPException Thrown when a signature error occurs + */ + public static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException { + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); + + Iterator keyRingIter = pgpSec.getKeyRings(); + while (keyRingIter.hasNext()) { + PGPSecretKeyRing keyRing = keyRingIter.next(); + + Iterator keyIter = keyRing.getSecretKeys(); + while (keyIter.hasNext()) { + PGPSecretKey key = keyIter.next(); + + if (key.isSigningKey()) { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find signing key in key ring."); + } + + /** + * Helper method to read a GPG private key from either a file, or String + * @param input File or String to process + * @return The read key bytes, or null + */ + public static byte[] resolvePrivateKey(String input) { + File f = new File(input); + + if (f.exists() && f.isFile()) { + try { + input = FileUtils.readFileToString(f, StandardCharsets.UTF_8); + } catch (Exception e) { + throw new GradleException("Failed to read GPG Private key", e); + } + } + + if (!isNullOrBlank(input)) { + if (input.startsWith("-----")) { + String[] parts = input.split("\n"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (!part.startsWith("-----")) { + sb.append(part); + } + } + return Base64.decodeBase64(sb.toString()); + } else { + return input.getBytes(StandardCharsets.UTF_8); + } + } + + return null; + } + + /** + * Helper method to check if a supplied string is null or empty + * @param s The string to test + * @return True if null or empty + */ + public static boolean isNullOrBlank(String s) { + if (s == null) + return true; + + return StringUtils.isBlank(s); + } + + /** + * Resolve an Object to a File + * @param project The project the file potentially belongs to + * @param obj The object to process + * @return A File object, ready to use + */ + public static File resolveFile(Project project, Object obj) { + if (obj == null) { + throw new NullPointerException("Null Path"); + } + + if (obj instanceof Provider) { + Provider p = (Provider) obj; + obj = p.get(); + } + + if (obj instanceof File) { + return (File) obj; + } + + if (obj instanceof AbstractArchiveTask) { + return ((AbstractArchiveTask)obj).getArchiveFile().get().getAsFile(); + } + + if (obj instanceof String) { + return new File(obj.toString()); + } + + return project.file(obj); + } +} diff --git a/testproject/build.gradle b/testproject/build.gradle index cc747d8..f66e8b6 100644 --- a/testproject/build.gradle +++ b/testproject/build.gradle @@ -16,6 +16,16 @@ dependencies { } +// This is optional. These values can be configured on the task +keymaster { + // GPG Password + gpgPassword = "123456" + // GPG Key file, or String. + gpgKey = System.getenv("GPG_KEY") + // Generate a .sig file for signed jars, to be used for verification + generateSignature = true +} + tasks.register('createDummyJar', Jar) { // Configure the JAR task to have no files from {} @@ -33,26 +43,12 @@ tasks.register('signJar', SignJarTask) { // Optional. Set the output name of the signed jar. This defaults to the artifactInput file name, and will overwrite it outputFileName = "testsign" - // The password of your key - keyPass = "123456" + // GPG Private key file or string. Not required when the extension is used + gpgKey = System.getenv("GPG_KEY") - // Your key alias - keyStoreAlias = "testalias" + // GPG Private Key password. Not required when extension is used + gpgPassword = "123456" - // Your keystore password - keyStorePass = "123456" - - // Your keystore location - keyStore = "/home/hypherionsa/dummystore.jks" -} - -// Example of signing another jar -tasks.register('signDummyJar', SignJarTask) { - dependsOn createDummyJar - artifactInput = createDummyJar - - keyPass = "123456" - keyStoreAlias = "testalias" - keyStorePass = "123456" - keyStore = "/home/hypherionsa/dummystore.jks" -} + // Should the task generate a .sig file. Defaults to true, and not required when extension is used + generateSignature = false +} \ No newline at end of file diff --git a/testproject/dummystore.jks b/testproject/dummystore.jks deleted file mode 100644 index 6b0555538cf5b530a6fb104aa3a64bb905fd67e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2238 zcmchY`#;l*AIJA`8QYpDKa=&E9{kmL=WXyF$h?J7cK`sep zh`E+qDWMfZ%q>yw_c`kO{XQP&FZllO{^9w0|MGsk-Z#!1XAT4caqR{0J0N6df3kyz ztAqcZl9>ONlK}$p0%!`)4q5;VMS%exK%VOW0Puo%C_FQNFiAa|`$P;vR-OuP;hGLmaIU%#s3(hD5nwA?*d7_uJkgVf(#Y*wK#nMGD?VC8PY^Kl0 z)k>qW(+*XE_AK=YUiz~m!HKwu3qMswO7JAZpz2#&QhK3>PqkL2W612W7>Z)WO{Tkd zSmfQ2n#!_ILrPT%C^Ei+F8}?@%0^X4~@(@S>YG#VmRWuC^z4+kuvT-2e|`*;teE}sfBP9 zf#3a_CeVv9N*!9xe%2%A7=hrk{Qr9>RAg z7nHKUy=cGjN%dB(?69txHQ!@SUS&tH5}*3)f@;)CyKmi!HB$ zuHMPT((95`^J_H>`{Xsl&cwl+-1NJ@{b-9f${EyY?pp$GJ(ff&y@?Zx`uK`S$<}X~ zT21x|)?)=f4oqunz@C{=n`q<8B(Ml0oX+mS-jl;fj&f~eS0PVc z&%=zR$ln01@W9$_$j#AbW`(mv>n47>Kk1Z^Em?5N^H|wmsrylmUjOML{ltL8 z5lyA)X$*Pt46B& zR<%s(BqNMsp!QY&#%kK6LY;#)(bny58#Pq+Jme4F$$K*+0&bZh1to_QQ=nt)~VI9fi_L+Kat+W@_BRDZcHB z7Fzh+FES^-Vo_cO9V1WE@^G2MUhUyWMe8G?#ZUIu*J`kYMlcdHzi%TiQ4xne4FYld zqbc0pXbP7_Ha8c*#l_>Iy<>xhgCX)!_^35F0Kj-bXcr6&4FPi#c)$oQOKptc-uc`E zegvP1pKE{v*%@;XjrgVTA@-SiUnjd9(I)x1IyqqWp`pKID1uw>+`0b|>^~%o7W$Rr zM!@jFJ}%CFuHIgz+Gr_JIQAs^Bw87Z!C=v7%ipMi#{L)okCjjWmY__dQtM?*!!|NqGwo);!~QStyUaNDgPJeL zjG6>i{D|-mu@*j&tm&e~8+DbPwcl;;k@1t;Pr_*^VnFH%5eso8c@^+A3(c)VcHqEG zU%!|~sdt&za+%9ufZSH@nczH#1-cF3NBcdwb7kIB( zIx+JY%U%onME=D(>j1<_u5F()g?e;DQ}c<_VHzx3cQp9Z2QRf?jIF^hY-f?>PA@opAzfX!PX?mDbe7jUc??_GP_HpW> z`>0`Y-T9W!g+%&}%#zykG^2wAA6X=g(`3q~1%B8~^vy}0IziX8iBHHT#8H>eEHrvP z#GzDv{9U_|oe&pf*%G*&B`38@>vgg_PaCHxA7}{{tkw&CAK*!~TgHS?WCtEsh;^_6 PIyW5#aL^ppf;`|KiYD*I