/*
* Copyright (c) 2016, 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.
*/
package jdk.test.lib;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
/**
* Run security tools (including jarsigner and keytool) in a new process.
* The en_US locale is always used so a test can always match output to
* English text. An argument can be a normal string,
* {@code -Jvm-options}, or {@code $sysProp}.
*/
public class SecurityTools {
/**
* The response file name for keytool. Use {@link #setResponse} to
* create one. Do NOT manipulate it directly.
*/
public static final String RESPONSE_FILE = "security_tools_response.txt";
private SecurityTools() {}
public static ProcessBuilder getProcessBuilder(String tool, List<String> args) {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK(tool)
.addVMArg("-Duser.language=en")
.addVMArg("-Duser.country=US");
for (String arg : args) {
if (arg.startsWith("-J")) {
launcher.addVMArg(arg.substring(2));
} else if (Platform.isWindows() && arg.isEmpty()) {
// JDK-6518827: special handling for empty argument on Windows
launcher.addToolArg("\"\"");
} else if (arg.length() > 1 && arg.charAt(0) == '$') {
launcher.addToolArg(System.getProperty(arg.substring(1)));
} else {
launcher.addToolArg(arg);
}
}
return new ProcessBuilder(launcher.getCommand());
}
/**
* Runs keytool.
*
* @param args arguments to keytool
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer keytool(List<String> args)
throws Exception {
ProcessBuilder pb = getProcessBuilder("keytool", args);
Path p = Paths.get(RESPONSE_FILE);
if (!Files.exists(p)) {
Files.createFile(p);
}
pb.redirectInput(ProcessBuilder.Redirect.from(new File(RESPONSE_FILE)));
try {
return execute(pb);
} finally {
Files.delete(p);
}
}
/**
* Runs keytool.
*
* @param args arguments to keytool in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer keytool(String args) throws Exception {
return keytool(makeList(args));
}
/**
* Runs keytool.
*
* @param args arguments to keytool
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer keytool(String... args) throws Exception {
return keytool(List.of(args));
}
/**
* Sets the responses (user input) for keytool.
* <p>
* For example, if keytool requires entering a password twice, call
* {@code setResponse("password", "password")}. Do NOT append a newline
* character to each response. If there are useless responses at the end,
* they will be discarded silently. If there are less responses than
* necessary, keytool will read EOF. The responses will be written into
* {@linkplain #RESPONSE_FILE a local file} and will only be used by the
* next keytool run. After the run, the file is removed. Calling this
* method will always overwrite the previous response file (if exists).
*
* @param responses response to keytool
* @throws IOException if there is an error
*/
public static void setResponse(String... responses) throws IOException {
String text;
if (responses.length > 0) {
text = Stream.of(responses).collect(
Collectors.joining("\n", "", "\n"));
} else {
text = "";
}
Files.write(Paths.get(RESPONSE_FILE), text.getBytes());
}
/**
* Runs jarsigner.
*
* @param args arguments to jarsigner
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer jarsigner(List<String> args)
throws Exception {
return execute(getProcessBuilder("jarsigner", args));
}
private static OutputAnalyzer execute(ProcessBuilder pb) throws Exception {
try {
OutputAnalyzer oa = ProcessTools.executeCommand(pb);
System.out.println("Exit value: " + oa.getExitValue());
return oa;
} catch (Throwable t) {
if (t instanceof Exception) {
throw (Exception) t;
} else {
throw new Exception(t);
}
}
}
/**
* Runs jarsigner.
*
* @param args arguments to jarsigner in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer jarsigner(String args) throws Exception {
return jarsigner(makeList(args));
}
/**
* Runs jarsigner.
*
* @param args arguments to jarsigner
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer jarsigner(String... args) throws Exception {
return jarsigner(List.of(args));
}
/**
* Runs ktab.
*
* @param args arguments to ktab in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer ktab(String args) throws Exception {
return execute(getProcessBuilder("ktab", makeList(args)));
}
/**
* Runs klist.
*
* @param args arguments to klist in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer klist(String args) throws Exception {
return execute(getProcessBuilder("klist", makeList(args)));
}
/**
* Runs kinit.
*
* @param args arguments to kinit in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer kinit(String args) throws Exception {
return execute(getProcessBuilder("kinit", makeList(args)));
}
/**
* Runs jar.
*
* @param args arguments to jar in a single string. The string is
* converted to be List with makeList.
* @return an {@link OutputAnalyzer} object
* @throws Exception if there is an error
*/
public static OutputAnalyzer jar(String args) throws Exception {
return execute(getProcessBuilder("jar", makeList(args)));
}
/**
* Split a line to a list of string. All whitespaces are treated as
* delimiters unless quoted between ` and `.
*
* @param line the input
* @return the list
*/
public static List<String> makeList(String line) {
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder();
boolean inBackTick = false;
for (char c : line.toCharArray()) {
if (inBackTick) {
if (c == '`') {
result.add(sb.toString());
sb.setLength(0);
inBackTick = false;
} else {
sb.append(c);
}
} else {
if (sb.length() == 0 && c == '`') {
// Allow ` inside a string
inBackTick = true;
} else {
if (Character.isWhitespace(c)) {
if (sb.length() != 0) {
result.add(sb.toString());
sb.setLength(0);
}
} else {
sb.append(c);
}
}
}
}
if (sb.length() != 0) {
result.add(sb.toString());
}
return result;
}
// Create a temporary keychain in macOS and use it. The original
// keychains will be restored when the object is closed.
public static class TemporaryKeychain implements Closeable {
// name of new keychain
private final String newChain;
// names of the original keychains
private final List<String> oldChains;
public TemporaryKeychain(String name) {
Path p = Path.of(name + ".keychain-db");
newChain = p.toAbsolutePath().toString();
try {
oldChains = ProcessTools.executeProcess("security", "list-keychains")
.shouldHaveExitValue(0)
.getStdout()
.lines()
.map(String::trim)
.map(x -> x.startsWith("\"") ? x.substring(1, x.length() - 1) : x)
.collect(Collectors.toList());
if (!Files.exists(p)) {
ProcessTools.executeProcess("security", "create-keychain", "-p", "changeit", newChain)
.shouldHaveExitValue(0);
}
ProcessTools.executeProcess("security", "unlock-keychain", "-p", "changeit", newChain)
.shouldHaveExitValue(0);
ProcessTools.executeProcess("security", "list-keychains", "-s", newChain)
.shouldHaveExitValue(0);
} catch (Throwable t) {
if (t instanceof RuntimeException re) {
throw re;
} else {
throw new RuntimeException(t);
}
}
}
public String chain() {
return newChain;
}
@Override
public void close() throws IOException {
List<String> cmds = new ArrayList<>();
cmds.addAll(List.of("security", "list-keychains", "-s"));
cmds.addAll(oldChains);
try {
ProcessTools.executeProcess(cmds.toArray(new String[0]))
.shouldHaveExitValue(0);
} catch (Throwable t) {
if (t instanceof RuntimeException re) {
throw re;
} else {
throw new RuntimeException(t);
}
}
}
}
}
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|