• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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