1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.jarutils; 18 19 import com.android.jarutils.DebugKeyProvider.IKeyGenOutput; 20 import com.android.jarutils.DebugKeyProvider.KeytoolException; 21 22 import java.io.BufferedReader; 23 import java.io.File; 24 import java.io.IOException; 25 import java.io.InputStreamReader; 26 import java.security.KeyStoreException; 27 import java.security.NoSuchAlgorithmException; 28 import java.security.UnrecoverableEntryException; 29 import java.security.cert.CertificateException; 30 import java.util.ArrayList; 31 32 /** 33 * A Helper to create new keystore/key. 34 */ 35 public final class KeystoreHelper { 36 37 /** 38 * Creates a new store 39 * @param osKeyStorePath the location of the store 40 * @param storeType an optional keystore type, or <code>null</code> if the default is to 41 * be used. 42 * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr 43 * of the keytool process call. 44 * @throws KeyStoreException 45 * @throws NoSuchAlgorithmException 46 * @throws CertificateException 47 * @throws UnrecoverableEntryException 48 * @throws IOException 49 * @throws KeytoolException 50 */ createNewStore( String osKeyStorePath, String storeType, String storePassword, String alias, String keyPassword, String description, int validityYears, IKeyGenOutput output)51 public static boolean createNewStore( 52 String osKeyStorePath, 53 String storeType, 54 String storePassword, 55 String alias, 56 String keyPassword, 57 String description, 58 int validityYears, 59 IKeyGenOutput output) 60 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, 61 UnrecoverableEntryException, IOException, KeytoolException { 62 63 // get the executable name of keytool depending on the platform. 64 String os = System.getProperty("os.name"); 65 66 String keytoolCommand; 67 if (os.startsWith("Windows")) { 68 keytoolCommand = "keytool.exe"; 69 } else { 70 keytoolCommand = "keytool"; 71 } 72 73 String javaHome = System.getProperty("java.home"); 74 75 if (javaHome != null && javaHome.length() > 0) { 76 keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand; 77 } 78 79 // create the command line to call key tool to build the key with no user input. 80 ArrayList<String> commandList = new ArrayList<String>(); 81 commandList.add(keytoolCommand); 82 commandList.add("-genkey"); 83 commandList.add("-alias"); 84 commandList.add(alias); 85 commandList.add("-keyalg"); 86 commandList.add("RSA"); 87 commandList.add("-dname"); 88 commandList.add(description); 89 commandList.add("-validity"); 90 commandList.add(Integer.toString(validityYears * 365)); 91 commandList.add("-keypass"); 92 commandList.add(keyPassword); 93 commandList.add("-keystore"); 94 commandList.add(osKeyStorePath); 95 commandList.add("-storepass"); 96 commandList.add(storePassword); 97 if (storeType != null) { 98 commandList.add("-storetype"); 99 commandList.add(storeType); 100 } 101 102 String[] commandArray = commandList.toArray(new String[commandList.size()]); 103 104 // launch the command line process 105 int result = 0; 106 try { 107 result = grabProcessOutput(Runtime.getRuntime().exec(commandArray), output); 108 } catch (Exception e) { 109 // create the command line as one string 110 StringBuilder builder = new StringBuilder(); 111 boolean firstArg = true; 112 for (String arg : commandArray) { 113 boolean hasSpace = arg.indexOf(' ') != -1; 114 115 if (firstArg == true) { 116 firstArg = false; 117 } else { 118 builder.append(' '); 119 } 120 121 if (hasSpace) { 122 builder.append('"'); 123 } 124 125 builder.append(arg); 126 127 if (hasSpace) { 128 builder.append('"'); 129 } 130 } 131 132 throw new KeytoolException("Failed to create key: " + e.getMessage(), 133 javaHome, builder.toString()); 134 } 135 136 if (result != 0) { 137 return false; 138 } 139 140 return true; 141 } 142 143 /** 144 * Get the stderr/stdout outputs of a process and return when the process is done. 145 * Both <b>must</b> be read or the process will block on windows. 146 * @param process The process to get the ouput from 147 * @return the process return code. 148 * @throws InterruptedException 149 */ grabProcessOutput(final Process process, final IKeyGenOutput output)150 private static int grabProcessOutput(final Process process, final IKeyGenOutput output) { 151 // read the lines as they come. if null is returned, it's 152 // because the process finished 153 Thread t1 = new Thread("") { 154 @Override 155 public void run() { 156 // create a buffer to read the stderr output 157 InputStreamReader is = new InputStreamReader(process.getErrorStream()); 158 BufferedReader errReader = new BufferedReader(is); 159 160 try { 161 while (true) { 162 String line = errReader.readLine(); 163 if (line != null) { 164 if (output != null) { 165 output.err(line); 166 } else { 167 System.err.println(line); 168 } 169 } else { 170 break; 171 } 172 } 173 } catch (IOException e) { 174 // do nothing. 175 } 176 } 177 }; 178 179 Thread t2 = new Thread("") { 180 @Override 181 public void run() { 182 InputStreamReader is = new InputStreamReader(process.getInputStream()); 183 BufferedReader outReader = new BufferedReader(is); 184 185 try { 186 while (true) { 187 String line = outReader.readLine(); 188 if (line != null) { 189 if (output != null) { 190 output.out(line); 191 } else { 192 System.out.println(line); 193 } 194 } else { 195 break; 196 } 197 } 198 } catch (IOException e) { 199 // do nothing. 200 } 201 } 202 }; 203 204 t1.start(); 205 t2.start(); 206 207 // it looks like on windows process#waitFor() can return 208 // before the thread have filled the arrays, so we wait for both threads and the 209 // process itself. 210 try { 211 t1.join(); 212 } catch (InterruptedException e) { 213 } 214 try { 215 t2.join(); 216 } catch (InterruptedException e) { 217 } 218 219 // get the return code from the process 220 try { 221 return process.waitFor(); 222 } catch (InterruptedException e) { 223 // since we're waiting for the output thread above, we should never actually wait 224 // on the process to end, since it'll be done by the time we call waitFor() 225 return 0; 226 } 227 } 228 } 229