8293462: [macos] app image signature invalid when creating DMG or PKG from post processed signed image
Reviewed-by: asemenyuk
This commit is contained in:
parent
43eff2b309
commit
1e222bccd3
@ -265,9 +265,29 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -33,6 +33,8 @@ import java.util.ArrayList;
|
||||
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 @@ import static jdk.jpackage.internal.StandardBundlerParam.APP_STORE;
|
||||
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 @@ public final class AppImageFile {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public final class AppImageFile {
|
||||
|
||||
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 @@ public final class AppImageFile {
|
||||
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 final class AppImageFile {
|
||||
return signed;
|
||||
}
|
||||
|
||||
boolean isAppStore() {
|
||||
public boolean isAppStore() {
|
||||
return appStore;
|
||||
}
|
||||
|
||||
@ -165,53 +183,118 @@ public final class AppImageFile {
|
||||
.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);
|
||||
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);
|
||||
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();
|
||||
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 final class AppImageFile {
|
||||
|
||||
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 final class AppImageFile {
|
||||
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 final class AppImageFile {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public final class AppImageFile {
|
||||
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;
|
||||
}
|
||||
|
@ -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\
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -395,10 +395,16 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
* 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 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
}
|
||||
|
||||
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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class AppImageFileTest {
|
||||
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 class AppImageFileTest {
|
||||
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 class AppImageFileTest {
|
||||
|
||||
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 class AppImageFileTest {
|
||||
|
||||
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 class AppImageFileTest {
|
||||
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 @@ public class AppImageFileTest {
|
||||
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 {
|
||||
|
@ -88,7 +88,7 @@ public class SigningAppImageTwoStepsTest {
|
||||
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 class SigningAppImageTwoStepsTest {
|
||||
.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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 class SigningBase {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user