All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.javacodesign.Signer Maven / Gradle / Ivy

package io.github.javacodesign;


import com.github.tools1000.CommandRunner;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class Signer extends InputVerifier {

    private final static List DEFAULT_CODESIGNING_ARGS = Arrays.asList("-v", "--timestamp", "--force", "--options", "runtime");

    private final String identity;

    private final Path inputPath;

    private final Path launcher;

    private final Path entitlements;

    /**
     *
     * @param identity Developer certificate ID. For example, "Developer ID Application: John Doe (1234567890)"
     * @param inputPath Path to the .app to sign, for example MyApp.app
     * @param launcher Path to the executable that is launching the Java app
     */
    public Signer(String identity, Path inputPath, Path launcher, Path entitlements ) {
        this.identity = identity;
        this.inputPath = inputPath;
        this.launcher = launcher;
        this.entitlements = entitlements;
        verifyInput(identity);
    }

    public Signer(String identity, Path inputPath, Path launcher) {
        this(identity, inputPath, launcher, getPath("/default-entitlements.plist"));
    }

    private static Path getPath(String name) {
        try (InputStream in = Signer.class.getResourceAsStream(name);
             BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
            File tempDir = new File(System.getProperty("java.io.tmpdir"));
            File tempFile = new File(tempDir, name);
            tempFile.deleteOnExit();
            FileWriter fileWriter = new FileWriter(tempFile, true);
            BufferedWriter bw = new BufferedWriter(fileWriter);
            reader.lines().forEach(s -> {
                try {
                    bw.write(s);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            bw.close();
            log.info("Wrote {} to {}", name, tempFile);
            return tempFile.toPath();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Does the code signing.
     *
     * @throws IOException if an IO error occurs
     */
    public void sign() throws IOException {
        verifyInput();
        codeSignAll();
        codeSignJre();
        codeSignLaunchers();
    }

    private void verifyInput() throws IOException {
        verifyInput(inputPath, launcher, entitlements);
        checkCanExecute(launcher);
    }

    private void run(List command) throws IOException {
        CommandRunner commandRunner = new CommandRunner();
        CommandRunner.OutputStreams result = commandRunner.runCommand(command);
        if(result.getSerr() != null && !result.getSerr().isEmpty()){
            log.warn("Command {} finished with errors: {}", command, result.getSerr());
        }
    }

    private void codeSignLaunchers() throws IOException {
        run(buildSignLaunchersCommand());
    }

    private void codeSignJre() throws IOException {
        run(buildCodeSignJreCommand());
    }

    /**
     * Removes all signatures, "de-signs" all files.
     */
    public void removeSignature() throws IOException {
        run(buildRemoveSignatureCommand());
    }

    private void codeSignAll() throws IOException {
        run(buildCodeSignAllCommand());
    }

    private List buildSignLaunchersCommand(){
        List result = new ArrayList<>();

        result.add("codesign");
        result.add("-s");
        result.add(identity);
        result.addAll(DEFAULT_CODESIGNING_ARGS);
        result.add("--entitlements");
        result.add(entitlements.toString());
        result.add(launcher.toString());

        return result;
    }

    private List buildRemoveSignatureCommand() {
        List result = new ArrayList<>();

        result.add("find");
        result.add(inputPath.toString()+ AppConfig.JRE_PATH_IN_APP);
        result.add("-type");
        result.add("f");
        result.add("-exec");
        result.add("codesign");
        result.add("--remove-signature");
        result.add("{}");
        result.add(";");

        return result;
    }

    private List buildCodeSignAllCommand() {
        List result = new ArrayList<>();

        result.add("find");
        result.add(inputPath.toString());
        result.add("-depth");
        result.add("-type");
        result.add("f");
        result.add("-exec");
        result.add("codesign");
        result.add("-s");
        result.add(identity);
        result.addAll(DEFAULT_CODESIGNING_ARGS);
        result.add("{}");
        result.add(";");

        return result;
    }

    private List buildCodeSignJreCommand() {
        List result = new ArrayList<>();

        result.add("find");
        result.add(inputPath.toString());
        result.add("-depth");
        result.add("-name");
        result.add("jspawnhelper");
        result.add("-exec");
        result.add("codesign");
        result.add("-s");
        result.add(identity);
        result.addAll(DEFAULT_CODESIGNING_ARGS);
        result.add("--entitlements");
        result.add(entitlements.toString());
        result.add("{}");
        result.add(";");

        return result;
    }



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy