8215895: Verify and create tests for Mac installer specific signing options JDK-8200758-branch
authorherrick
Fri, 04 Oct 2019 14:56:02 -0400
branchJDK-8200758-branch
changeset 58464 d82489644b15
parent 58463 4e71249f291c
child 58466 47f0d21c7e8d
8215895: Verify and create tests for Mac installer specific signing options Submitted-by: almatvee Reviewed-by: herrick, asemenyuk
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java
test/jdk/tools/jpackage/macosx/SigningAppImageTest.java
test/jdk/tools/jpackage/macosx/SigningPackageTest.java
test/jdk/tools/jpackage/macosx/base/SigningBase.java
test/jdk/tools/jpackage/macosx/base/SigningCheck.java
test/jdk/tools/jpackage/share/AppImagePackageTest.java
test/jdk/tools/jpackage/share/InstallDirTest.java
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java	Fri Oct 04 14:56:02 2019 -0400
@@ -25,14 +25,10 @@
 
 package jdk.jpackage.internal;
 
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.Files;
@@ -45,21 +41,21 @@
 import java.util.List;
 import java.util.Locale;
 
-final class MacCertificate {
+public final class MacCertificate {
     private final String certificate;
     private final boolean verbose;
 
-    MacCertificate(String certificate) {
+    public MacCertificate(String certificate) {
         this.certificate = certificate;
         this.verbose = false;
     }
 
-    MacCertificate(String certificate, boolean verbose) {
+    public MacCertificate(String certificate, boolean verbose) {
         this.certificate = certificate;
         this.verbose = verbose;
     }
 
-    boolean isValid() {
+    public boolean isValid() {
         return verifyCertificate(this.certificate, verbose);
     }
 
--- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java	Fri Oct 04 14:56:02 2019 -0400
@@ -25,6 +25,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX;
 
@@ -72,26 +73,49 @@
 
         TKit.runTests(tests);
 
-        final long failedCount = tests.stream().filter(Functional.identity(
-                TestInstance::passed).negate()).count();
-        final long passedCount = tests.size() - failedCount;
-
+        final long passedCount = tests.stream().filter(TestInstance::passed).count();
         TKit.log(String.format("[==========] %d tests ran", tests.size()));
         TKit.log(String.format("[  PASSED  ] %d %s", passedCount,
                 passedCount == 1 ? "test" : "tests"));
-        if (failedCount != 0) {
-            TKit.log(String.format("[  FAILED  ] %d %s, listed below",
-                    failedCount,
-                    failedCount == 1 ? "test" : "tests"));
-            tests.stream().filter(
-                    Functional.identity(TestInstance::passed).negate()).forEach(
-                    test -> TKit.log(String.format("[  FAILED  ] %s",
-                            test.fullName())));
+
+        reportDetails(tests, "[  SKIPPED ]", TestInstance::skipped);
+        reportDetails(tests, "[  FAILED  ]", TestInstance::failed);
+
+        var withSkipped = reportSummary(tests, "SKIPPED", TestInstance::skipped);
+        var withFailures = reportSummary(tests, "FAILED", TestInstance::failed);
 
-            final String errorMessage = String.format("%d FAILED %s",
-                    failedCount, failedCount == 1 ? "TEST" : "TESTS");
-            TKit.log(errorMessage);
-            throw new RuntimeException(errorMessage);
+        if (withFailures != null) {
+            throw withFailures;
+        }
+
+        if (withSkipped != null) {
+            tests.stream().filter(TestInstance::skipped).findFirst().get().rethrowIfSkipped();
         }
     }
+
+    private static long reportDetails(List<TestInstance> tests,
+            String label, Predicate<TestInstance> selector) {
+        final long count = tests.stream().filter(selector).count();
+        if (count != 0) {
+            TKit.log(String.format("%s %d %s, listed below", label, count, count
+                    == 1 ? "test" : "tests"));
+            tests.stream().filter(selector).forEach(test -> TKit.log(
+                    String.format("%s %s", label, test.fullName())));
+        }
+
+        return count;
+    }
+
+    private static RuntimeException reportSummary(List<TestInstance> tests,
+            String label, Predicate<TestInstance> selector) {
+        final long count = tests.stream().filter(selector).count();
+        if (count != 0) {
+            final String message = String.format("%d %s %s", count, label, count
+                    == 1 ? "TEST" : "TESTS");
+            TKit.log(message);
+            return new RuntimeException(message);
+        }
+
+        return null;
+    }
 }
--- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java	Fri Oct 04 14:56:02 2019 -0400
@@ -70,6 +70,7 @@
 
         TestInstance test = new TestInstance(testBody);
         ThrowingRunnable.toRunnable(() -> runTests(List.of(test))).run();
+        test.rethrowIfSkipped();
         if (!test.passed()) {
             throw new RuntimeException();
         }
@@ -366,6 +367,16 @@
         throw new IllegalStateException("Unknown platform");
     }
 
+    public static RuntimeException throwSkippedException(String reason) {
+        trace("Skip the test: " + reason);
+        RuntimeException ex = ThrowingSupplier.toSupplier(
+                () -> (RuntimeException) Class.forName("jtreg.SkippedException").getConstructor(
+                        String.class).newInstance(reason)).get();
+
+        currentTest.notifySkipped(ex);
+        throw ex;
+    }
+
     static void waitForFileCreated(Path fileToWaitFor,
             long timeoutSeconds) throws IOException {
 
--- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java	Fri Oct 04 14:56:02 2019 -0400
@@ -34,7 +34,6 @@
 import jdk.jpackage.test.Functional.ThrowingRunnable;
 import jdk.jpackage.test.Functional.ThrowingSupplier;
 
-
 class TestInstance implements ThrowingRunnable {
 
     static class TestDesc {
@@ -100,8 +99,20 @@
         assertCount++;
     }
 
+    void notifySkipped(RuntimeException ex) {
+        skippedTestException = ex;
+    }
+
     boolean passed() {
-        return success;
+        return status == Status.Passed;
+    }
+
+    boolean skipped() {
+        return status == Status.Skipped;
+    }
+
+    boolean failed() {
+        return status == Status.Failed;
     }
 
     String functionName() {
@@ -116,6 +127,12 @@
         return testDesc.testFullName();
     }
 
+    void rethrowIfSkipped() {
+        if (skippedTestException != null) {
+            throw skippedTestException;
+        }
+    }
+
     @Override
     public void run() throws Throwable {
         final String fullName = testDesc.testFullName();
@@ -131,10 +148,14 @@
                 afterActions.forEach(a -> TKit.ignoreExceptions(() -> a.accept(
                         testInstance)));
             }
-            success = true;
+            status = Status.Passed;
         } finally {
-            TKit.log(String.format("%s %s; checks=%d",
-                    success ? "[       OK ]" : "[  FAILED  ]", fullName,
+            if (skippedTestException != null) {
+                status = Status.Skipped;
+            } else if (status == null) {
+                status = Status.Failed;
+            }
+            TKit.log(String.format("%s %s; checks=%d", status, fullName,
                     assertCount));
         }
     }
@@ -150,8 +171,26 @@
         return null;
     }
 
+    private enum Status {
+        Passed("[       OK ]"),
+        Failed("[  FAILED  ]"),
+        Skipped("[  SKIPPED ]");
+
+        Status(String msg) {
+            this.msg = msg;
+        }
+
+        @Override
+        public String toString() {
+            return msg;
+        }
+
+        private final String msg;
+    }
+
     private int assertCount;
-    private boolean success;
+    private Status status;
+    private RuntimeException skippedTestException;
     private final TestDesc testDesc;
     private final ThrowingFunction testConstructor;
     private final ThrowingConsumer testBody;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java	Fri Oct 04 14:56:02 2019 -0400
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.file.Path;
+import jdk.jpackage.test.JPackageCommand;
+import jdk.jpackage.test.TKit;
+
+/**
+ * Tests generation of app image with --mac-sign and related arguments. Test will
+ * generate app image and verify signature of main launcher and app bundle itself.
+ * This test requires that machine is configured with test certificate for
+ * "Developer ID Application: jpackage.openjdk.java.net" in jpackagerTest keychain with
+ * always allowed access to this keychain for user which runs test.
+ */
+
+/*
+ * @test
+ * @summary jpackage with --package-type app-image --mac-sign
+ * @library ../helpers
+ * @library /test/lib
+ * @library base
+ * @build SigningBase
+ * @build SigningCheck
+ * @build jtreg.SkippedException
+ * @build jdk.jpackage.test.*
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @requires (os.family == "mac")
+ * @run main/othervm -Xmx512m SigningAppImageTest
+ */
+public class SigningAppImageTest {
+
+    public static void main(String[] args) throws Exception {
+        TKit.run(args, () -> {
+            SigningCheck.checkCertificates();
+
+            JPackageCommand cmd = JPackageCommand.helloAppImage();
+            cmd.addArguments("--mac-sign", "--mac-signing-key-user-name",
+                    SigningBase.DEV_NAME, "--mac-signing-keychain",
+                    "jpackagerTest.keychain");
+            cmd.executeAndAssertHelloAppImageCreated();
+
+            Path launcherPath = cmd.appImage().resolve(cmd.launcherPathInAppImage());
+            SigningBase.verifyCodesign(launcherPath, true);
+
+            Path appImage = cmd.appImage();
+            SigningBase.verifyCodesign(appImage, true);
+            SigningBase.verifySpctl(appImage, "exec");
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java	Fri Oct 04 14:56:02 2019 -0400
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.jpackage.test.*;
+
+/**
+ * Tests generation of dmg and pkg with --mac-sign and related arguments. Test will
+ * generate pkg and verifies its signature. It verifies that dmg is not signed, but app
+ * image inside dmg is signed. This test requires that machine is configured with test
+ * certificate for "Developer ID Installer: jpackage.openjdk.java.net" in jpackagerTest
+ * keychain with always allowed access to this keychain for user which runs test.
+ */
+
+/*
+ * @test
+ * @summary jpackage with --package-type pkg,dmg --mac-sign
+ * @library ../helpers
+ * @library /test/lib
+ * @library base
+ * @build SigningBase
+ * @build SigningCheck
+ * @build jtreg.SkippedException
+ * @build jdk.jpackage.test.*
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @requires (os.family == "mac")
+ * @run main/othervm -Xmx512m SigningPackageTest
+ */
+public class SigningPackageTest {
+
+    private static void verifyPKG(JPackageCommand cmd) {
+        Path outputBundle = cmd.outputBundle();
+        SigningBase.verifyPkgutil(outputBundle);
+        SigningBase.verifySpctl(outputBundle, "install");
+    }
+
+    private static void verifyDMG(JPackageCommand cmd) {
+        Path outputBundle = cmd.outputBundle();
+        SigningBase.verifyCodesign(outputBundle, false);
+    }
+
+    private static void verifyAppImageInDMG(JPackageCommand cmd) throws Exception {
+        Path disk = Paths.get("/Volumes", cmd.name());
+        try {
+            new Executor()
+                    .setExecutable("/usr/bin/hdiutil")
+                    .addArgument("attach").addArgument(cmd.outputBundle())
+                    .execute().assertExitCodeIsZero();
+
+            Path appImageInDMG = disk.resolve(cmd.name() + ".app");
+            Path launcherPath = appImageInDMG.resolve(Path.of("Contents", "MacOS", cmd.name()));
+            SigningBase.verifyCodesign(launcherPath, true);
+            SigningBase.verifyCodesign(appImageInDMG, true);
+            SigningBase.verifySpctl(appImageInDMG, "exec");
+        } finally {
+            new Executor()
+                    .setExecutable("/usr/bin/hdiutil")
+                    .addArgument("detach").addArgument(disk)
+                    .execute().assertExitCodeIsZero();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        TKit.run(args, () -> {
+            SigningCheck.checkCertificates();
+
+            new PackageTest()
+                    .configureHelloApp()
+                    .forTypes(PackageType.MAC)
+                    .addInitializer(cmd -> {
+                        cmd.addArguments("--mac-sign",
+                                "--mac-signing-key-user-name", SigningBase.DEV_NAME,
+                                "--mac-signing-keychain", "jpackagerTest.keychain");
+                    })
+                    .forTypes(PackageType.MAC_PKG)
+                    .addBundleVerifier(cmd -> {
+                        verifyPKG(cmd);
+                    })
+                    .forTypes(PackageType.MAC_DMG)
+                    .addBundleVerifier(cmd -> {
+                        verifyDMG(cmd);
+                        verifyAppImageInDMG(cmd);
+                    })
+                    .run();
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/macosx/base/SigningBase.java	Fri Oct 04 14:56:02 2019 -0400
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.file.Path;
+import java.util.List;
+
+import jdk.jpackage.test.TKit;
+import jdk.jpackage.test.Executor;
+
+public class SigningBase {
+
+    public static String DEV_NAME = "jpackage.openjdk.java.net";
+    public static String APP_CERT
+            = "Developer ID Application: " + DEV_NAME;
+    public static String INSTALLER_CERT
+            = "Developer ID Installer: " + DEV_NAME;
+    public static String KEYCHAIN = "jpackagerTest.keychain";
+
+    private static void checkString(List<String> result, String lookupString) {
+        result.stream()
+                .filter(line -> line.trim().equals(lookupString)).findFirst().or(
+                () -> {
+                    TKit.assertUnexpected(String.format(
+                            "Failed to find [%s] string in the output",
+                            lookupString));
+                    return null;
+                });
+    }
+
+    private static List<String> codesignResult(Path target, boolean signed) {
+        int exitCode = signed ? 0 : 1;
+        List<String> result = new Executor()
+                .setExecutable("codesign")
+                .addArguments("--verify", "--deep", "--strict", "--verbose=2",
+                        target.toString())
+                .saveOutput()
+                .execute()
+                .assertExitCodeIs(exitCode).getOutput();
+
+        return result;
+    }
+
+    private static void verifyCodesignResult(List<String> result, Path target,
+            boolean signed) {
+        result.stream().peek(TKit::trace);
+        if (signed) {
+            String lookupString = target.toString() + ": valid on disk";
+            checkString(result, lookupString);
+            lookupString = target.toString() + ": satisfies its Designated Requirement";
+            checkString(result, lookupString);
+        } else {
+            String lookupString = target.toString()
+                    + ": code object is not signed at all";
+            checkString(result, lookupString);
+        }
+    }
+
+    private static List<String> spctlResult(Path target, String type) {
+        List<String> result = new Executor()
+                .setExecutable("/usr/sbin/spctl")
+                .addArguments("-vvv", "--assess", "--type", type,
+                        target.toString())
+                .executeAndGetOutput();
+
+        return result;
+    }
+
+    private static void verifySpctlResult(List<String> result, Path target, String type) {
+        result.stream().peek(TKit::trace);
+        String lookupString = target.toString() + ": accepted";
+        checkString(result, lookupString);
+        lookupString = "source=" + DEV_NAME;
+        checkString(result, lookupString);
+        if (type.equals("install")) {
+            lookupString = "origin=" + INSTALLER_CERT;
+        } else {
+            lookupString = "origin=" + APP_CERT;
+        }
+        checkString(result, lookupString);
+    }
+
+    private static List<String> pkgutilResult(Path target) {
+        List<String> result = new Executor()
+                .setExecutable("/usr/sbin/pkgutil")
+                .addArguments("--check-signature",
+                        target.toString())
+                .executeAndGetOutput();
+
+        return result;
+    }
+
+    private static void verifyPkgutilResult(List<String> result) {
+        result.stream().peek(line -> TKit.trace(line));
+        String lookupString = "Status: signed by a certificate trusted for current user";
+        checkString(result, lookupString);
+        lookupString = "1. " + INSTALLER_CERT;
+        checkString(result, lookupString);
+    }
+
+    public static void verifyCodesign(Path target, boolean signed) {
+        List<String> result = codesignResult(target, signed);
+        verifyCodesignResult(result, target, signed);
+    }
+
+    public static void verifySpctl(Path target, String type) {
+        List<String> result = spctlResult(target, type);
+        verifySpctlResult(result, target, type);
+    }
+
+    public static void verifyPkgutil(Path target) {
+        List<String> result = pkgutilResult(target);
+        verifyPkgutilResult(result);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/macosx/base/SigningCheck.java	Fri Oct 04 14:56:02 2019 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import jdk.jpackage.test.TKit;
+import jdk.jpackage.test.Executor;
+
+import jdk.jpackage.internal.MacCertificate;
+
+public class SigningCheck {
+
+    public static void checkCertificates() {
+        List<String> result = findCertificate(SigningBase.APP_CERT, SigningBase.KEYCHAIN);
+        String key = findKey(SigningBase.APP_CERT, result);
+        validateCertificate(key);
+        validateCertificateTrust(SigningBase.APP_CERT);
+
+        result = findCertificate(SigningBase.INSTALLER_CERT, SigningBase.KEYCHAIN);
+        key = findKey(SigningBase.INSTALLER_CERT, result);
+        validateCertificate(key);
+        validateCertificateTrust(SigningBase.INSTALLER_CERT);
+    }
+
+    private static List<String> findCertificate(String name, String keyChain) {
+        List<String> result = new Executor()
+                .setExecutable("security")
+                .addArguments("find-certificate", "-c", name, "-a", keyChain)
+                .executeAndGetOutput();
+
+        return result;
+    }
+
+    private static String findKey(String name, List<String> result) {
+        Pattern p = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\"");
+        Matcher m = p.matcher(result.stream().collect(Collectors.joining()));
+        if (!m.find()) {
+            TKit.trace("Did not found a key for '" + name + "'");
+            return null;
+        }
+        String matchedKey = m.group(1);
+        if (m.find()) {
+            TKit.trace("Found more than one key for '" + name + "'");
+            return null;
+        }
+        TKit.trace("Using key '" + matchedKey);
+        return matchedKey;
+    }
+
+    private static void validateCertificate(String key) {
+        if (key != null) {
+            MacCertificate certificate = new MacCertificate(
+                    key, true);
+            if (!certificate.isValid()) {
+                TKit.throwSkippedException("Certifcate expired: " + key);
+            } else {
+                return;
+            }
+        }
+
+        TKit.throwSkippedException("Cannot find required certifciates: " + key);
+    }
+
+    private static void validateCertificateTrust(String name) {
+        List<String> result = new Executor()
+                .setExecutable("security")
+                .addArguments("dump-trust-settings")
+                .executeAndGetOutput();
+        result.stream().peek(TKit::trace);
+        result.stream()
+                .filter(line -> line.trim().endsWith(name)).findFirst().orElseThrow(
+                () -> {
+                    throw TKit.throwSkippedException("Certifcate not trusted by current user: "
+                            + name);
+                });
+    }
+
+}
--- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.java	Fri Oct 04 14:56:02 2019 -0400
@@ -39,7 +39,7 @@
  * @library ../helpers
  * @build jdk.jpackage.test.*
  * @modules jdk.jpackage/jdk.jpackage.internal
- * @run main/othervm/timeout=360 -Xmx512m AppImagePackageTest
+ * @run main/othervm/timeout=540 -Xmx512m AppImagePackageTest
  */
 public class AppImagePackageTest {
 
--- a/test/jdk/tools/jpackage/share/InstallDirTest.java	Fri Oct 04 14:53:39 2019 -0400
+++ b/test/jdk/tools/jpackage/share/InstallDirTest.java	Fri Oct 04 14:56:02 2019 -0400
@@ -59,7 +59,7 @@
  * @build jdk.jpackage.test.*
  * @compile InstallDirTest.java
  * @modules jdk.jpackage/jdk.jpackage.internal
- * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
+ * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
  *  --jpt-run=InstallDirTest.testCommon
  */