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.sdklib.internal.build; 18 19 import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; 20 import com.android.sdklib.internal.build.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 */ grabProcessOutput(final Process process, final IKeyGenOutput output)149 private static int grabProcessOutput(final Process process, final IKeyGenOutput output) { 150 // read the lines as they come. if null is returned, it's 151 // because the process finished 152 Thread t1 = new Thread("") { 153 @Override 154 public void run() { 155 // create a buffer to read the stderr output 156 InputStreamReader is = new InputStreamReader(process.getErrorStream()); 157 BufferedReader errReader = new BufferedReader(is); 158 159 try { 160 while (true) { 161 String line = errReader.readLine(); 162 if (line != null) { 163 if (output != null) { 164 output.err(line); 165 } else { 166 System.err.println(line); 167 } 168 } else { 169 break; 170 } 171 } 172 } catch (IOException e) { 173 // do nothing. 174 } 175 } 176 }; 177 178 Thread t2 = new Thread("") { 179 @Override 180 public void run() { 181 InputStreamReader is = new InputStreamReader(process.getInputStream()); 182 BufferedReader outReader = new BufferedReader(is); 183 184 try { 185 while (true) { 186 String line = outReader.readLine(); 187 if (line != null) { 188 if (output != null) { 189 output.out(line); 190 } else { 191 System.out.println(line); 192 } 193 } else { 194 break; 195 } 196 } 197 } catch (IOException e) { 198 // do nothing. 199 } 200 } 201 }; 202 203 t1.start(); 204 t2.start(); 205 206 // it looks like on windows process#waitFor() can return 207 // before the thread have filled the arrays, so we wait for both threads and the 208 // process itself. 209 try { 210 t1.join(); 211 } catch (InterruptedException e) { 212 } 213 try { 214 t2.join(); 215 } catch (InterruptedException e) { 216 } 217 218 // get the return code from the process 219 try { 220 return process.waitFor(); 221 } catch (InterruptedException e) { 222 // since we're waiting for the output thread above, we should never actually wait 223 // on the process to end, since it'll be done by the time we call waitFor() 224 return 0; 225 } 226 } 227 } 228