diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java
index f165c149b04f6..543bbe2987512 100644
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java
@@ -265,9 +265,29 @@ private void writeEntry(InputStream in, Path dstFile) throws IOException {
     @Override
     public void prepareApplicationFiles(Map<String, ? super Object> params)
             throws IOException {
-        // If predefine app image is provided, then just sign it and return.
-        if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
-            doSigning(params);
+        // If predefined app image is provided, then just sign it and return.
+        Path predefinedAppImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
+        if (predefinedAppImage != null) {
+            // Mark app image as signed, before we signing it.
+            AppImageFile appImageFile =
+                AppImageFile.load(predefinedAppImage);
+            if (!appImageFile.isSigned()) {
+                appImageFile.copyAsSigned().save(predefinedAppImage);
+            } else {
+                appImageFile = null;
+            }
+
+            try {
+                doSigning(params);
+            } catch (Exception ex) {
+                // Restore original app image file if signing failed
+                if (appImageFile != null) {
+                    appImageFile.save(predefinedAppImage);
+                }
+
+                throw ex;
+            }
+
             return;
         }
 
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java
index 4a8ee4f501729..4e46d9a59cc52 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022, 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
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java
index c66cb47cf2e8e..8ce67092b81f8 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java
@@ -33,6 +33,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -59,11 +61,12 @@
 public final class AppImageFile {
 
     // These values will be loaded from AppImage xml file.
-    private final String creatorVersion;
-    private final String creatorPlatform;
+    private final String appVersion;
     private final String launcherName;
     private final String mainClass;
     private final List<LauncherInfo> addLauncherInfos;
+    private final String creatorVersion;
+    private final String creatorPlatform;
     private final boolean signed;
     private final boolean appStore;
 
@@ -73,15 +76,13 @@ public final class AppImageFile {
             Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC,
             "macOS");
 
-    private AppImageFile(Path appImageDir, String launcherName, String mainClass,
-            List<LauncherInfo> launcherInfos, String creatorVersion,
-            String creatorPlatform, String signedStr, String appStoreStr) {
+    private AppImageFile(Path appImageDir, String appVersion, String launcherName,
+            String mainClass, List<LauncherInfo> launcherInfos,
+            String creatorVersion, String creatorPlatform, String signedStr,
+            String appStoreStr) {
         boolean isValid = true;
-        if (!Objects.equals(getVersion(), creatorVersion)) {
-            isValid = false;
-        }
 
-        if (!Objects.equals(getPlatform(), creatorPlatform)) {
+        if (appVersion == null || appVersion.length() == 0) {
             isValid = false;
         }
 
@@ -99,6 +100,14 @@ private AppImageFile(Path appImageDir, String launcherName, String mainClass,
             }
         }
 
+        if (!Objects.equals(getVersion(), creatorVersion)) {
+            isValid = false;
+        }
+
+        if (!Objects.equals(getPlatform(), creatorPlatform)) {
+            isValid = false;
+        }
+
         if (signedStr == null ||
                 !("true".equals(signedStr) || "false".equals(signedStr))) {
             isValid = false;
@@ -111,9 +120,11 @@ private AppImageFile(Path appImageDir, String launcherName, String mainClass,
 
         if (!isValid) {
             throw new RuntimeException(MessageFormat.format(I18N.getString(
-                "error.invalid-app-image"), appImageDir));
+                "error.invalid-app-image"), appImageDir,
+                AppImageFile.getPathInAppImage(appImageDir)));
         }
 
+        this.appVersion = appVersion;
         this.launcherName = launcherName;
         this.mainClass = mainClass;
         this.addLauncherInfos = launcherInfos;
@@ -132,6 +143,13 @@ List<LauncherInfo> getAddLaunchers() {
         return addLauncherInfos;
     }
 
+    /**
+     * Returns application version. Never returns null or empty value.
+     */
+    String getAppVersion() {
+        return appVersion;
+    }
+
     /**
      * Returns main application launcher name. Never returns null or empty value.
      */
@@ -150,7 +168,7 @@ public boolean isSigned() {
         return signed;
     }
 
-    boolean isAppStore() {
+    public boolean isAppStore() {
         return appStore;
     }
 
@@ -165,53 +183,118 @@ public static Path getPathInAppImage(Path appImageDir) {
                 .resolve(FILENAME);
     }
 
+    /**
+     * Saves file with application image info in application image using values
+     * from current instance.
+     * @param appImageDir - path to application image
+     * @throws IOException
+     */
+    void save(Path appImageDir) throws IOException {
+        AppImageFile.save(appImageDir, null, this);
+    }
+
     /**
      * Saves file with application image info in application image.
      * @param appImageDir - path to application image
+     * @param params - parameters used to generate application image
      * @throws IOException
      */
     static void save(Path appImageDir, Map<String, Object> params)
             throws IOException {
+        AppImageFile.save(appImageDir, params, null);
+    }
+
+    /**
+     * Saves file with application image info in application image using params
+     * or appImage. Both params or appImage cannot be valid.
+     * @param appImageDir - path to application image
+     * @param params - parameters used to generate application image
+     * @param appImage - instance of already existing application image file
+     * @throws IOException
+     * @throws IllegalArgumentException - If both params and appImage are null or
+     *                                    If both params and appImage are not null
+     */
+    private static void save(Path appImageDir,
+            Map<String, Object> params,
+            AppImageFile appImage) throws IOException {
+        if ((params == null && appImage == null) ||
+            (params != null && appImage != null)) {
+                throw new IllegalArgumentException();
+        }
+
+        final String appVersionSave;
+        final String mainLauncherSave;
+        final String mainClassSave;
+        final String signedSave;
+        final String appStoreSave;
+        final List<LauncherInfo> addLauncherInfoSave;
+        if (params != null) {
+            appVersionSave = VERSION.fetchFrom(params);
+            mainLauncherSave = APP_NAME.fetchFrom(params);
+            mainClassSave = MAIN_CLASS.fetchFrom(params);
+            signedSave = SIGN_BUNDLE.fetchFrom(params).toString();
+            appStoreSave = APP_STORE.fetchFrom(params).toString();
+            addLauncherInfoSave = null;
+        } else {
+            appVersionSave = appImage.getAppVersion();
+            mainLauncherSave = appImage.getLauncherName();
+            mainClassSave = appImage.getMainClass();
+            signedSave = String.valueOf(appImage.isSigned());
+            appStoreSave = String.valueOf(appImage.isAppStore());
+            addLauncherInfoSave = appImage.getAddLaunchers();
+        }
+
         IOUtils.createXml(getPathInAppImage(appImageDir), xml -> {
             xml.writeStartElement("jpackage-state");
             xml.writeAttribute("version", getVersion());
             xml.writeAttribute("platform", getPlatform());
 
             xml.writeStartElement("app-version");
-            xml.writeCharacters(VERSION.fetchFrom(params));
+            xml.writeCharacters(appVersionSave);
             xml.writeEndElement();
 
             xml.writeStartElement("main-launcher");
-            xml.writeCharacters(APP_NAME.fetchFrom(params));
+            xml.writeCharacters(mainLauncherSave);
             xml.writeEndElement();
 
             xml.writeStartElement("main-class");
-            xml.writeCharacters(MAIN_CLASS.fetchFrom(params));
+            xml.writeCharacters(mainClassSave);
             xml.writeEndElement();
 
             xml.writeStartElement("signed");
-            xml.writeCharacters(SIGN_BUNDLE.fetchFrom(params).toString());
+            xml.writeCharacters(signedSave);
             xml.writeEndElement();
 
             xml.writeStartElement("app-store");
-            xml.writeCharacters(APP_STORE.fetchFrom(params).toString());
+            xml.writeCharacters(appStoreSave);
             xml.writeEndElement();
 
-            List<Map<String, ? super Object>> addLaunchers =
-                ADD_LAUNCHERS.fetchFrom(params);
-
-            for (var launcherParams : addLaunchers) {
-                var li = new LauncherInfo(launcherParams);
-                xml.writeStartElement("add-launcher");
-                xml.writeAttribute("name", li.getName());
-                xml.writeAttribute("shortcut", Boolean.toString(li.isShortcut()));
-                xml.writeAttribute("menu", Boolean.toString(li.isMenu()));
-                xml.writeAttribute("service", Boolean.toString(li.isService()));
-                xml.writeEndElement();
+            if (addLauncherInfoSave != null) {
+                for (var li : addLauncherInfoSave) {
+                    addLauncherInfo(xml, li);
+                }
+            } else {
+                List<Map<String, ? super Object>> addLaunchers =
+                    ADD_LAUNCHERS.fetchFrom(params);
+
+                for (var launcherParams : addLaunchers) {
+                    var li = new LauncherInfo(launcherParams);
+                    addLauncherInfo(xml, li);
+                }
             }
         });
     }
 
+    static void addLauncherInfo(XMLStreamWriter xml, LauncherInfo li)
+            throws XMLStreamException {
+        xml.writeStartElement("add-launcher");
+        xml.writeAttribute("name", li.getName());
+        xml.writeAttribute("shortcut", Boolean.toString(li.isShortcut()));
+        xml.writeAttribute("menu", Boolean.toString(li.isMenu()));
+        xml.writeAttribute("service", Boolean.toString(li.isService()));
+        xml.writeEndElement();
+    }
+
     /**
      * Loads application image info from application image.
      * @param appImageDir - path to application image
@@ -224,6 +307,9 @@ public static AppImageFile load(Path appImageDir) {
 
             XPath xPath = XPathFactory.newInstance().newXPath();
 
+            String appVersion = xpathQueryNullable(xPath,
+                    "/jpackage-state/app-version/text()", doc);
+
             String mainLauncher = xpathQueryNullable(xPath,
                     "/jpackage-state/main-launcher/text()", doc);
 
@@ -252,8 +338,9 @@ public static AppImageFile load(Path appImageDir) {
                  launcherInfos.add(new LauncherInfo(launcherNodes.item(i)));
             }
 
-            return new AppImageFile(appImageDir, mainLauncher, mainClass,
-                    launcherInfos, version, platform, signedStr, appStoreStr);
+            return new AppImageFile(appImageDir, appVersion, mainLauncher,
+                    mainClass, launcherInfos, version, platform, signedStr,
+                    appStoreStr);
         } catch (XPathExpressionException ex) {
             // This should never happen as XPath expressions should be correct
             throw new RuntimeException(ex);
@@ -266,10 +353,22 @@ public static AppImageFile load(Path appImageDir) {
         }
     }
 
-    private static String getAttribute(Node item, String attr) {
-        NamedNodeMap attrs = item.getAttributes();
-        Node attrNode = attrs.getNamedItem(attr);
-        return ((attrNode == null) ? null : attrNode.getNodeValue());
+    /**
+     * Returns copy of AppImageFile, but with signed set to true if AppImageFile
+     * is not marked as signed. If AppImageFile already signed it will return
+     * instance to itself.
+     */
+    public AppImageFile copyAsSigned() {
+        if (isSigned()) {
+            return this;
+        }
+
+        // Pass null for appImageDir, it is used only to show location of
+        // .jpackage.xml in case of error. copyAsSigned() should not produce
+        // invalid app image file.
+        return new AppImageFile(null, getAppVersion(),
+                getLauncherName(), getMainClass(), getAddLaunchers(),
+                getVersion(), getPlatform(), "true", String.valueOf(isAppStore()));
     }
 
     public static Document readXml(Path appImageDir) throws IOException {
@@ -362,6 +461,12 @@ private LauncherInfo(Node node) {
             this.service = !"false".equals(getAttribute(node, "service"));
         }
 
+        private String getAttribute(Node item, String attr) {
+            NamedNodeMap attrs = item.getAttributes();
+            Node attrNode = attrs.getNamedItem(attr);
+            return ((attrNode == null) ? null : attrNode.getNodeValue());
+        }
+
         public String getName() {
             return name;
         }
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties
index ebec3aee43627..5297ef92d3414 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties
@@ -78,8 +78,8 @@ error.no.name=Name not specified with --name and cannot infer one from app-image
 
 warning.no.jdk.modules.found=Warning: No JDK Modules found
 
-error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir ({0})
-error.invalid-app-image=Error: app-image dir ({0}) generated by another jpackage version or malformed .jpackage.xml
+error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir "{0}"
+error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage version or malformed "{1}"
 
 MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package
 MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties
index ddf156e4ff28e..eaa60273a27c2 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties
@@ -76,8 +76,8 @@ error.no.name=Name nicht mit --name angegeben. Es kann auch kein Name aus app-im
 
 warning.no.jdk.modules.found=Warnung: Keine JDK-Module gefunden
 
-error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir ({0})
-error.invalid-app-image=Error: app-image dir ({0}) generated by another jpackage version or malformed .jpackage.xml
+error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir "{0}"
+error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage version or malformed "{1}"
 
 MSG_BundlerFailed=Fehler: Bundler "{1}" ({0}) konnte kein Package generieren
 MSG_BundlerConfigException=Bundler {0} aufgrund eines Konfigurationsproblems \u00FCbersprungen: {1} \nEmpfehlung zur Behebung: {2}
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties
index 707cf533f88e2..b6ab53989706a 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties
@@ -76,8 +76,8 @@ error.no.name=\u540D\u524D\u304C--name\u3067\u6307\u5B9A\u3055\u308C\u3066\u304A
 
 warning.no.jdk.modules.found=\u8B66\u544A: JDK\u30E2\u30B8\u30E5\u30FC\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
 
-error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir ({0})
-error.invalid-app-image=Error: app-image dir ({0}) generated by another jpackage version or malformed .jpackage.xml
+error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir "{0}"
+error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage version or malformed "{1}"
 
 MSG_BundlerFailed=\u30A8\u30E9\u30FC: \u30D0\u30F3\u30C9\u30E9"{1}" ({0})\u304C\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F
 MSG_BundlerConfigException=\u69CB\u6210\u306E\u554F\u984C\u306E\u305F\u3081\u3001\u30D0\u30F3\u30C9\u30E9{0}\u304C\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F: {1} \n\u6B21\u306E\u4FEE\u6B63\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044: {2}
diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties
index 92f510bfedf46..2fa426df094de 100644
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties
@@ -76,8 +76,8 @@ error.no.name=\u672A\u4F7F\u7528 --name \u6307\u5B9A\u540D\u79F0\uFF0C\u65E0\u6C
 
 warning.no.jdk.modules.found=\u8B66\u544A: \u672A\u627E\u5230 JDK \u6A21\u5757
 
-error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir ({0})
-error.invalid-app-image=Error: app-image dir ({0}) generated by another jpackage version or malformed .jpackage.xml
+error.foreign-app-image=Error: Missing .jpackage.xml file in app-image dir "{0}"
+error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage version or malformed "{1}"
 
 MSG_BundlerFailed=\u9519\u8BEF\uFF1A\u6253\u5305\u7A0B\u5E8F "{1}" ({0}) \u65E0\u6CD5\u751F\u6210\u7A0B\u5E8F\u5305
 MSG_BundlerConfigException=\u7531\u4E8E\u914D\u7F6E\u95EE\u9898, \u8DF3\u8FC7\u4E86\u6253\u5305\u7A0B\u5E8F{0}: {1} \n\u4FEE\u590D\u5EFA\u8BAE: {2}
diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
index 639eeaf380bd8..6e6897bb12f00 100644
--- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
+++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
@@ -395,10 +395,16 @@ public JPackageCommand setDefaultAppName() {
      * Returns path to output bundle of configured jpackage command.
      *
      * If this is build image command, returns path to application image directory.
+     *
+     * Special case for masOS. If this is sign app image command, returns value
+     * of "--app-image".
      */
     public Path outputBundle() {
         final String bundleName;
         if (isImagePackageType()) {
+            if (TKit.isOSX() && hasArgument("--app-image")) {
+                return Path.of(getArgumentValue("--app-image", () -> null));
+            }
             String dirName = name();
             if (TKit.isOSX()) {
                 dirName = dirName + ".app";
@@ -818,12 +824,34 @@ JPackageCommand assertAppLayout() {
     }
 
     private void assertAppImageFile() {
-        final Path lookupPath = AppImageFile.getPathInAppImage(Path.of(""));
+        Path appImageDir = Path.of("");
+        if (isImagePackageType() && hasArgument("--app-image")) {
+            appImageDir = Path.of(getArgumentValue("--app-image", () -> null));
+        }
 
+        final Path lookupPath = AppImageFile.getPathInAppImage(appImageDir);
         if (isRuntime() || (!isImagePackageType() && !TKit.isOSX())) {
             assertFileInAppImage(lookupPath, null);
         } else {
             assertFileInAppImage(lookupPath, lookupPath);
+
+            // If file exist validated important values based on arguments
+            // Exclude validation when we generating packages from predefined
+            // app images, since we do not know if image is signed or not.
+            if (isImagePackageType() || !hasArgument("--app-image")) {
+                final Path rootDir = isImagePackageType() ? outputBundle() :
+                        pathToUnpackedPackageFile(appInstallationDirectory());
+
+                boolean expectedValue = hasArgument("--mac-sign");
+                boolean actualValue = AppImageFile.load(rootDir).isSigned();
+                TKit.assertTrue(expectedValue == actualValue,
+                    "Unexptected value in app image file for <signed>");
+
+                expectedValue = hasArgument("--mac-app-store");
+                actualValue = AppImageFile.load(rootDir).isAppStore();
+                TKit.assertTrue(expectedValue == actualValue,
+                    "Unexptected value in app image file for <app-store>");
+            }
         }
     }
 
diff --git a/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java b/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java
index 894a76b382aa6..cbf06a5e241f6 100644
--- a/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java
+++ b/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java
@@ -75,7 +75,7 @@ public void testInvalidCommandLine() throws IOException {
         params = new LinkedHashMap<>();
         params.put(Arguments.CLIOptions.NAME.getId(), "foo");
         params.put(Arguments.CLIOptions.APPCLASS.getId(), "TestClass");
-        params.put(Arguments.CLIOptions.VERSION.getId(), "");
+        params.put(Arguments.CLIOptions.VERSION.getId(), "1.0");
         create(params);
     }
 
@@ -104,6 +104,7 @@ public void testInavlidXml() throws IOException {
     public void testValidXml() throws IOException {
         Assert.assertEquals("Foo", (createFromXml(
                 JPACKAGE_STATE_OPEN,
+                    "<app-version>1.0</app-version>",
                     "<main-launcher>Foo</main-launcher>",
                     "<main-class>main.Class</main-class>",
                     "<signed>false</signed>",
@@ -112,6 +113,7 @@ public void testValidXml() throws IOException {
 
         Assert.assertEquals("Boo", (createFromXml(
                 JPACKAGE_STATE_OPEN,
+                    "<app-version>1.0</app-version>",
                     "<main-launcher>Boo</main-launcher>",
                     "<main-launcher>Bar</main-launcher>",
                     "<main-class>main.Class</main-class>",
@@ -121,6 +123,7 @@ public void testValidXml() throws IOException {
 
         var file = createFromXml(
                 JPACKAGE_STATE_OPEN,
+                    "<app-version>1.0</app-version>",
                     "<main-launcher>Foo</main-launcher>",
                     "<main-class>main.Class</main-class>",
                     "<signed>false</signed>",
@@ -166,6 +169,21 @@ public void testMacSign() throws IOException {
         Assert.assertTrue(aif.isSigned());
     }
 
+    @Test
+    public void testCopyAsSigned() throws IOException {
+        Map<String, Object> params = new LinkedHashMap<>();
+        params.put("name", "Foo");
+        params.put("main-class", "main.Class");
+        params.put("description", "Duck App Description");
+        params.put("mac-sign", Boolean.FALSE);
+
+        AppImageFile aif = create(params);
+        Assert.assertFalse(aif.isSigned());
+
+        aif = aif.copyAsSigned();
+        Assert.assertTrue(aif.isSigned());
+    }
+
     @Test
     public void testMacAppStore() throws IOException {
         Map<String, Object> params = new LinkedHashMap<>();
@@ -215,7 +233,10 @@ private AppImageFile create(Map<String, Object> params) throws IOException {
     private void assertInvalid(ThrowingRunnable action) {
         Exception ex = Assert.assertThrows(RuntimeException.class, action);
         Assert.assertTrue(ex instanceof RuntimeException);
-        Assert.assertTrue(ex.getMessage().contains("malformed .jpackage.xml"));
+        Assert.assertTrue(ex.getMessage()
+                .contains("generated by another jpackage version or malformed"));
+        Assert.assertTrue(ex.getMessage()
+                .endsWith(".jpackage.xml\""));
     }
 
     private AppImageFile createFromXml(String... xmlData) throws IOException {
diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java
index fb155bb02498a..e6dc7a6b49c2e 100644
--- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java
+++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java
@@ -88,7 +88,7 @@ public void test(boolean signAppImage) throws Exception {
         appImageCmd.executeAndAssertHelloAppImageCreated();
 
         // Double check if it is signed or unsigned based on signAppImage
-        verifySignature(appImageCmd, signAppImage);
+        SigningBase.verifyAppImageSignature(appImageCmd, signAppImage, "testAL");
 
         // Sign app image
         JPackageCommand cmd = new JPackageCommand();
@@ -97,24 +97,9 @@ public void test(boolean signAppImage) throws Exception {
             .addArguments("--mac-sign")
             .addArguments("--mac-signing-key-user-name", SigningBase.DEV_NAME)
             .addArguments("--mac-signing-keychain", SigningBase.KEYCHAIN);
-        cmd.execute();
+        cmd.executeAndAssertImageCreated();
 
         // Should be signed app image
-        verifySignature(appImageCmd, true);
-    }
-
-    private void verifySignature(JPackageCommand appImageCmd, boolean isSigned) throws Exception {
-        Path launcherPath = appImageCmd.appLauncherPath();
-        SigningBase.verifyCodesign(launcherPath, isSigned);
-
-        Path testALPath = launcherPath.getParent().resolve("testAL");
-        SigningBase.verifyCodesign(testALPath, isSigned);
-
-        Path appImage = appImageCmd.outputBundle();
-        SigningBase.verifyCodesign(appImage, isSigned);
-        if (isSigned) {
-            SigningBase.verifySpctl(appImage, "exec");
-        }
+        SigningBase.verifyAppImageSignature(appImageCmd, true, "testAL");
     }
 }
-
diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java
new file mode 100644
index 0000000000000..79c0660748242
--- /dev/null
+++ b/test/jdk/tools/jpackage/macosx/SigningPackageFromTwoStepAppImageTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2022, 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.internal.ApplicationLayout;
+import jdk.jpackage.test.JPackageCommand;
+import jdk.jpackage.test.TKit;
+import jdk.jpackage.test.PackageTest;
+import jdk.jpackage.test.PackageType;
+import jdk.jpackage.test.MacHelper;
+import jdk.jpackage.test.Annotations.Test;
+import jdk.jpackage.test.Annotations.Parameter;
+
+/**
+ * Tests generation of dmg and pkg from signed predefined app image which was
+ * signed using two step process (generate app image and then signed using
+ * --app-image and --mac-sign). 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 the 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.
+ * note:
+ * "jpackage.openjdk.java.net" can be over-ridden by systerm property
+ * "jpackage.mac.signing.key.user.name", and
+ * "jpackagerTest" can be over-ridden by system property
+ * "jpackage.mac.signing.keychain"
+ */
+
+/*
+ * @test
+ * @summary jpackage with --type pkg,dmg --app-image
+ * @library ../helpers
+ * @library /test/lib
+ * @library base
+ * @key jpackagePlatformPackage
+ * @build SigningBase
+ * @build SigningCheck
+ * @build jtreg.SkippedException
+ * @build jdk.jpackage.test.*
+ * @build SigningPackageFromTwoStepAppImageTest
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @requires (os.family == "mac")
+ * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main
+ *  --jpt-run=SigningPackageFromTwoStepAppImageTest
+ */
+public class SigningPackageFromTwoStepAppImageTest {
+
+    private static void verifyPKG(JPackageCommand cmd) {
+        if (!cmd.hasArgument("--mac-sign")) {
+            return; // Nothing to check if not signed
+        }
+
+        Path outputBundle = cmd.outputBundle();
+        SigningBase.verifyPkgutil(outputBundle);
+        SigningBase.verifySpctl(outputBundle, "install");
+    }
+
+    private static void verifyDMG(JPackageCommand cmd) {
+        // DMG always unsigned, so we will check it
+        Path outputBundle = cmd.outputBundle();
+        SigningBase.verifyCodesign(outputBundle, false);
+    }
+
+    private static void verifyAppImageInDMG(JPackageCommand cmd) {
+        MacHelper.withExplodedDmg(cmd, dmgImage -> {
+            // We will be called with all folders in DMG since JDK-8263155, but
+            // we only need to verify app.
+            if (dmgImage.endsWith(cmd.name() + ".app")) {
+                Path launcherPath = ApplicationLayout.platformAppImage()
+                    .resolveAt(dmgImage).launchersDirectory().resolve(cmd.name());
+                SigningBase.verifyCodesign(launcherPath, true);
+                SigningBase.verifyCodesign(dmgImage, true);
+                SigningBase.verifySpctl(dmgImage, "exec");
+            }
+        });
+    }
+
+    @Test
+    @Parameter("true")
+    @Parameter("false")
+    public static void test(boolean signAppImage) throws Exception {
+        SigningCheck.checkCertificates();
+
+        Path appimageOutput = TKit.createTempDirectory("appimage");
+
+        // Generate app image. Signed or unsigned based on test
+        // parameter. We should able to sign predfined app images
+        // which are signed or unsigned.
+        JPackageCommand appImageCmd = JPackageCommand.helloAppImage()
+                .setArgumentValue("--dest", appimageOutput);
+        if (signAppImage) {
+            appImageCmd.addArguments("--mac-sign", "--mac-signing-key-user-name",
+                    SigningBase.DEV_NAME, "--mac-signing-keychain",
+                    SigningBase.KEYCHAIN);
+        }
+
+        // Generate app image
+        appImageCmd.executeAndAssertHelloAppImageCreated();
+
+        // Double check if it is signed or unsigned based on signAppImage
+        SigningBase.verifyAppImageSignature(appImageCmd, signAppImage);
+
+        // Sign app image
+        JPackageCommand appImageSignedCmd = new JPackageCommand();
+        appImageSignedCmd.setPackageType(PackageType.IMAGE)
+            .addArguments("--app-image", appImageCmd.outputBundle().toAbsolutePath())
+            .addArguments("--mac-sign")
+            .addArguments("--mac-signing-key-user-name", SigningBase.DEV_NAME)
+            .addArguments("--mac-signing-keychain", SigningBase.KEYCHAIN);
+        appImageSignedCmd.executeAndAssertImageCreated();
+
+        // Should be signed app image
+        SigningBase.verifyAppImageSignature(appImageCmd, true);
+
+        new PackageTest()
+                .forTypes(PackageType.MAC)
+                .addInitializer(cmd -> {
+                    cmd.addArguments("--app-image", appImageCmd.outputBundle());
+                    cmd.removeArgumentWithValue("--input");
+                    if (signAppImage) {
+                        cmd.addArguments("--mac-sign",
+                                "--mac-signing-key-user-name",
+                                SigningBase.DEV_NAME,
+                                "--mac-signing-keychain",
+                                SigningBase.KEYCHAIN);
+                    }
+                })
+                .forTypes(PackageType.MAC_PKG)
+                .addBundleVerifier(
+                    SigningPackageFromTwoStepAppImageTest::verifyPKG)
+                .forTypes(PackageType.MAC_DMG)
+                .addBundleVerifier(
+                    SigningPackageFromTwoStepAppImageTest::verifyDMG)
+                .addBundleVerifier(
+                    SigningPackageFromTwoStepAppImageTest::verifyAppImageInDMG)
+                .run();
+    }
+}
diff --git a/test/jdk/tools/jpackage/macosx/base/SigningBase.java b/test/jdk/tools/jpackage/macosx/base/SigningBase.java
index 9c7f7f21851b7..3126c3f8b9925 100644
--- a/test/jdk/tools/jpackage/macosx/base/SigningBase.java
+++ b/test/jdk/tools/jpackage/macosx/base/SigningBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, 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
@@ -24,6 +24,7 @@
 import java.nio.file.Path;
 import java.util.List;
 
+import jdk.jpackage.test.JPackageCommand;
 import jdk.jpackage.test.TKit;
 import jdk.jpackage.test.Executor;
 import jdk.jpackage.test.Executor.Result;
@@ -147,4 +148,22 @@ public static void verifyPkgutil(Path target) {
         verifyPkgutilResult(result);
     }
 
+    public static void verifyAppImageSignature(JPackageCommand appImageCmd,
+            boolean isSigned, String... launchers) throws Exception {
+        Path launcherPath = appImageCmd.appLauncherPath();
+        SigningBase.verifyCodesign(launcherPath, isSigned);
+
+        final List<String> launchersList = List.of(launchers);
+        launchersList.forEach(launcher -> {
+            Path testALPath = launcherPath.getParent().resolve(launcher);
+            SigningBase.verifyCodesign(testALPath, isSigned);
+        });
+
+        Path appImage = appImageCmd.outputBundle();
+        SigningBase.verifyCodesign(appImage, isSigned);
+        if (isSigned) {
+            SigningBase.verifySpctl(appImage, "exec");
+        }
+    }
+
 }