| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.jarutils; |
| |
| import com.android.jarutils.DebugKeyProvider.IKeyGenOutput; |
| import com.android.jarutils.DebugKeyProvider.KeytoolException; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.security.KeyStoreException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.UnrecoverableEntryException; |
| import java.security.cert.CertificateException; |
| import java.util.ArrayList; |
| |
| /** |
| * A Helper to create new keystore/key. |
| */ |
| public final class KeystoreHelper { |
| |
| /** |
| * Creates a new store |
| * @param osKeyStorePath the location of the store |
| * @param storeType an optional keystore type, or <code>null</code> if the default is to |
| * be used. |
| * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr |
| * of the keytool process call. |
| * @throws KeyStoreException |
| * @throws NoSuchAlgorithmException |
| * @throws CertificateException |
| * @throws UnrecoverableEntryException |
| * @throws IOException |
| * @throws KeytoolException |
| */ |
| public static boolean createNewStore( |
| String osKeyStorePath, |
| String storeType, |
| String storePassword, |
| String alias, |
| String keyPassword, |
| String description, |
| int validityYears, |
| IKeyGenOutput output) |
| throws KeyStoreException, NoSuchAlgorithmException, CertificateException, |
| UnrecoverableEntryException, IOException, KeytoolException { |
| |
| // get the executable name of keytool depending on the platform. |
| String os = System.getProperty("os.name"); |
| |
| String keytoolCommand; |
| if (os.startsWith("Windows")) { |
| keytoolCommand = "keytool.exe"; |
| } else { |
| keytoolCommand = "keytool"; |
| } |
| |
| String javaHome = System.getProperty("java.home"); |
| |
| if (javaHome != null && javaHome.length() > 0) { |
| keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand; |
| } |
| |
| // create the command line to call key tool to build the key with no user input. |
| ArrayList<String> commandList = new ArrayList<String>(); |
| commandList.add(keytoolCommand); |
| commandList.add("-genkey"); |
| commandList.add("-alias"); |
| commandList.add(alias); |
| commandList.add("-keyalg"); |
| commandList.add("RSA"); |
| commandList.add("-dname"); |
| commandList.add(description); |
| commandList.add("-validity"); |
| commandList.add(Integer.toString(validityYears * 365)); |
| commandList.add("-keypass"); |
| commandList.add(keyPassword); |
| commandList.add("-keystore"); |
| commandList.add(osKeyStorePath); |
| commandList.add("-storepass"); |
| commandList.add(storePassword); |
| if (storeType != null) { |
| commandList.add("-storetype"); |
| commandList.add(storeType); |
| } |
| |
| String[] commandArray = commandList.toArray(new String[commandList.size()]); |
| |
| // launch the command line process |
| int result = 0; |
| try { |
| result = grabProcessOutput(Runtime.getRuntime().exec(commandArray), output); |
| } catch (Exception e) { |
| // create the command line as one string |
| StringBuilder builder = new StringBuilder(); |
| boolean firstArg = true; |
| for (String arg : commandArray) { |
| boolean hasSpace = arg.indexOf(' ') != -1; |
| |
| if (firstArg == true) { |
| firstArg = false; |
| } else { |
| builder.append(' '); |
| } |
| |
| if (hasSpace) { |
| builder.append('"'); |
| } |
| |
| builder.append(arg); |
| |
| if (hasSpace) { |
| builder.append('"'); |
| } |
| } |
| |
| throw new KeytoolException("Failed to create key: " + e.getMessage(), |
| javaHome, builder.toString()); |
| } |
| |
| if (result != 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Get the stderr/stdout outputs of a process and return when the process is done. |
| * Both <b>must</b> be read or the process will block on windows. |
| * @param process The process to get the ouput from |
| * @return the process return code. |
| * @throws InterruptedException |
| */ |
| private static int grabProcessOutput(final Process process, final IKeyGenOutput output) { |
| // read the lines as they come. if null is returned, it's |
| // because the process finished |
| Thread t1 = new Thread("") { |
| @Override |
| public void run() { |
| // create a buffer to read the stderr output |
| InputStreamReader is = new InputStreamReader(process.getErrorStream()); |
| BufferedReader errReader = new BufferedReader(is); |
| |
| try { |
| while (true) { |
| String line = errReader.readLine(); |
| if (line != null) { |
| if (output != null) { |
| output.err(line); |
| } else { |
| System.err.println(line); |
| } |
| } else { |
| break; |
| } |
| } |
| } catch (IOException e) { |
| // do nothing. |
| } |
| } |
| }; |
| |
| Thread t2 = new Thread("") { |
| @Override |
| public void run() { |
| InputStreamReader is = new InputStreamReader(process.getInputStream()); |
| BufferedReader outReader = new BufferedReader(is); |
| |
| try { |
| while (true) { |
| String line = outReader.readLine(); |
| if (line != null) { |
| if (output != null) { |
| output.out(line); |
| } else { |
| System.out.println(line); |
| } |
| } else { |
| break; |
| } |
| } |
| } catch (IOException e) { |
| // do nothing. |
| } |
| } |
| }; |
| |
| t1.start(); |
| t2.start(); |
| |
| // it looks like on windows process#waitFor() can return |
| // before the thread have filled the arrays, so we wait for both threads and the |
| // process itself. |
| try { |
| t1.join(); |
| } catch (InterruptedException e) { |
| } |
| try { |
| t2.join(); |
| } catch (InterruptedException e) { |
| } |
| |
| // get the return code from the process |
| try { |
| return process.waitFor(); |
| } catch (InterruptedException e) { |
| // since we're waiting for the output thread above, we should never actually wait |
| // on the process to end, since it'll be done by the time we call waitFor() |
| return 0; |
| } |
| } |
| } |