1/*
2 * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8047305 8075618
27 * @summary Tests jarsigner tool and JarSigner API work with multi-release JAR files.
28 * @library /test/lib
29 * @build jdk.test.lib.compiler.CompilerUtils
30 *        jdk.test.lib.Utils
31 *        jdk.test.lib.Asserts
32 *        jdk.test.lib.JDKToolFinder
33 *        jdk.test.lib.JDKToolLauncher
34 *        jdk.test.lib.Platform
35 *        jdk.test.lib.process.*
36 * @run main MVJarSigningTest
37 */
38
39import jdk.security.jarsigner.JarSigner;
40
41import java.io.BufferedReader;
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.FileOutputStream;
45import java.io.IOException;
46import java.io.InputStreamReader;
47import java.nio.file.Files;
48import java.nio.file.Path;
49import java.nio.file.Paths;
50import java.security.KeyStore;
51import java.security.PrivateKey;
52import java.security.cert.Certificate;
53import java.security.cert.CertificateFactory;
54import java.util.ArrayList;
55import java.util.Arrays;
56import java.util.Collections;
57import java.util.List;
58import java.util.concurrent.TimeUnit;
59import java.util.jar.JarFile;
60import java.util.stream.Stream;
61import java.util.zip.ZipEntry;
62import java.util.zip.ZipFile;
63import java.util.zip.ZipOutputStream;
64
65import jdk.test.lib.JDKToolFinder;
66import jdk.test.lib.JDKToolLauncher;
67import jdk.test.lib.Utils;
68import jdk.test.lib.compiler.CompilerUtils;
69import jdk.test.lib.process.OutputAnalyzer;
70import jdk.test.lib.process.ProcessTools;
71
72
73public class MVJarSigningTest {
74
75    private static final String TEST_SRC = System.getProperty("test.src", ".");
76    private static final String USR_DIR = System.getProperty("user.dir", ".");
77    private static final String JAR_NAME = "MV.jar";
78    private static final String KEYSTORE = "keystore.jks";
79    private static final String ALIAS = "JavaTest";
80    private static final String STOREPASS = "changeit";
81    private static final String KEYPASS = "changeit";
82    private static final String SIGNED_JAR = "Signed.jar";
83    private static final String POLICY_FILE = "SignedJar.policy";
84    private static final String VERSION = "" + Runtime.version().major();
85    private static final String VERSION_MESSAGE = "I am running on version " + VERSION;
86
87    public static void main(String[] args) throws Throwable {
88        // compile java files in jarContent directory
89        compile("jarContent");
90
91        // create multi-release jar
92        Path classes = Paths.get("classes");
93        jar("cf", JAR_NAME, "-C", classes.resolve("base").toString(), ".",
94                "--release", "9", "-C", classes.resolve("v9").toString(), ".",
95                "--release", "10", "-C", classes.resolve("v10").toString(), ".")
96            .shouldHaveExitValue(0);
97
98        genKey();
99        signJar(JAR_NAME)
100            .shouldHaveExitValue(0)
101            .shouldMatch("signing.*META-INF/versions/9/version/Version.class")
102            .shouldMatch("signing.*META-INF/versions/10/version/Version.class")
103            .shouldMatch("signing.*version/Main.class")
104            .shouldMatch("signing.*version/Version.class");
105        verify(SIGNED_JAR);
106
107        // test with JarSigner API
108        Files.deleteIfExists(Paths.get(SIGNED_JAR));
109        signWithJarSignerAPI(JAR_NAME);
110        verify(SIGNED_JAR);
111
112        // test Permission granted
113        File keypass = new File("keypass");
114        try (FileOutputStream fos = new FileOutputStream(keypass)) {
115            fos.write(KEYPASS.getBytes());
116        }
117        String[] cmd = {
118                "-classpath", SIGNED_JAR,
119                "-Djava.security.manager",
120                "-Djava.security.policy=" +
121                TEST_SRC + File.separator + POLICY_FILE,
122                "version.Main"};
123        ProcessTools.executeTestJvm(cmd)
124            .shouldHaveExitValue(0)
125            .shouldContain(VERSION_MESSAGE);
126    }
127
128    private static void compile (String jarContent_path) throws Throwable {
129        Path classes = Paths.get(USR_DIR, "classes", "base");
130        Path source = Paths.get(TEST_SRC, jarContent_path, "base", "version");
131        CompilerUtils.compile(source, classes);
132
133        classes = Paths.get(USR_DIR, "classes", "v9");
134        source = Paths.get(TEST_SRC, jarContent_path , "v9", "version");
135        CompilerUtils.compile(source, classes);
136
137        classes = Paths.get(USR_DIR, "classes", "v10");
138        source = Paths.get(TEST_SRC, jarContent_path, "v10", "version");
139        CompilerUtils.compile(source, classes);
140    }
141
142    private static OutputAnalyzer jar(String...args) throws Throwable {
143        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jar");
144        Stream.of(args).forEach(launcher::addToolArg);
145        return ProcessTools.executeCommand(launcher.getCommand());
146    }
147
148    private static void genKey() throws Throwable {
149        String keytool = JDKToolFinder.getJDKTool("keytool");
150        Files.deleteIfExists(Paths.get(KEYSTORE));
151        ProcessTools.executeCommand(keytool,
152                "-J-Duser.language=en",
153                "-J-Duser.country=US",
154                "-genkey",
155                "-alias", ALIAS,
156                "-keystore", KEYSTORE,
157                "-keypass", KEYPASS,
158                "-dname", "cn=sample",
159                "-storepass", STOREPASS
160        ).shouldHaveExitValue(0);
161    }
162
163    private static OutputAnalyzer signJar(String jarName) throws Throwable {
164        List<String> args = new ArrayList<>();
165        args.add("-verbose");
166        args.add("-signedjar");
167        args.add(SIGNED_JAR);
168        args.add(jarName);
169        args.add(ALIAS);
170
171        return jarsigner(args);
172    }
173
174    private static void verify(String signedJarName) throws Throwable {
175        verifyJar(signedJarName)
176            .shouldHaveExitValue(0)
177            .shouldContain("jar verified")
178            .shouldMatch("smk.*META-INF/versions/9/version/Version.class")
179            .shouldMatch("smk.*META-INF/versions/10/version/Version.class")
180            .shouldMatch("smk.*version/Main.class")
181            .shouldMatch("smk.*version/Version.class");
182    }
183
184    private static OutputAnalyzer verifyJar(String signedJarName) throws Throwable {
185        List<String> args = new ArrayList<>();
186        args.add("-verbose");
187        args.add("-verify");
188        args.add(signedJarName);
189
190        return jarsigner(args);
191    }
192
193    private static OutputAnalyzer jarsigner(List<String> extra)
194            throws Throwable {
195        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
196                .addVMArg("-Duser.language=en")
197                .addVMArg("-Duser.country=US")
198                .addToolArg("-keystore")
199                .addToolArg(KEYSTORE)
200                .addToolArg("-storepass")
201                .addToolArg(STOREPASS)
202                .addToolArg("-keypass")
203                .addToolArg(KEYPASS);
204        for (String s : extra) {
205            if (s.startsWith("-J")) {
206                launcher.addVMArg(s.substring(2));
207            } else {
208                launcher.addToolArg(s);
209            }
210        }
211        return ProcessTools.executeCommand(launcher.getCommand());
212    }
213
214    private static void signWithJarSignerAPI(String jarName)
215            throws Throwable {
216        // Get JarSigner
217        try (FileInputStream fis = new FileInputStream(KEYSTORE)) {
218                KeyStore ks = KeyStore.getInstance("JKS");
219                ks.load(fis, STOREPASS.toCharArray());
220                PrivateKey pk = (PrivateKey)ks.getKey(ALIAS, KEYPASS.toCharArray());
221                Certificate cert = ks.getCertificate(ALIAS);
222                JarSigner signer = new JarSigner.Builder(pk,
223                        CertificateFactory.getInstance("X.509").generateCertPath(
224                                Collections.singletonList(cert)))
225                        .build();
226            // Sign jar
227            try (ZipFile src = new JarFile(jarName);
228                    FileOutputStream out = new FileOutputStream(SIGNED_JAR)) {
229                signer.sign(src,out);
230            }
231        }
232    }
233
234}
235
236
237