mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-11 19:08:23 +00:00
275 lines
13 KiB
Java
275 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2025, 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.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Stream;
|
|
import jdk.jpackage.test.Annotations.Parameter;
|
|
import jdk.jpackage.test.Annotations.ParameterSupplier;
|
|
import jdk.jpackage.test.Annotations.Test;
|
|
import jdk.jpackage.test.CannedFormattedString;
|
|
import jdk.jpackage.test.JPackageCommand;
|
|
import jdk.jpackage.test.JPackageStringBundle;
|
|
import jdk.jpackage.test.MacHelper;
|
|
import jdk.jpackage.test.MacSign;
|
|
import jdk.jpackage.test.MacSign.CertificateRequest;
|
|
import jdk.jpackage.test.MacSignVerify;
|
|
import jdk.jpackage.test.PackageType;
|
|
import jdk.jpackage.test.TKit;
|
|
|
|
/*
|
|
* @test
|
|
* @summary jpackage with --mac-sign
|
|
* @library /test/jdk/tools/jpackage/helpers
|
|
* @library base
|
|
* @build SigningBase
|
|
* @build jdk.jpackage.test.*
|
|
* @build MacSignTest
|
|
* @requires (jpackage.test.MacSignTests == "run")
|
|
* @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main
|
|
* --jpt-run=MacSignTest
|
|
* --jpt-before-run=SigningBase.verifySignTestEnvReady
|
|
*/
|
|
public class MacSignTest {
|
|
|
|
@Test
|
|
public static void testAppContentWarning() throws IOException {
|
|
|
|
// Create app content directory with the name known to fail signing.
|
|
// This will trigger jpackage exit with status code "1".
|
|
final var appContent = TKit.createTempDirectory("app-content").resolve("foo.1");
|
|
Files.createDirectory(appContent);
|
|
Files.createFile(appContent.resolve("file"));
|
|
|
|
final List<CannedFormattedString> expectedStrings = new ArrayList<>();
|
|
expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.app.content"));
|
|
|
|
expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign"));
|
|
|
|
final var xcodeWarning = JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.xcode.tools");
|
|
if (!MacHelper.isXcodeDevToolsInstalled()) {
|
|
expectedStrings.add(xcodeWarning);
|
|
}
|
|
|
|
MacSign.withKeychain(keychain -> {
|
|
// --app-content and --type app-image
|
|
// Expect `message.codesign.failed.reason.app.content` message in the log.
|
|
// This is not a fatal error, just a warning.
|
|
// To make jpackage fail, specify bad additional content.
|
|
final var cmd = JPackageCommand.helloAppImage()
|
|
.ignoreDefaultVerbose(true)
|
|
.validateOutput(expectedStrings.toArray(CannedFormattedString[]::new))
|
|
.addArguments("--app-content", appContent)
|
|
.addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name());
|
|
|
|
if (MacHelper.isXcodeDevToolsInstalled()) {
|
|
// Check there is no warning about missing xcode command line developer tools.
|
|
cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate());
|
|
}
|
|
|
|
MacHelper.useKeychain(cmd, keychain).execute(1);
|
|
}, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain());
|
|
}
|
|
|
|
@Test
|
|
public static void testCodesignUnspecifiedFailure() throws IOException {
|
|
|
|
var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime();
|
|
|
|
appImageCmd.executeIgnoreExitCode().assertExitCodeIsZero();
|
|
|
|
// This test expects jpackage to respond in a specific way on a codesign failure.
|
|
// The simplest option to trigger codesign failure is to request the signing of an invalid bundle.
|
|
// Create app content directory with the name known to fail signing.
|
|
final var appContent = appImageCmd.appLayout().contentDirectory().resolve("foo.1");
|
|
Files.createDirectory(appContent);
|
|
Files.createFile(appContent.resolve("file"));
|
|
|
|
final List<CannedFormattedString> expectedStrings = new ArrayList<>();
|
|
expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign"));
|
|
|
|
MacSign.withKeychain(keychain -> {
|
|
final var cmd = new JPackageCommand().setPackageType(PackageType.IMAGE)
|
|
.ignoreDefaultVerbose(true)
|
|
.validateOutput(expectedStrings.toArray(CannedFormattedString[]::new))
|
|
.addArguments("--app-image", appImageCmd.outputBundle())
|
|
.addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name());
|
|
|
|
MacHelper.useKeychain(cmd, keychain).execute(1);
|
|
}, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain());
|
|
}
|
|
|
|
@Test
|
|
@Parameter({"IMAGE", "EXPIRED_SIGNING_KEY_USER_NAME"})
|
|
@Parameter({"MAC_DMG", "EXPIRED_SIGNING_KEY_USER_NAME"})
|
|
@Parameter({"MAC_PKG", "EXPIRED_SIGNING_KEY_USER_NAME", "EXPIRED_SIGNING_KEY_USER_NAME_PKG"})
|
|
|
|
@Parameter({"IMAGE", "EXPIRED_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_DMG", "EXPIRED_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_PKG", "EXPIRED_SIGN_IDENTITY"})
|
|
|
|
@Parameter({"IMAGE", "EXPIRED_CODESIGN_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_DMG", "EXPIRED_CODESIGN_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_PKG", "EXPIRED_CODESIGN_SIGN_IDENTITY"})
|
|
|
|
@Parameter({"MAC_PKG", "GOOD_CODESIGN_SIGN_IDENTITY", "EXPIRED_PKG_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_PKG", "EXPIRED_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"})
|
|
public static void testExpiredCertificate(PackageType type, SignOption... options) {
|
|
|
|
MacSign.withKeychain(keychain -> {
|
|
final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain)
|
|
.ignoreDefaultVerbose(true)
|
|
.addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList())
|
|
.setPackageType(type);
|
|
|
|
SignOption.configureOutputValidation(cmd, Stream.of(options).filter(SignOption::expired).toList(), opt -> {
|
|
return JPackageStringBundle.MAIN.cannedFormattedString("error.certificate.expired", opt.identityName());
|
|
}).execute(1);
|
|
}, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.EXPIRED.keychain());
|
|
}
|
|
|
|
@Test
|
|
// Case "--mac-signing-key-user-name": jpackage selects first certificate
|
|
// found with warning message. Certificate hash is pass to "codesign" in this
|
|
// case.
|
|
@Parameter({"IMAGE", "0", "GOOD_SIGNING_KEY_USER_NAME"})
|
|
@Parameter({"MAC_DMG", "0", "GOOD_SIGNING_KEY_USER_NAME"})
|
|
@Parameter({"MAC_PKG", "0", "GOOD_SIGNING_KEY_USER_NAME_PKG", "GOOD_SIGNING_KEY_USER_NAME"})
|
|
|
|
// Case "--mac-app-image-sign-identity": sign identity will be pass to
|
|
// "codesign" and "codesign" should fail due to multiple certificates with
|
|
// same common name found.
|
|
@Parameter({"IMAGE", "1", "GOOD_CODESIGN_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_PKG", "1", "GOOD_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"})
|
|
@Parameter({"MAC_PKG", "1", "GOOD_PKG_SIGN_IDENTITY"})
|
|
public static void testMultipleCertificates(PackageType type, int jpackageExitCode, SignOption... options) {
|
|
|
|
MacSign.withKeychain(keychain -> {
|
|
final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain)
|
|
.ignoreDefaultVerbose(true)
|
|
.addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList())
|
|
.setPackageType(type);
|
|
|
|
SignOption.configureOutputValidation(cmd, List.of(options), opt -> {
|
|
return JPackageStringBundle.MAIN.cannedFormattedString("error.multiple.certs.found", opt.identityName(), keychain.name());
|
|
}).execute(jpackageExitCode);
|
|
}, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.DUPLICATE.keychain());
|
|
}
|
|
|
|
@Test
|
|
@ParameterSupplier
|
|
@ParameterSupplier("testSelectSigningIdentity_JDK_8371094")
|
|
public static void testSelectSigningIdentity(String signingKeyUserName, CertificateRequest certRequest) {
|
|
|
|
MacSign.withKeychain(keychain -> {
|
|
final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain)
|
|
.setFakeRuntime()
|
|
.addArguments("--mac-signing-key-user-name", signingKeyUserName);
|
|
|
|
cmd.executeAndAssertHelloAppImageCreated();
|
|
|
|
MacSignVerify.assertSigned(cmd.outputBundle(), certRequest);
|
|
}, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain());
|
|
}
|
|
|
|
public static Collection<Object[]> testSelectSigningIdentity() {
|
|
return Stream.of(
|
|
SigningBase.StandardCertificateRequest.CODESIGN,
|
|
SigningBase.StandardCertificateRequest.CODESIGN_UNICODE
|
|
).map(SigningBase.StandardCertificateRequest::spec).<Object[]>mapMulti((certRequest, acc) -> {
|
|
acc.accept(new Object[] {certRequest.shortName(), certRequest});
|
|
acc.accept(new Object[] {certRequest.name(), certRequest});
|
|
}).toList();
|
|
}
|
|
|
|
public static Collection<Object[]> testSelectSigningIdentity_JDK_8371094() {
|
|
return List.<Object[]>of(new Object[] {
|
|
"ACME Technologies Limited", SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD.spec()
|
|
});
|
|
}
|
|
|
|
enum SignOption {
|
|
EXPIRED_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), true, false),
|
|
EXPIRED_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), true, false),
|
|
EXPIRED_SIGN_IDENTITY("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, false),
|
|
EXPIRED_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, true),
|
|
EXPIRED_PKG_SIGN_IDENTITY("--mac-installer-sign-identity", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), false, true),
|
|
GOOD_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN.spec(), true, false),
|
|
GOOD_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG.spec(), true, false),
|
|
GOOD_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec(), false, true),
|
|
GOOD_PKG_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.PKG.spec(), false, true);
|
|
|
|
SignOption(String option, MacSign.CertificateRequest cert, boolean shortName, boolean passThrough) {
|
|
this.option = Objects.requireNonNull(option);
|
|
this.cert = Objects.requireNonNull(cert);
|
|
this.shortName = shortName;
|
|
this.passThrough = passThrough;
|
|
}
|
|
|
|
boolean passThrough() {
|
|
return passThrough;
|
|
}
|
|
|
|
boolean expired() {
|
|
return cert.expired();
|
|
}
|
|
|
|
String identityName() {
|
|
return cert.name();
|
|
}
|
|
|
|
List<String> args() {
|
|
return List.of(option, shortName ? cert.shortName() : cert.name());
|
|
}
|
|
|
|
static JPackageCommand configureOutputValidation(JPackageCommand cmd, List<SignOption> options,
|
|
Function<SignOption, CannedFormattedString> conv) {
|
|
options.stream().filter(SignOption::passThrough)
|
|
.map(conv)
|
|
.map(CannedFormattedString::getValue)
|
|
.map(TKit::assertTextStream)
|
|
.map(TKit.TextStreamVerifier::negate)
|
|
.forEach(cmd::validateOutput);
|
|
|
|
options.stream().filter(Predicate.not(SignOption::passThrough))
|
|
.map(conv)
|
|
.map(CannedFormattedString::getValue)
|
|
.map(TKit::assertTextStream)
|
|
.forEach(cmd::validateOutput);
|
|
|
|
return cmd;
|
|
}
|
|
|
|
private final String option;
|
|
private final MacSign.CertificateRequest cert;
|
|
private final boolean shortName;
|
|
private final boolean passThrough;
|
|
}
|
|
}
|