[Refactor] Use GPG instead of using legacy java keystores
This commit is contained in:
@@ -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}"
|
||||
|
@@ -4,4 +4,7 @@ version_patch=0
|
||||
|
||||
# Dependencies
|
||||
lombok=1.18.30
|
||||
commons_lang=3.14.0
|
||||
commons_lang=3.14.0
|
||||
commons_io=2.16.0
|
||||
commons_codec=1.16.1
|
||||
bouncy=1.77
|
91
readme.md
91
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<KeymasterExtension>("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<SignJarTask>("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<SignJarTask>("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<SignJarTask>("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
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -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<String> gpgKey;
|
||||
private final Property<String> gpgPassword;
|
||||
private final Property<Boolean> generateSignature;
|
||||
private final Property<String> 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");
|
||||
}
|
||||
|
||||
}
|
@@ -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<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(@NotNull Project target) {
|
||||
target.getLogger().info("KeyMaster Plugin is activated");
|
||||
target.getExtensions().create("keymaster", KeyMasterGradleExtension.class);
|
||||
}
|
||||
}
|
||||
|
@@ -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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
135
src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java
Normal file
135
src/main/java/dev/firstdark/keymaster/utils/PluginUtils.java
Normal file
@@ -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<PGPSecretKeyRing> keyRingIter = pgpSec.getKeyRings();
|
||||
while (keyRingIter.hasNext()) {
|
||||
PGPSecretKeyRing keyRing = keyRingIter.next();
|
||||
|
||||
Iterator<PGPSecretKey> 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);
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
Binary file not shown.
Reference in New Issue
Block a user