|
1 /* |
|
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package sun.security.tools; |
|
27 |
|
28 import java.io.*; |
|
29 import java.math.BigInteger; |
|
30 import java.security.GeneralSecurityException; |
|
31 import java.security.InvalidParameterException; |
|
32 import java.security.KeyStore; |
|
33 import java.security.KeyStoreException; |
|
34 import java.security.MessageDigest; |
|
35 import java.security.NoSuchAlgorithmException; |
|
36 import java.security.Key; |
|
37 import java.security.PublicKey; |
|
38 import java.security.PrivateKey; |
|
39 import java.security.Security; |
|
40 import java.security.Signature; |
|
41 import java.security.SignatureException; |
|
42 import java.security.UnrecoverableEntryException; |
|
43 import java.security.UnrecoverableKeyException; |
|
44 import java.security.Principal; |
|
45 import java.security.Provider; |
|
46 import java.security.Identity; |
|
47 import java.security.Signer; |
|
48 import java.security.cert.Certificate; |
|
49 import java.security.cert.CertificateFactory; |
|
50 import java.security.cert.X509Certificate; |
|
51 import java.security.cert.CertificateException; |
|
52 import java.security.interfaces.DSAParams; |
|
53 import java.security.interfaces.DSAPrivateKey; |
|
54 import java.security.interfaces.DSAPublicKey; |
|
55 import java.security.interfaces.RSAPrivateCrtKey; |
|
56 import java.security.interfaces.RSAPrivateKey; |
|
57 import java.security.interfaces.RSAPublicKey; |
|
58 import java.text.Collator; |
|
59 import java.text.MessageFormat; |
|
60 import java.util.*; |
|
61 import java.lang.reflect.Constructor; |
|
62 import java.net.URL; |
|
63 import java.net.URLClassLoader; |
|
64 |
|
65 import sun.misc.BASE64Decoder; |
|
66 import sun.misc.BASE64Encoder; |
|
67 import sun.security.util.ObjectIdentifier; |
|
68 import sun.security.pkcs.PKCS10; |
|
69 import sun.security.provider.IdentityDatabase; |
|
70 import sun.security.provider.SystemSigner; |
|
71 import sun.security.provider.SystemIdentity; |
|
72 import sun.security.provider.X509Factory; |
|
73 import sun.security.util.DerOutputStream; |
|
74 import sun.security.util.Password; |
|
75 import sun.security.util.Resources; |
|
76 import sun.security.util.PathList; |
|
77 import javax.crypto.KeyGenerator; |
|
78 import javax.crypto.SecretKey; |
|
79 |
|
80 import sun.security.x509.*; |
|
81 |
|
82 import static java.security.KeyStore.*; |
|
83 |
|
84 /** |
|
85 * This tool manages keystores. |
|
86 * |
|
87 * @author Jan Luehe |
|
88 * |
|
89 * |
|
90 * @see java.security.KeyStore |
|
91 * @see sun.security.provider.KeyProtector |
|
92 * @see sun.security.provider.JavaKeyStore |
|
93 * |
|
94 * @since 1.2 |
|
95 */ |
|
96 |
|
97 public final class KeyTool { |
|
98 |
|
99 private boolean debug = false; |
|
100 private int command = -1; |
|
101 private String sigAlgName = null; |
|
102 private String keyAlgName = null; |
|
103 private boolean verbose = false; |
|
104 private int keysize = -1; |
|
105 private boolean rfc = false; |
|
106 private long validity = (long)90; |
|
107 private String alias = null; |
|
108 private String dname = null; |
|
109 private String dest = null; |
|
110 private String filename = null; |
|
111 private String srcksfname = null; |
|
112 |
|
113 // User-specified providers are added before any command is called. |
|
114 // However, they are not removed before the end of the main() method. |
|
115 // If you're calling KeyTool.main() directly in your own Java program, |
|
116 // please programtically add any providers you need and do not specify |
|
117 // them through the command line. |
|
118 |
|
119 private Set<Pair <String, String>> providers = null; |
|
120 private String storetype = null; |
|
121 private String srcProviderName = null; |
|
122 private String providerName = null; |
|
123 private String pathlist = null; |
|
124 private char[] storePass = null; |
|
125 private char[] storePassNew = null; |
|
126 private char[] keyPass = null; |
|
127 private char[] keyPassNew = null; |
|
128 private char[] oldPass = null; |
|
129 private char[] newPass = null; |
|
130 private char[] destKeyPass = null; |
|
131 private char[] srckeyPass = null; |
|
132 private String ksfname = null; |
|
133 private File ksfile = null; |
|
134 private InputStream ksStream = null; // keystore stream |
|
135 private KeyStore keyStore = null; |
|
136 private boolean token = false; |
|
137 private boolean nullStream = false; |
|
138 private boolean kssave = false; |
|
139 private boolean noprompt = false; |
|
140 private boolean trustcacerts = false; |
|
141 private boolean protectedPath = false; |
|
142 private boolean srcprotectedPath = false; |
|
143 private CertificateFactory cf = null; |
|
144 private KeyStore caks = null; // "cacerts" keystore |
|
145 private char[] srcstorePass = null; |
|
146 private String srcstoretype = null; |
|
147 private Set<char[]> passwords = new HashSet<char[]> (); |
|
148 private String startDate = null; |
|
149 |
|
150 private static final int CERTREQ = 1; |
|
151 private static final int CHANGEALIAS = 2; |
|
152 private static final int DELETE = 3; |
|
153 private static final int EXPORTCERT = 4; |
|
154 private static final int GENKEYPAIR = 5; |
|
155 private static final int GENSECKEY = 6; |
|
156 // there is no HELP |
|
157 private static final int IDENTITYDB = 7; |
|
158 private static final int IMPORTCERT = 8; |
|
159 private static final int IMPORTKEYSTORE = 9; |
|
160 private static final int KEYCLONE = 10; |
|
161 private static final int KEYPASSWD = 11; |
|
162 private static final int LIST = 12; |
|
163 private static final int PRINTCERT = 13; |
|
164 private static final int SELFCERT = 14; |
|
165 private static final int STOREPASSWD = 15; |
|
166 |
|
167 private static final Class[] PARAM_STRING = { String.class }; |
|
168 |
|
169 private static final String JKS = "jks"; |
|
170 private static final String NONE = "NONE"; |
|
171 private static final String P11KEYSTORE = "PKCS11"; |
|
172 private static final String P12KEYSTORE = "PKCS12"; |
|
173 private final String keyAlias = "mykey"; |
|
174 |
|
175 // for i18n |
|
176 private static final java.util.ResourceBundle rb = |
|
177 java.util.ResourceBundle.getBundle("sun.security.util.Resources"); |
|
178 private static final Collator collator = Collator.getInstance(); |
|
179 static { |
|
180 // this is for case insensitive string comparisons |
|
181 collator.setStrength(Collator.PRIMARY); |
|
182 }; |
|
183 |
|
184 private KeyTool() { } |
|
185 |
|
186 public static void main(String[] args) throws Exception { |
|
187 KeyTool kt = new KeyTool(); |
|
188 kt.run(args, System.out); |
|
189 } |
|
190 |
|
191 private void run(String[] args, PrintStream out) throws Exception { |
|
192 try { |
|
193 parseArgs(args); |
|
194 doCommands(out); |
|
195 } catch (Exception e) { |
|
196 System.out.println(rb.getString("keytool error: ") + e); |
|
197 if (verbose) { |
|
198 e.printStackTrace(System.out); |
|
199 } |
|
200 if (!debug) { |
|
201 System.exit(1); |
|
202 } else { |
|
203 throw e; |
|
204 } |
|
205 } finally { |
|
206 for (char[] pass : passwords) { |
|
207 if (pass != null) { |
|
208 Arrays.fill(pass, ' '); |
|
209 pass = null; |
|
210 } |
|
211 } |
|
212 |
|
213 if (ksStream != null) { |
|
214 ksStream.close(); |
|
215 } |
|
216 } |
|
217 } |
|
218 |
|
219 /** |
|
220 * Parse command line arguments. |
|
221 */ |
|
222 void parseArgs(String[] args) { |
|
223 |
|
224 if (args.length == 0) usage(); |
|
225 |
|
226 int i=0; |
|
227 |
|
228 for (i=0; (i < args.length) && args[i].startsWith("-"); i++) { |
|
229 |
|
230 String flags = args[i]; |
|
231 /* |
|
232 * command modes |
|
233 */ |
|
234 if (collator.compare(flags, "-certreq") == 0) { |
|
235 command = CERTREQ; |
|
236 } else if (collator.compare(flags, "-delete") == 0) { |
|
237 command = DELETE; |
|
238 } else if (collator.compare(flags, "-export") == 0 || |
|
239 collator.compare(flags, "-exportcert") == 0) { |
|
240 command = EXPORTCERT; |
|
241 } else if (collator.compare(flags, "-genkey") == 0 || |
|
242 collator.compare(flags, "-genkeypair") == 0) { |
|
243 command = GENKEYPAIR; |
|
244 } else if (collator.compare(flags, "-help") == 0) { |
|
245 usage(); |
|
246 return; |
|
247 } else if (collator.compare(flags, "-identitydb") == 0) { // obsolete |
|
248 command = IDENTITYDB; |
|
249 } else if (collator.compare(flags, "-import") == 0 || |
|
250 collator.compare(flags, "-importcert") == 0) { |
|
251 command = IMPORTCERT; |
|
252 } else if (collator.compare(flags, "-keyclone") == 0) { // obsolete |
|
253 command = KEYCLONE; |
|
254 } else if (collator.compare(flags, "-changealias") == 0) { |
|
255 command = CHANGEALIAS; |
|
256 } else if (collator.compare(flags, "-keypasswd") == 0) { |
|
257 command = KEYPASSWD; |
|
258 } else if (collator.compare(flags, "-list") == 0) { |
|
259 command = LIST; |
|
260 } else if (collator.compare(flags, "-printcert") == 0) { |
|
261 command = PRINTCERT; |
|
262 } else if (collator.compare(flags, "-selfcert") == 0) { // obsolete |
|
263 command = SELFCERT; |
|
264 } else if (collator.compare(flags, "-storepasswd") == 0) { |
|
265 command = STOREPASSWD; |
|
266 } else if (collator.compare(flags, "-importkeystore") == 0) { |
|
267 command = IMPORTKEYSTORE; |
|
268 } else if (collator.compare(flags, "-genseckey") == 0) { |
|
269 command = GENSECKEY; |
|
270 } |
|
271 |
|
272 /* |
|
273 * specifiers |
|
274 */ |
|
275 else if (collator.compare(flags, "-keystore") == 0 || |
|
276 collator.compare(flags, "-destkeystore") == 0) { |
|
277 if (++i == args.length) errorNeedArgument(flags); |
|
278 ksfname = args[i]; |
|
279 } else if (collator.compare(flags, "-storepass") == 0 || |
|
280 collator.compare(flags, "-deststorepass") == 0) { |
|
281 if (++i == args.length) errorNeedArgument(flags); |
|
282 storePass = args[i].toCharArray(); |
|
283 passwords.add(storePass); |
|
284 } else if (collator.compare(flags, "-storetype") == 0 || |
|
285 collator.compare(flags, "-deststoretype") == 0) { |
|
286 if (++i == args.length) errorNeedArgument(flags); |
|
287 storetype = args[i]; |
|
288 } else if (collator.compare(flags, "-srcstorepass") == 0) { |
|
289 if (++i == args.length) errorNeedArgument(flags); |
|
290 srcstorePass = args[i].toCharArray(); |
|
291 passwords.add(srcstorePass); |
|
292 } else if (collator.compare(flags, "-srcstoretype") == 0) { |
|
293 if (++i == args.length) errorNeedArgument(flags); |
|
294 srcstoretype = args[i]; |
|
295 } else if (collator.compare(flags, "-srckeypass") == 0) { |
|
296 if (++i == args.length) errorNeedArgument(flags); |
|
297 srckeyPass = args[i].toCharArray(); |
|
298 passwords.add(srckeyPass); |
|
299 } else if (collator.compare(flags, "-srcprovidername") == 0) { |
|
300 if (++i == args.length) errorNeedArgument(flags); |
|
301 srcProviderName = args[i]; |
|
302 } else if (collator.compare(flags, "-providername") == 0 || |
|
303 collator.compare(flags, "-destprovidername") == 0) { |
|
304 if (++i == args.length) errorNeedArgument(flags); |
|
305 providerName = args[i]; |
|
306 } else if (collator.compare(flags, "-providerpath") == 0) { |
|
307 if (++i == args.length) errorNeedArgument(flags); |
|
308 pathlist = args[i]; |
|
309 } else if (collator.compare(flags, "-keypass") == 0) { |
|
310 if (++i == args.length) errorNeedArgument(flags); |
|
311 keyPass = args[i].toCharArray(); |
|
312 passwords.add(keyPass); |
|
313 } else if (collator.compare(flags, "-new") == 0) { |
|
314 if (++i == args.length) errorNeedArgument(flags); |
|
315 newPass = args[i].toCharArray(); |
|
316 passwords.add(newPass); |
|
317 } else if (collator.compare(flags, "-destkeypass") == 0) { |
|
318 if (++i == args.length) errorNeedArgument(flags); |
|
319 destKeyPass = args[i].toCharArray(); |
|
320 passwords.add(destKeyPass); |
|
321 } else if (collator.compare(flags, "-alias") == 0 || |
|
322 collator.compare(flags, "-srcalias") == 0) { |
|
323 if (++i == args.length) errorNeedArgument(flags); |
|
324 alias = args[i]; |
|
325 } else if (collator.compare(flags, "-dest") == 0 || |
|
326 collator.compare(flags, "-destalias") == 0) { |
|
327 if (++i == args.length) errorNeedArgument(flags); |
|
328 dest = args[i]; |
|
329 } else if (collator.compare(flags, "-dname") == 0) { |
|
330 if (++i == args.length) errorNeedArgument(flags); |
|
331 dname = args[i]; |
|
332 } else if (collator.compare(flags, "-keysize") == 0) { |
|
333 if (++i == args.length) errorNeedArgument(flags); |
|
334 keysize = Integer.parseInt(args[i]); |
|
335 } else if (collator.compare(flags, "-keyalg") == 0) { |
|
336 if (++i == args.length) errorNeedArgument(flags); |
|
337 keyAlgName = args[i]; |
|
338 } else if (collator.compare(flags, "-sigalg") == 0) { |
|
339 if (++i == args.length) errorNeedArgument(flags); |
|
340 sigAlgName = args[i]; |
|
341 } else if (collator.compare(flags, "-startdate") == 0) { |
|
342 if (++i == args.length) errorNeedArgument(flags); |
|
343 startDate = args[i]; |
|
344 } else if (collator.compare(flags, "-validity") == 0) { |
|
345 if (++i == args.length) errorNeedArgument(flags); |
|
346 validity = Long.parseLong(args[i]); |
|
347 } else if (collator.compare(flags, "-file") == 0) { |
|
348 if (++i == args.length) errorNeedArgument(flags); |
|
349 filename = args[i]; |
|
350 } else if (collator.compare(flags, "-srckeystore") == 0) { |
|
351 if (++i == args.length) errorNeedArgument(flags); |
|
352 srcksfname = args[i]; |
|
353 } else if ((collator.compare(flags, "-provider") == 0) || |
|
354 (collator.compare(flags, "-providerclass") == 0)) { |
|
355 if (++i == args.length) errorNeedArgument(flags); |
|
356 if (providers == null) { |
|
357 providers = new HashSet<Pair <String, String>> (3); |
|
358 } |
|
359 String providerClass = args[i]; |
|
360 String providerArg = null; |
|
361 |
|
362 if (args.length > (i+1)) { |
|
363 flags = args[i+1]; |
|
364 if (collator.compare(flags, "-providerarg") == 0) { |
|
365 if (args.length == (i+2)) errorNeedArgument(flags); |
|
366 providerArg = args[i+2]; |
|
367 i += 2; |
|
368 } |
|
369 } |
|
370 providers.add( |
|
371 new Pair<String, String>(providerClass, providerArg)); |
|
372 } |
|
373 |
|
374 /* |
|
375 * options |
|
376 */ |
|
377 else if (collator.compare(flags, "-v") == 0) { |
|
378 verbose = true; |
|
379 } else if (collator.compare(flags, "-debug") == 0) { |
|
380 debug = true; |
|
381 } else if (collator.compare(flags, "-rfc") == 0) { |
|
382 rfc = true; |
|
383 } else if (collator.compare(flags, "-noprompt") == 0) { |
|
384 noprompt = true; |
|
385 } else if (collator.compare(flags, "-trustcacerts") == 0) { |
|
386 trustcacerts = true; |
|
387 } else if (collator.compare(flags, "-protected") == 0 || |
|
388 collator.compare(flags, "-destprotected") == 0) { |
|
389 protectedPath = true; |
|
390 } else if (collator.compare(flags, "-srcprotected") == 0) { |
|
391 srcprotectedPath = true; |
|
392 } else { |
|
393 System.err.println(rb.getString("Illegal option: ") + flags); |
|
394 tinyHelp(); |
|
395 } |
|
396 } |
|
397 |
|
398 if (i<args.length) { |
|
399 MessageFormat form = new MessageFormat |
|
400 (rb.getString("Usage error, <arg> is not a legal command")); |
|
401 Object[] source = {args[i]}; |
|
402 throw new RuntimeException(form.format(source)); |
|
403 } |
|
404 |
|
405 if (command == -1) { |
|
406 System.err.println(rb.getString("Usage error: no command provided")); |
|
407 tinyHelp(); |
|
408 } |
|
409 } |
|
410 |
|
411 /** |
|
412 * Execute the commands. |
|
413 */ |
|
414 void doCommands(PrintStream out) throws Exception { |
|
415 |
|
416 if (storetype == null) { |
|
417 storetype = KeyStore.getDefaultType(); |
|
418 } |
|
419 storetype = KeyStoreUtil.niceStoreTypeName(storetype); |
|
420 |
|
421 if (srcstoretype == null) { |
|
422 srcstoretype = KeyStore.getDefaultType(); |
|
423 } |
|
424 srcstoretype = KeyStoreUtil.niceStoreTypeName(srcstoretype); |
|
425 |
|
426 if (P11KEYSTORE.equalsIgnoreCase(storetype) || |
|
427 KeyStoreUtil.isWindowsKeyStore(storetype)) { |
|
428 token = true; |
|
429 if (ksfname == null) { |
|
430 ksfname = NONE; |
|
431 } |
|
432 } |
|
433 if (NONE.equals(ksfname)) { |
|
434 nullStream = true; |
|
435 } |
|
436 |
|
437 if (token && !nullStream) { |
|
438 System.err.println(MessageFormat.format(rb.getString |
|
439 ("-keystore must be NONE if -storetype is {0}"), storetype)); |
|
440 System.err.println(); |
|
441 tinyHelp(); |
|
442 } |
|
443 |
|
444 if (token && |
|
445 (command == KEYPASSWD || command == STOREPASSWD)) { |
|
446 throw new UnsupportedOperationException(MessageFormat.format(rb.getString |
|
447 ("-storepasswd and -keypasswd commands not supported " + |
|
448 "if -storetype is {0}"), storetype)); |
|
449 } |
|
450 |
|
451 if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) { |
|
452 throw new UnsupportedOperationException(rb.getString |
|
453 ("-keypasswd commands not supported " + |
|
454 "if -storetype is PKCS12")); |
|
455 } |
|
456 |
|
457 if (token && (keyPass != null || newPass != null || destKeyPass != null)) { |
|
458 throw new IllegalArgumentException(MessageFormat.format(rb.getString |
|
459 ("-keypass and -new " + |
|
460 "can not be specified if -storetype is {0}"), storetype)); |
|
461 } |
|
462 |
|
463 if (protectedPath) { |
|
464 if (storePass != null || keyPass != null || |
|
465 newPass != null || destKeyPass != null) { |
|
466 throw new IllegalArgumentException(rb.getString |
|
467 ("if -protected is specified, " + |
|
468 "then -storepass, -keypass, and -new " + |
|
469 "must not be specified")); |
|
470 } |
|
471 } |
|
472 |
|
473 if (srcprotectedPath) { |
|
474 if (srcstorePass != null || srckeyPass != null) { |
|
475 throw new IllegalArgumentException(rb.getString |
|
476 ("if -srcprotected is specified, " + |
|
477 "then -srcstorepass and -srckeypass " + |
|
478 "must not be specified")); |
|
479 } |
|
480 } |
|
481 |
|
482 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { |
|
483 if (storePass != null || keyPass != null || |
|
484 newPass != null || destKeyPass != null) { |
|
485 throw new IllegalArgumentException(rb.getString |
|
486 ("if keystore is not password protected, " + |
|
487 "then -storepass, -keypass, and -new " + |
|
488 "must not be specified")); |
|
489 } |
|
490 } |
|
491 |
|
492 if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { |
|
493 if (srcstorePass != null || srckeyPass != null) { |
|
494 throw new IllegalArgumentException(rb.getString |
|
495 ("if source keystore is not password protected, " + |
|
496 "then -srcstorepass and -srckeypass " + |
|
497 "must not be specified")); |
|
498 } |
|
499 } |
|
500 |
|
501 if (validity <= (long)0) { |
|
502 throw new Exception |
|
503 (rb.getString("Validity must be greater than zero")); |
|
504 } |
|
505 |
|
506 // Try to load and install specified provider |
|
507 if (providers != null) { |
|
508 ClassLoader cl = null; |
|
509 if (pathlist != null) { |
|
510 String path = null; |
|
511 path = PathList.appendPath( |
|
512 path, System.getProperty("java.class.path")); |
|
513 path = PathList.appendPath( |
|
514 path, System.getProperty("env.class.path")); |
|
515 path = PathList.appendPath(path, pathlist); |
|
516 |
|
517 URL[] urls = PathList.pathToURLs(path); |
|
518 cl = new URLClassLoader(urls); |
|
519 } else { |
|
520 cl = ClassLoader.getSystemClassLoader(); |
|
521 } |
|
522 |
|
523 for (Pair <String, String> provider: providers) { |
|
524 String provName = provider.fst; |
|
525 Class<?> provClass; |
|
526 if (cl != null) { |
|
527 provClass = cl.loadClass(provName); |
|
528 } else { |
|
529 provClass = Class.forName(provName); |
|
530 } |
|
531 |
|
532 String provArg = provider.snd; |
|
533 Object obj; |
|
534 if (provArg == null) { |
|
535 obj = provClass.newInstance(); |
|
536 } else { |
|
537 Constructor<?> c = provClass.getConstructor(PARAM_STRING); |
|
538 obj = c.newInstance(provArg); |
|
539 } |
|
540 if (!(obj instanceof Provider)) { |
|
541 MessageFormat form = new MessageFormat |
|
542 (rb.getString("provName not a provider")); |
|
543 Object[] source = {provName}; |
|
544 throw new Exception(form.format(source)); |
|
545 } |
|
546 Security.addProvider((Provider)obj); |
|
547 } |
|
548 } |
|
549 |
|
550 if (command == LIST && verbose && rfc) { |
|
551 System.err.println(rb.getString |
|
552 ("Must not specify both -v and -rfc with 'list' command")); |
|
553 tinyHelp(); |
|
554 } |
|
555 |
|
556 // Make sure provided passwords are at least 6 characters long |
|
557 if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) { |
|
558 throw new Exception(rb.getString |
|
559 ("Key password must be at least 6 characters")); |
|
560 } |
|
561 if (newPass != null && newPass.length < 6) { |
|
562 throw new Exception(rb.getString |
|
563 ("New password must be at least 6 characters")); |
|
564 } |
|
565 if (destKeyPass != null && destKeyPass.length < 6) { |
|
566 throw new Exception(rb.getString |
|
567 ("New password must be at least 6 characters")); |
|
568 } |
|
569 |
|
570 // Check if keystore exists. |
|
571 // If no keystore has been specified at the command line, try to use |
|
572 // the default, which is located in $HOME/.keystore. |
|
573 // If the command is "genkey", "identitydb", "import", or "printcert", |
|
574 // it is OK not to have a keystore. |
|
575 if (command != PRINTCERT) { |
|
576 if (ksfname == null) { |
|
577 ksfname = System.getProperty("user.home") + File.separator |
|
578 + ".keystore"; |
|
579 } |
|
580 |
|
581 if (!nullStream) { |
|
582 try { |
|
583 ksfile = new File(ksfname); |
|
584 // Check if keystore file is empty |
|
585 if (ksfile.exists() && ksfile.length() == 0) { |
|
586 throw new Exception(rb.getString |
|
587 ("Keystore file exists, but is empty: ") + ksfname); |
|
588 } |
|
589 ksStream = new FileInputStream(ksfile); |
|
590 } catch (FileNotFoundException e) { |
|
591 if (command != GENKEYPAIR && |
|
592 command != GENSECKEY && |
|
593 command != IDENTITYDB && |
|
594 command != IMPORTCERT && |
|
595 command != IMPORTKEYSTORE) { |
|
596 throw new Exception(rb.getString |
|
597 ("Keystore file does not exist: ") + ksfname); |
|
598 } |
|
599 } |
|
600 } |
|
601 } |
|
602 |
|
603 if ((command == KEYCLONE || command == CHANGEALIAS) |
|
604 && dest == null) { |
|
605 dest = getAlias("destination"); |
|
606 if ("".equals(dest)) { |
|
607 throw new Exception(rb.getString |
|
608 ("Must specify destination alias")); |
|
609 } |
|
610 } |
|
611 |
|
612 if (command == DELETE && alias == null) { |
|
613 alias = getAlias(null); |
|
614 if ("".equals(alias)) { |
|
615 throw new Exception(rb.getString("Must specify alias")); |
|
616 } |
|
617 } |
|
618 |
|
619 // Create new keystore |
|
620 if (providerName == null) { |
|
621 keyStore = KeyStore.getInstance(storetype); |
|
622 } else { |
|
623 keyStore = KeyStore.getInstance(storetype, providerName); |
|
624 } |
|
625 |
|
626 /* |
|
627 * Load the keystore data. |
|
628 * |
|
629 * At this point, it's OK if no keystore password has been provided. |
|
630 * We want to make sure that we can load the keystore data, i.e., |
|
631 * the keystore data has the right format. If we cannot load the |
|
632 * keystore, why bother asking the user for his or her password? |
|
633 * Only if we were able to load the keystore, and no keystore |
|
634 * password has been provided, will we prompt the user for the |
|
635 * keystore password to verify the keystore integrity. |
|
636 * This means that the keystore is loaded twice: first load operation |
|
637 * checks the keystore format, second load operation verifies the |
|
638 * keystore integrity. |
|
639 * |
|
640 * If the keystore password has already been provided (at the |
|
641 * command line), however, the keystore is loaded only once, and the |
|
642 * keystore format and integrity are checked "at the same time". |
|
643 * |
|
644 * Null stream keystores are loaded later. |
|
645 */ |
|
646 if (!nullStream) { |
|
647 keyStore.load(ksStream, storePass); |
|
648 if (ksStream != null) { |
|
649 ksStream.close(); |
|
650 } |
|
651 } |
|
652 |
|
653 // All commands that create or modify the keystore require a keystore |
|
654 // password. |
|
655 |
|
656 if (nullStream && storePass != null) { |
|
657 keyStore.load(null, storePass); |
|
658 } else if (!nullStream && storePass != null) { |
|
659 // If we are creating a new non nullStream-based keystore, |
|
660 // insist that the password be at least 6 characters |
|
661 if (ksStream == null && storePass.length < 6) { |
|
662 throw new Exception(rb.getString |
|
663 ("Keystore password must be at least 6 characters")); |
|
664 } |
|
665 } else if (storePass == null) { |
|
666 |
|
667 // only prompt if (protectedPath == false) |
|
668 |
|
669 if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) && |
|
670 (command == CERTREQ || |
|
671 command == DELETE || |
|
672 command == GENKEYPAIR || |
|
673 command == GENSECKEY || |
|
674 command == IMPORTCERT || |
|
675 command == IMPORTKEYSTORE || |
|
676 command == KEYCLONE || |
|
677 command == CHANGEALIAS || |
|
678 command == SELFCERT || |
|
679 command == STOREPASSWD || |
|
680 command == KEYPASSWD || |
|
681 command == IDENTITYDB)) { |
|
682 int count = 0; |
|
683 do { |
|
684 if (command == IMPORTKEYSTORE) { |
|
685 System.err.print |
|
686 (rb.getString("Enter destination keystore password: ")); |
|
687 } else { |
|
688 System.err.print |
|
689 (rb.getString("Enter keystore password: ")); |
|
690 } |
|
691 System.err.flush(); |
|
692 storePass = Password.readPassword(System.in); |
|
693 passwords.add(storePass); |
|
694 |
|
695 // If we are creating a new non nullStream-based keystore, |
|
696 // insist that the password be at least 6 characters |
|
697 if (!nullStream && (storePass == null || storePass.length < 6)) { |
|
698 System.err.println(rb.getString |
|
699 ("Keystore password is too short - " + |
|
700 "must be at least 6 characters")); |
|
701 storePass = null; |
|
702 } |
|
703 |
|
704 // If the keystore file does not exist and needs to be |
|
705 // created, the storepass should be prompted twice. |
|
706 if (storePass != null && !nullStream && ksStream == null) { |
|
707 System.err.print(rb.getString("Re-enter new password: ")); |
|
708 char[] storePassAgain = Password.readPassword(System.in); |
|
709 passwords.add(storePassAgain); |
|
710 if (!Arrays.equals(storePass, storePassAgain)) { |
|
711 System.err.println |
|
712 (rb.getString("They don't match. Try again")); |
|
713 storePass = null; |
|
714 } |
|
715 } |
|
716 |
|
717 count++; |
|
718 } while ((storePass == null) && count < 3); |
|
719 |
|
720 |
|
721 if (storePass == null) { |
|
722 System.err.println |
|
723 (rb.getString("Too many failures - try later")); |
|
724 return; |
|
725 } |
|
726 } else if (!protectedPath |
|
727 && !KeyStoreUtil.isWindowsKeyStore(storetype) |
|
728 && !(command == PRINTCERT)) { |
|
729 // here we have EXPORTCERT and LIST (info valid until STOREPASSWD) |
|
730 System.err.print(rb.getString("Enter keystore password: ")); |
|
731 System.err.flush(); |
|
732 storePass = Password.readPassword(System.in); |
|
733 passwords.add(storePass); |
|
734 } |
|
735 |
|
736 // Now load a nullStream-based keystore, |
|
737 // or verify the integrity of an input stream-based keystore |
|
738 if (nullStream) { |
|
739 keyStore.load(null, storePass); |
|
740 } else if (ksStream != null) { |
|
741 ksStream = new FileInputStream(ksfile); |
|
742 keyStore.load(ksStream, storePass); |
|
743 ksStream.close(); |
|
744 } |
|
745 } |
|
746 |
|
747 if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) { |
|
748 MessageFormat form = new MessageFormat(rb.getString( |
|
749 "Warning: Different store and key passwords not supported " + |
|
750 "for PKCS12 KeyStores. Ignoring user-specified <command> value.")); |
|
751 if (keyPass != null && !Arrays.equals(storePass, keyPass)) { |
|
752 Object[] source = {"-keypass"}; |
|
753 System.err.println(form.format(source)); |
|
754 keyPass = storePass; |
|
755 } |
|
756 if (newPass != null && !Arrays.equals(storePass, newPass)) { |
|
757 Object[] source = {"-new"}; |
|
758 System.err.println(form.format(source)); |
|
759 newPass = storePass; |
|
760 } |
|
761 if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) { |
|
762 Object[] source = {"-destkeypass"}; |
|
763 System.err.println(form.format(source)); |
|
764 destKeyPass = storePass; |
|
765 } |
|
766 } |
|
767 |
|
768 // Create a certificate factory |
|
769 if (command == PRINTCERT || command == IMPORTCERT |
|
770 || command == IDENTITYDB) { |
|
771 cf = CertificateFactory.getInstance("X509"); |
|
772 } |
|
773 |
|
774 if (trustcacerts) { |
|
775 caks = getCacertsKeyStore(); |
|
776 } |
|
777 |
|
778 // Perform the specified command |
|
779 if (command == CERTREQ) { |
|
780 PrintStream ps = null; |
|
781 if (filename != null) { |
|
782 ps = new PrintStream(new FileOutputStream |
|
783 (filename)); |
|
784 out = ps; |
|
785 } |
|
786 try { |
|
787 doCertReq(alias, sigAlgName, out); |
|
788 } finally { |
|
789 if (ps != null) { |
|
790 ps.close(); |
|
791 } |
|
792 } |
|
793 if (verbose && filename != null) { |
|
794 MessageFormat form = new MessageFormat(rb.getString |
|
795 ("Certification request stored in file <filename>")); |
|
796 Object[] source = {filename}; |
|
797 System.err.println(form.format(source)); |
|
798 System.err.println(rb.getString("Submit this to your CA")); |
|
799 } |
|
800 } else if (command == DELETE) { |
|
801 doDeleteEntry(alias); |
|
802 kssave = true; |
|
803 } else if (command == EXPORTCERT) { |
|
804 PrintStream ps = null; |
|
805 if (filename != null) { |
|
806 ps = new PrintStream(new FileOutputStream |
|
807 (filename)); |
|
808 out = ps; |
|
809 } |
|
810 try { |
|
811 doExportCert(alias, out); |
|
812 } finally { |
|
813 if (ps != null) { |
|
814 ps.close(); |
|
815 } |
|
816 } |
|
817 if (filename != null) { |
|
818 MessageFormat form = new MessageFormat(rb.getString |
|
819 ("Certificate stored in file <filename>")); |
|
820 Object[] source = {filename}; |
|
821 System.err.println(form.format(source)); |
|
822 } |
|
823 } else if (command == GENKEYPAIR) { |
|
824 if (keyAlgName == null) { |
|
825 keyAlgName = "DSA"; |
|
826 } |
|
827 doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName); |
|
828 kssave = true; |
|
829 } else if (command == GENSECKEY) { |
|
830 if (keyAlgName == null) { |
|
831 keyAlgName = "DES"; |
|
832 } |
|
833 doGenSecretKey(alias, keyAlgName, keysize); |
|
834 kssave = true; |
|
835 } else if (command == IDENTITYDB) { |
|
836 InputStream inStream = System.in; |
|
837 if (filename != null) { |
|
838 inStream = new FileInputStream(filename); |
|
839 } |
|
840 try { |
|
841 doImportIdentityDatabase(inStream); |
|
842 } finally { |
|
843 if (inStream != System.in) { |
|
844 inStream.close(); |
|
845 } |
|
846 } |
|
847 } else if (command == IMPORTCERT) { |
|
848 InputStream inStream = System.in; |
|
849 if (filename != null) { |
|
850 inStream = new FileInputStream(filename); |
|
851 } |
|
852 try { |
|
853 String importAlias = (alias!=null)?alias:keyAlias; |
|
854 if (keyStore.entryInstanceOf(importAlias, KeyStore.PrivateKeyEntry.class)) { |
|
855 kssave = installReply(importAlias, inStream); |
|
856 if (kssave) { |
|
857 System.err.println(rb.getString |
|
858 ("Certificate reply was installed in keystore")); |
|
859 } else { |
|
860 System.err.println(rb.getString |
|
861 ("Certificate reply was not installed in keystore")); |
|
862 } |
|
863 } else if (!keyStore.containsAlias(importAlias) || |
|
864 keyStore.entryInstanceOf(importAlias, |
|
865 KeyStore.TrustedCertificateEntry.class)) { |
|
866 kssave = addTrustedCert(importAlias, inStream); |
|
867 if (kssave) { |
|
868 System.err.println(rb.getString |
|
869 ("Certificate was added to keystore")); |
|
870 } else { |
|
871 System.err.println(rb.getString |
|
872 ("Certificate was not added to keystore")); |
|
873 } |
|
874 } |
|
875 } finally { |
|
876 if (inStream != System.in) { |
|
877 inStream.close(); |
|
878 } |
|
879 } |
|
880 } else if (command == IMPORTKEYSTORE) { |
|
881 doImportKeyStore(); |
|
882 kssave = true; |
|
883 } else if (command == KEYCLONE) { |
|
884 keyPassNew = newPass; |
|
885 |
|
886 // added to make sure only key can go thru |
|
887 if (alias == null) { |
|
888 alias = keyAlias; |
|
889 } |
|
890 if (keyStore.containsAlias(alias) == false) { |
|
891 MessageFormat form = new MessageFormat |
|
892 (rb.getString("Alias <alias> does not exist")); |
|
893 Object[] source = {alias}; |
|
894 throw new Exception(form.format(source)); |
|
895 } |
|
896 if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { |
|
897 MessageFormat form = new MessageFormat(rb.getString( |
|
898 "Alias <alias> references an entry type that is not a private key entry. " + |
|
899 "The -keyclone command only supports cloning of private key entries")); |
|
900 Object[] source = {alias}; |
|
901 throw new Exception(form.format(source)); |
|
902 } |
|
903 |
|
904 doCloneEntry(alias, dest, true); // Now everything can be cloned |
|
905 kssave = true; |
|
906 } else if (command == CHANGEALIAS) { |
|
907 if (alias == null) { |
|
908 alias = keyAlias; |
|
909 } |
|
910 doCloneEntry(alias, dest, false); |
|
911 // in PKCS11, clone a PrivateKeyEntry will delete the old one |
|
912 if (keyStore.containsAlias(alias)) { |
|
913 doDeleteEntry(alias); |
|
914 } |
|
915 kssave = true; |
|
916 } else if (command == KEYPASSWD) { |
|
917 keyPassNew = newPass; |
|
918 doChangeKeyPasswd(alias); |
|
919 kssave = true; |
|
920 } else if (command == LIST) { |
|
921 if (alias != null) { |
|
922 doPrintEntry(alias, out, true); |
|
923 } else { |
|
924 doPrintEntries(out); |
|
925 } |
|
926 } else if (command == PRINTCERT) { |
|
927 InputStream inStream = System.in; |
|
928 if (filename != null) { |
|
929 inStream = new FileInputStream(filename); |
|
930 } |
|
931 try { |
|
932 doPrintCert(inStream, out); |
|
933 } finally { |
|
934 if (inStream != System.in) { |
|
935 inStream.close(); |
|
936 } |
|
937 } |
|
938 } else if (command == SELFCERT) { |
|
939 doSelfCert(alias, dname, sigAlgName); |
|
940 kssave = true; |
|
941 } else if (command == STOREPASSWD) { |
|
942 storePassNew = newPass; |
|
943 if (storePassNew == null) { |
|
944 storePassNew = getNewPasswd("keystore password", storePass); |
|
945 } |
|
946 kssave = true; |
|
947 } |
|
948 |
|
949 // If we need to save the keystore, do so. |
|
950 if (kssave) { |
|
951 if (verbose) { |
|
952 MessageFormat form = new MessageFormat |
|
953 (rb.getString("[Storing ksfname]")); |
|
954 Object[] source = {nullStream ? "keystore" : ksfname}; |
|
955 System.err.println(form.format(source)); |
|
956 } |
|
957 |
|
958 if (token) { |
|
959 keyStore.store(null, null); |
|
960 } else { |
|
961 FileOutputStream fout = null; |
|
962 try { |
|
963 fout = (nullStream ? |
|
964 (FileOutputStream)null : |
|
965 new FileOutputStream(ksfname)); |
|
966 keyStore.store |
|
967 (fout, |
|
968 (storePassNew!=null) ? storePassNew : storePass); |
|
969 } finally { |
|
970 if (fout != null) { |
|
971 fout.close(); |
|
972 } |
|
973 } |
|
974 } |
|
975 } |
|
976 } |
|
977 |
|
978 /** |
|
979 * Creates a PKCS#10 cert signing request, corresponding to the |
|
980 * keys (and name) associated with a given alias. |
|
981 */ |
|
982 private void doCertReq(String alias, String sigAlgName, PrintStream out) |
|
983 throws Exception |
|
984 { |
|
985 if (alias == null) { |
|
986 alias = keyAlias; |
|
987 } |
|
988 |
|
989 Object[] objs = recoverKey(alias, storePass, keyPass); |
|
990 PrivateKey privKey = (PrivateKey)objs[0]; |
|
991 if (keyPass == null) { |
|
992 keyPass = (char[])objs[1]; |
|
993 } |
|
994 |
|
995 Certificate cert = keyStore.getCertificate(alias); |
|
996 if (cert == null) { |
|
997 MessageFormat form = new MessageFormat |
|
998 (rb.getString("alias has no public key (certificate)")); |
|
999 Object[] source = {alias}; |
|
1000 throw new Exception(form.format(source)); |
|
1001 } |
|
1002 PKCS10 request = new PKCS10(cert.getPublicKey()); |
|
1003 |
|
1004 // Construct an X500Signer object, so that we can sign the request |
|
1005 if (sigAlgName == null) { |
|
1006 // If no signature algorithm was specified at the command line, |
|
1007 // we choose one that is compatible with the selected private key |
|
1008 String keyAlgName = privKey.getAlgorithm(); |
|
1009 if ("DSA".equalsIgnoreCase(keyAlgName) |
|
1010 || "DSS".equalsIgnoreCase(keyAlgName)) { |
|
1011 sigAlgName = "SHA1WithDSA"; |
|
1012 } else if ("RSA".equalsIgnoreCase(keyAlgName)) { |
|
1013 sigAlgName = "SHA1WithRSA"; |
|
1014 } else { |
|
1015 throw new Exception(rb.getString |
|
1016 ("Cannot derive signature algorithm")); |
|
1017 } |
|
1018 } |
|
1019 |
|
1020 Signature signature = Signature.getInstance(sigAlgName); |
|
1021 signature.initSign(privKey); |
|
1022 X500Name subject = |
|
1023 new X500Name(((X509Certificate)cert).getSubjectDN().toString()); |
|
1024 X500Signer signer = new X500Signer(signature, subject); |
|
1025 |
|
1026 // Sign the request and base-64 encode it |
|
1027 request.encodeAndSign(signer); |
|
1028 request.print(out); |
|
1029 } |
|
1030 |
|
1031 /** |
|
1032 * Deletes an entry from the keystore. |
|
1033 */ |
|
1034 private void doDeleteEntry(String alias) throws Exception { |
|
1035 if (keyStore.containsAlias(alias) == false) { |
|
1036 MessageFormat form = new MessageFormat |
|
1037 (rb.getString("Alias <alias> does not exist")); |
|
1038 Object[] source = {alias}; |
|
1039 throw new Exception(form.format(source)); |
|
1040 } |
|
1041 keyStore.deleteEntry(alias); |
|
1042 } |
|
1043 |
|
1044 /** |
|
1045 * Exports a certificate from the keystore. |
|
1046 */ |
|
1047 private void doExportCert(String alias, PrintStream out) |
|
1048 throws Exception |
|
1049 { |
|
1050 if (storePass == null |
|
1051 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { |
|
1052 printWarning(); |
|
1053 } |
|
1054 if (alias == null) { |
|
1055 alias = keyAlias; |
|
1056 } |
|
1057 if (keyStore.containsAlias(alias) == false) { |
|
1058 MessageFormat form = new MessageFormat |
|
1059 (rb.getString("Alias <alias> does not exist")); |
|
1060 Object[] source = {alias}; |
|
1061 throw new Exception(form.format(source)); |
|
1062 } |
|
1063 |
|
1064 X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias); |
|
1065 if (cert == null) { |
|
1066 MessageFormat form = new MessageFormat |
|
1067 (rb.getString("Alias <alias> has no certificate")); |
|
1068 Object[] source = {alias}; |
|
1069 throw new Exception(form.format(source)); |
|
1070 } |
|
1071 dumpCert(cert, out); |
|
1072 } |
|
1073 |
|
1074 /** |
|
1075 * Prompt the user for a keypass when generating a key entry. |
|
1076 * @param alias the entry we will set password for |
|
1077 * @param orig the original entry of doing a dup, null if generate new |
|
1078 * @param origPass the password to copy from if user press ENTER |
|
1079 */ |
|
1080 private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{ |
|
1081 if (P12KEYSTORE.equalsIgnoreCase(storetype)) { |
|
1082 return origPass; |
|
1083 } else if (!token) { |
|
1084 // Prompt for key password |
|
1085 int count; |
|
1086 for (count = 0; count < 3; count++) { |
|
1087 MessageFormat form = new MessageFormat(rb.getString |
|
1088 ("Enter key password for <alias>")); |
|
1089 Object[] source = {alias}; |
|
1090 System.err.println(form.format(source)); |
|
1091 if (orig == null) { |
|
1092 System.err.print(rb.getString |
|
1093 ("\t(RETURN if same as keystore password): ")); |
|
1094 } else { |
|
1095 form = new MessageFormat(rb.getString |
|
1096 ("\t(RETURN if same as for <otherAlias>)")); |
|
1097 Object[] src = {orig}; |
|
1098 System.err.print(form.format(src)); |
|
1099 } |
|
1100 System.err.flush(); |
|
1101 char[] entered = Password.readPassword(System.in); |
|
1102 passwords.add(entered); |
|
1103 if (entered == null) { |
|
1104 return origPass; |
|
1105 } else if (entered.length >= 6) { |
|
1106 System.err.print(rb.getString("Re-enter new password: ")); |
|
1107 char[] passAgain = Password.readPassword(System.in); |
|
1108 passwords.add(passAgain); |
|
1109 if (!Arrays.equals(entered, passAgain)) { |
|
1110 System.err.println |
|
1111 (rb.getString("They don't match. Try again")); |
|
1112 continue; |
|
1113 } |
|
1114 return entered; |
|
1115 } else { |
|
1116 System.err.println(rb.getString |
|
1117 ("Key password is too short - must be at least 6 characters")); |
|
1118 } |
|
1119 } |
|
1120 if (count == 3) { |
|
1121 if (command == KEYCLONE) { |
|
1122 throw new Exception(rb.getString |
|
1123 ("Too many failures. Key entry not cloned")); |
|
1124 } else { |
|
1125 throw new Exception(rb.getString |
|
1126 ("Too many failures - key not added to keystore")); |
|
1127 } |
|
1128 } |
|
1129 } |
|
1130 return null; // PKCS11 |
|
1131 } |
|
1132 /** |
|
1133 * Creates a new secret key. |
|
1134 */ |
|
1135 private void doGenSecretKey(String alias, String keyAlgName, |
|
1136 int keysize) |
|
1137 throws Exception |
|
1138 { |
|
1139 if (alias == null) { |
|
1140 alias = keyAlias; |
|
1141 } |
|
1142 if (keyStore.containsAlias(alias)) { |
|
1143 MessageFormat form = new MessageFormat(rb.getString |
|
1144 ("Secret key not generated, alias <alias> already exists")); |
|
1145 Object[] source = {alias}; |
|
1146 throw new Exception(form.format(source)); |
|
1147 } |
|
1148 |
|
1149 SecretKey secKey = null; |
|
1150 KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName); |
|
1151 if (keysize != -1) { |
|
1152 keygen.init(keysize); |
|
1153 } else if ("DES".equalsIgnoreCase(keyAlgName)) { |
|
1154 keygen.init(56); |
|
1155 } else if ("DESede".equalsIgnoreCase(keyAlgName)) { |
|
1156 keygen.init(168); |
|
1157 } else { |
|
1158 throw new Exception(rb.getString |
|
1159 ("Please provide -keysize for secret key generation")); |
|
1160 } |
|
1161 |
|
1162 secKey = keygen.generateKey(); |
|
1163 if (keyPass == null) { |
|
1164 keyPass = promptForKeyPass(alias, null, storePass); |
|
1165 } |
|
1166 keyStore.setKeyEntry(alias, secKey, keyPass, null); |
|
1167 } |
|
1168 |
|
1169 /** |
|
1170 * Creates a new key pair and self-signed certificate. |
|
1171 */ |
|
1172 private void doGenKeyPair(String alias, String dname, String keyAlgName, |
|
1173 int keysize, String sigAlgName) |
|
1174 throws Exception |
|
1175 { |
|
1176 if (keysize == -1) { |
|
1177 if ("EC".equalsIgnoreCase(keyAlgName)) { |
|
1178 keysize = 256; |
|
1179 } else { |
|
1180 keysize = 1024; |
|
1181 } |
|
1182 } |
|
1183 |
|
1184 if (alias == null) { |
|
1185 alias = keyAlias; |
|
1186 } |
|
1187 |
|
1188 if (keyStore.containsAlias(alias)) { |
|
1189 MessageFormat form = new MessageFormat(rb.getString |
|
1190 ("Key pair not generated, alias <alias> already exists")); |
|
1191 Object[] source = {alias}; |
|
1192 throw new Exception(form.format(source)); |
|
1193 } |
|
1194 |
|
1195 if (sigAlgName == null) { |
|
1196 if ("DSA".equalsIgnoreCase(keyAlgName)) { |
|
1197 sigAlgName = "SHA1WithDSA"; |
|
1198 } else if ("RSA".equalsIgnoreCase(keyAlgName)) { |
|
1199 sigAlgName = "SHA1WithRSA"; |
|
1200 } else if ("EC".equalsIgnoreCase(keyAlgName)) { |
|
1201 sigAlgName = "SHA1withECDSA"; |
|
1202 } else { |
|
1203 throw new Exception(rb.getString |
|
1204 ("Cannot derive signature algorithm")); |
|
1205 } |
|
1206 } |
|
1207 CertAndKeyGen keypair = |
|
1208 new CertAndKeyGen(keyAlgName, sigAlgName, providerName); |
|
1209 |
|
1210 |
|
1211 // If DN is provided, parse it. Otherwise, prompt the user for it. |
|
1212 X500Name x500Name; |
|
1213 if (dname == null) { |
|
1214 x500Name = getX500Name(); |
|
1215 } else { |
|
1216 x500Name = new X500Name(dname); |
|
1217 } |
|
1218 |
|
1219 keypair.generate(keysize); |
|
1220 PrivateKey privKey = keypair.getPrivateKey(); |
|
1221 |
|
1222 X509Certificate[] chain = new X509Certificate[1]; |
|
1223 chain[0] = keypair.getSelfCertificate( |
|
1224 x500Name, getStartDate(startDate), validity*24L*60L*60L); |
|
1225 |
|
1226 if (verbose) { |
|
1227 MessageFormat form = new MessageFormat(rb.getString |
|
1228 ("Generating keysize bit keyAlgName key pair and self-signed certificate " + |
|
1229 "(sigAlgName) with a validity of validality days\n\tfor: x500Name")); |
|
1230 Object[] source = {new Integer(keysize), |
|
1231 privKey.getAlgorithm(), |
|
1232 chain[0].getSigAlgName(), |
|
1233 new Long(validity), |
|
1234 x500Name}; |
|
1235 System.err.println(form.format(source)); |
|
1236 } |
|
1237 |
|
1238 if (keyPass == null) { |
|
1239 keyPass = promptForKeyPass(alias, null, storePass); |
|
1240 } |
|
1241 keyStore.setKeyEntry(alias, privKey, keyPass, chain); |
|
1242 } |
|
1243 |
|
1244 /** |
|
1245 * Clones an entry |
|
1246 * @param orig original alias |
|
1247 * @param dest destination alias |
|
1248 * @changePassword if the password can be changed |
|
1249 */ |
|
1250 private void doCloneEntry(String orig, String dest, boolean changePassword) |
|
1251 throws Exception |
|
1252 { |
|
1253 if (orig == null) { |
|
1254 orig = keyAlias; |
|
1255 } |
|
1256 |
|
1257 if (keyStore.containsAlias(dest)) { |
|
1258 MessageFormat form = new MessageFormat |
|
1259 (rb.getString("Destination alias <dest> already exists")); |
|
1260 Object[] source = {dest}; |
|
1261 throw new Exception(form.format(source)); |
|
1262 } |
|
1263 |
|
1264 Object[] objs = recoverEntry(keyStore, orig, storePass, keyPass); |
|
1265 Entry entry = (Entry)objs[0]; |
|
1266 keyPass = (char[])objs[1]; |
|
1267 |
|
1268 PasswordProtection pp = null; |
|
1269 |
|
1270 if (keyPass != null) { // protected |
|
1271 if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) { |
|
1272 keyPassNew = keyPass; |
|
1273 } else { |
|
1274 if (keyPassNew == null) { |
|
1275 keyPassNew = promptForKeyPass(dest, orig, keyPass); |
|
1276 } |
|
1277 } |
|
1278 pp = new PasswordProtection(keyPassNew); |
|
1279 } |
|
1280 keyStore.setEntry(dest, entry, pp); |
|
1281 } |
|
1282 |
|
1283 /** |
|
1284 * Changes a key password. |
|
1285 */ |
|
1286 private void doChangeKeyPasswd(String alias) throws Exception |
|
1287 { |
|
1288 |
|
1289 if (alias == null) { |
|
1290 alias = keyAlias; |
|
1291 } |
|
1292 Object[] objs = recoverKey(alias, storePass, keyPass); |
|
1293 Key privKey = (Key)objs[0]; |
|
1294 if (keyPass == null) { |
|
1295 keyPass = (char[])objs[1]; |
|
1296 } |
|
1297 |
|
1298 if (keyPassNew == null) { |
|
1299 MessageFormat form = new MessageFormat |
|
1300 (rb.getString("key password for <alias>")); |
|
1301 Object[] source = {alias}; |
|
1302 keyPassNew = getNewPasswd(form.format(source), keyPass); |
|
1303 } |
|
1304 keyStore.setKeyEntry(alias, privKey, keyPassNew, |
|
1305 keyStore.getCertificateChain(alias)); |
|
1306 } |
|
1307 |
|
1308 /** |
|
1309 * Imports a JDK 1.1-style identity database. We can only store one |
|
1310 * certificate per identity, because we use the identity's name as the |
|
1311 * alias (which references a keystore entry), and aliases must be unique. |
|
1312 */ |
|
1313 private void doImportIdentityDatabase(InputStream in) |
|
1314 throws Exception |
|
1315 { |
|
1316 byte[] encoded; |
|
1317 ByteArrayInputStream bais; |
|
1318 java.security.cert.X509Certificate newCert; |
|
1319 java.security.cert.Certificate[] chain = null; |
|
1320 PrivateKey privKey; |
|
1321 boolean modified = false; |
|
1322 |
|
1323 IdentityDatabase idb = IdentityDatabase.fromStream(in); |
|
1324 for (Enumeration<Identity> enum_ = idb.identities(); |
|
1325 enum_.hasMoreElements();) { |
|
1326 Identity id = enum_.nextElement(); |
|
1327 newCert = null; |
|
1328 // only store trusted identities in keystore |
|
1329 if ((id instanceof SystemSigner && ((SystemSigner)id).isTrusted()) |
|
1330 || (id instanceof SystemIdentity |
|
1331 && ((SystemIdentity)id).isTrusted())) { |
|
1332 // ignore if keystore entry with same alias name already exists |
|
1333 if (keyStore.containsAlias(id.getName())) { |
|
1334 MessageFormat form = new MessageFormat |
|
1335 (rb.getString("Keystore entry for <id.getName()> already exists")); |
|
1336 Object[] source = {id.getName()}; |
|
1337 System.err.println(form.format(source)); |
|
1338 continue; |
|
1339 } |
|
1340 java.security.Certificate[] certs = id.certificates(); |
|
1341 if (certs!=null && certs.length>0) { |
|
1342 // we can only store one user cert per identity. |
|
1343 // convert old-style to new-style cert via the encoding |
|
1344 DerOutputStream dos = new DerOutputStream(); |
|
1345 certs[0].encode(dos); |
|
1346 encoded = dos.toByteArray(); |
|
1347 bais = new ByteArrayInputStream(encoded); |
|
1348 newCert = (X509Certificate)cf.generateCertificate(bais); |
|
1349 bais.close(); |
|
1350 |
|
1351 // if certificate is self-signed, make sure it verifies |
|
1352 if (isSelfSigned(newCert)) { |
|
1353 PublicKey pubKey = newCert.getPublicKey(); |
|
1354 try { |
|
1355 newCert.verify(pubKey); |
|
1356 } catch (Exception e) { |
|
1357 // ignore this cert |
|
1358 continue; |
|
1359 } |
|
1360 } |
|
1361 |
|
1362 if (id instanceof SystemSigner) { |
|
1363 MessageFormat form = new MessageFormat(rb.getString |
|
1364 ("Creating keystore entry for <id.getName()> ...")); |
|
1365 Object[] source = {id.getName()}; |
|
1366 System.err.println(form.format(source)); |
|
1367 if (chain==null) { |
|
1368 chain = new java.security.cert.Certificate[1]; |
|
1369 } |
|
1370 chain[0] = newCert; |
|
1371 privKey = ((SystemSigner)id).getPrivateKey(); |
|
1372 keyStore.setKeyEntry(id.getName(), privKey, storePass, |
|
1373 chain); |
|
1374 } else { |
|
1375 keyStore.setCertificateEntry(id.getName(), newCert); |
|
1376 } |
|
1377 kssave = true; |
|
1378 } |
|
1379 } |
|
1380 } |
|
1381 if (!kssave) { |
|
1382 System.err.println(rb.getString |
|
1383 ("No entries from identity database added")); |
|
1384 } |
|
1385 } |
|
1386 |
|
1387 /** |
|
1388 * Prints a single keystore entry. |
|
1389 */ |
|
1390 private void doPrintEntry(String alias, PrintStream out, |
|
1391 boolean printWarning) |
|
1392 throws Exception |
|
1393 { |
|
1394 if (storePass == null && printWarning |
|
1395 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { |
|
1396 printWarning(); |
|
1397 } |
|
1398 |
|
1399 if (keyStore.containsAlias(alias) == false) { |
|
1400 MessageFormat form = new MessageFormat |
|
1401 (rb.getString("Alias <alias> does not exist")); |
|
1402 Object[] source = {alias}; |
|
1403 throw new Exception(form.format(source)); |
|
1404 } |
|
1405 |
|
1406 if (verbose || rfc || debug) { |
|
1407 MessageFormat form = new MessageFormat |
|
1408 (rb.getString("Alias name: alias")); |
|
1409 Object[] source = {alias}; |
|
1410 out.println(form.format(source)); |
|
1411 |
|
1412 if (!token) { |
|
1413 form = new MessageFormat(rb.getString |
|
1414 ("Creation date: keyStore.getCreationDate(alias)")); |
|
1415 Object[] src = {keyStore.getCreationDate(alias)}; |
|
1416 out.println(form.format(src)); |
|
1417 } |
|
1418 } else { |
|
1419 if (!token) { |
|
1420 MessageFormat form = new MessageFormat |
|
1421 (rb.getString("alias, keyStore.getCreationDate(alias), ")); |
|
1422 Object[] source = {alias, keyStore.getCreationDate(alias)}; |
|
1423 out.print(form.format(source)); |
|
1424 } else { |
|
1425 MessageFormat form = new MessageFormat |
|
1426 (rb.getString("alias, ")); |
|
1427 Object[] source = {alias}; |
|
1428 out.print(form.format(source)); |
|
1429 } |
|
1430 } |
|
1431 |
|
1432 if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { |
|
1433 if (verbose || rfc || debug) { |
|
1434 Object[] source = {"SecretKeyEntry"}; |
|
1435 out.println(new MessageFormat( |
|
1436 rb.getString("Entry type: <type>")).format(source)); |
|
1437 } else { |
|
1438 out.println("SecretKeyEntry, "); |
|
1439 } |
|
1440 } else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { |
|
1441 if (verbose || rfc || debug) { |
|
1442 Object[] source = {"PrivateKeyEntry"}; |
|
1443 out.println(new MessageFormat( |
|
1444 rb.getString("Entry type: <type>")).format(source)); |
|
1445 } else { |
|
1446 out.println("PrivateKeyEntry, "); |
|
1447 } |
|
1448 |
|
1449 // Get the chain |
|
1450 Certificate[] chain = keyStore.getCertificateChain(alias); |
|
1451 if (chain != null) { |
|
1452 if (verbose || rfc || debug) { |
|
1453 out.println(rb.getString |
|
1454 ("Certificate chain length: ") + chain.length); |
|
1455 for (int i = 0; i < chain.length; i ++) { |
|
1456 MessageFormat form = new MessageFormat |
|
1457 (rb.getString("Certificate[(i + 1)]:")); |
|
1458 Object[] source = {new Integer((i + 1))}; |
|
1459 out.println(form.format(source)); |
|
1460 if (verbose && (chain[i] instanceof X509Certificate)) { |
|
1461 printX509Cert((X509Certificate)(chain[i]), out); |
|
1462 } else if (debug) { |
|
1463 out.println(chain[i].toString()); |
|
1464 } else { |
|
1465 dumpCert(chain[i], out); |
|
1466 } |
|
1467 } |
|
1468 } else { |
|
1469 // Print the digest of the user cert only |
|
1470 out.println |
|
1471 (rb.getString("Certificate fingerprint (MD5): ") + |
|
1472 getCertFingerPrint("MD5", chain[0])); |
|
1473 } |
|
1474 } |
|
1475 } else if (keyStore.entryInstanceOf(alias, |
|
1476 KeyStore.TrustedCertificateEntry.class)) { |
|
1477 // We have a trusted certificate entry |
|
1478 Certificate cert = keyStore.getCertificate(alias); |
|
1479 if (verbose && (cert instanceof X509Certificate)) { |
|
1480 out.println(rb.getString("Entry type: trustedCertEntry\n")); |
|
1481 printX509Cert((X509Certificate)cert, out); |
|
1482 } else if (rfc) { |
|
1483 out.println(rb.getString("Entry type: trustedCertEntry\n")); |
|
1484 dumpCert(cert, out); |
|
1485 } else if (debug) { |
|
1486 out.println(cert.toString()); |
|
1487 } else { |
|
1488 out.println(rb.getString("trustedCertEntry,")); |
|
1489 out.println(rb.getString("Certificate fingerprint (MD5): ") |
|
1490 + getCertFingerPrint("MD5", cert)); |
|
1491 } |
|
1492 } else { |
|
1493 out.println(rb.getString("Unknown Entry Type")); |
|
1494 } |
|
1495 } |
|
1496 |
|
1497 /** |
|
1498 * Load the srckeystore from a stream, used in -importkeystore |
|
1499 * @returns the src KeyStore |
|
1500 */ |
|
1501 KeyStore loadSourceKeyStore() throws Exception { |
|
1502 boolean isPkcs11 = false; |
|
1503 |
|
1504 InputStream is = null; |
|
1505 |
|
1506 if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) || |
|
1507 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { |
|
1508 if (!NONE.equals(srcksfname)) { |
|
1509 System.err.println(MessageFormat.format(rb.getString |
|
1510 ("-keystore must be NONE if -storetype is {0}"), srcstoretype)); |
|
1511 System.err.println(); |
|
1512 tinyHelp(); |
|
1513 } |
|
1514 isPkcs11 = true; |
|
1515 } else { |
|
1516 if (srcksfname != null) { |
|
1517 File srcksfile = new File(srcksfname); |
|
1518 if (srcksfile.exists() && srcksfile.length() == 0) { |
|
1519 throw new Exception(rb.getString |
|
1520 ("Source keystore file exists, but is empty: ") + |
|
1521 srcksfname); |
|
1522 } |
|
1523 is = new FileInputStream(srcksfile); |
|
1524 } else { |
|
1525 throw new Exception(rb.getString |
|
1526 ("Please specify -srckeystore")); |
|
1527 } |
|
1528 } |
|
1529 |
|
1530 KeyStore store; |
|
1531 try { |
|
1532 if (srcProviderName == null) { |
|
1533 store = KeyStore.getInstance(srcstoretype); |
|
1534 } else { |
|
1535 store = KeyStore.getInstance(srcstoretype, srcProviderName); |
|
1536 } |
|
1537 |
|
1538 if (srcstorePass == null |
|
1539 && !srcprotectedPath |
|
1540 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { |
|
1541 System.err.print(rb.getString("Enter source keystore password: ")); |
|
1542 System.err.flush(); |
|
1543 srcstorePass = Password.readPassword(System.in); |
|
1544 passwords.add(srcstorePass); |
|
1545 } |
|
1546 |
|
1547 // always let keypass be storepass when using pkcs12 |
|
1548 if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) { |
|
1549 if (srckeyPass != null && srcstorePass != null && |
|
1550 !Arrays.equals(srcstorePass, srckeyPass)) { |
|
1551 MessageFormat form = new MessageFormat(rb.getString( |
|
1552 "Warning: Different store and key passwords not supported " + |
|
1553 "for PKCS12 KeyStores. Ignoring user-specified <command> value.")); |
|
1554 Object[] source = {"-srckeypass"}; |
|
1555 System.err.println(form.format(source)); |
|
1556 srckeyPass = srcstorePass; |
|
1557 } |
|
1558 } |
|
1559 |
|
1560 store.load(is, srcstorePass); // "is" already null in PKCS11 |
|
1561 } finally { |
|
1562 if (is != null) { |
|
1563 is.close(); |
|
1564 } |
|
1565 } |
|
1566 |
|
1567 if (srcstorePass == null |
|
1568 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { |
|
1569 // anti refactoring, copied from printWarning(), |
|
1570 // but change 2 lines |
|
1571 System.err.println(); |
|
1572 System.err.println(rb.getString |
|
1573 ("***************** WARNING WARNING WARNING *****************")); |
|
1574 System.err.println(rb.getString |
|
1575 ("* The integrity of the information stored in the srckeystore*")); |
|
1576 System.err.println(rb.getString |
|
1577 ("* has NOT been verified! In order to verify its integrity, *")); |
|
1578 System.err.println(rb.getString |
|
1579 ("* you must provide the srckeystore password. *")); |
|
1580 System.err.println(rb.getString |
|
1581 ("***************** WARNING WARNING WARNING *****************")); |
|
1582 System.err.println(); |
|
1583 } |
|
1584 |
|
1585 return store; |
|
1586 } |
|
1587 |
|
1588 /** |
|
1589 * import all keys and certs from importkeystore. |
|
1590 * keep alias unchanged if no name conflict, otherwise, prompt. |
|
1591 * keep keypass unchanged for keys |
|
1592 */ |
|
1593 private void doImportKeyStore() throws Exception { |
|
1594 |
|
1595 if (alias != null) { |
|
1596 doImportKeyStoreSingle(loadSourceKeyStore(), alias); |
|
1597 } else { |
|
1598 if (dest != null || srckeyPass != null || destKeyPass != null) { |
|
1599 throw new Exception(rb.getString( |
|
1600 "if alias not specified, destalias, srckeypass, " + |
|
1601 "and destkeypass must not be specified")); |
|
1602 } |
|
1603 doImportKeyStoreAll(loadSourceKeyStore()); |
|
1604 } |
|
1605 /* |
|
1606 * Information display rule of -importkeystore |
|
1607 * 1. inside single, shows failure |
|
1608 * 2. inside all, shows sucess |
|
1609 * 3. inside all where there is a failure, prompt for continue |
|
1610 * 4. at the final of all, shows summary |
|
1611 */ |
|
1612 } |
|
1613 |
|
1614 /** |
|
1615 * Import a single entry named alias from srckeystore |
|
1616 * @returns 1 if the import action succeed |
|
1617 * 0 if user choose to ignore an alias-dumplicated entry |
|
1618 * 2 if setEntry throws Exception |
|
1619 */ |
|
1620 private int doImportKeyStoreSingle(KeyStore srckeystore, String alias) |
|
1621 throws Exception { |
|
1622 |
|
1623 String newAlias = (dest==null) ? alias : dest; |
|
1624 |
|
1625 if (keyStore.containsAlias(newAlias)) { |
|
1626 Object[] source = {alias}; |
|
1627 if (noprompt) { |
|
1628 System.err.println(new MessageFormat(rb.getString( |
|
1629 "Warning: Overwriting existing alias <alias> in destination keystore")).format(source)); |
|
1630 } else { |
|
1631 String reply = getYesNoReply(new MessageFormat(rb.getString( |
|
1632 "Existing entry alias <alias> exists, overwrite? [no]: ")).format(source)); |
|
1633 if ("NO".equals(reply)) { |
|
1634 newAlias = inputStringFromStdin(rb.getString |
|
1635 ("Enter new alias name\t(RETURN to cancel import for this entry): ")); |
|
1636 if ("".equals(newAlias)) { |
|
1637 System.err.println(new MessageFormat(rb.getString( |
|
1638 "Entry for alias <alias> not imported.")).format( |
|
1639 source)); |
|
1640 return 0; |
|
1641 } |
|
1642 } |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 Object[] objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass); |
|
1647 Entry entry = (Entry)objs[0]; |
|
1648 |
|
1649 PasswordProtection pp = null; |
|
1650 |
|
1651 // According to keytool.html, "The destination entry will be protected |
|
1652 // using destkeypass. If destkeypass is not provided, the destination |
|
1653 // entry will be protected with the source entry password." |
|
1654 // so always try to protect with destKeyPass. |
|
1655 if (destKeyPass != null) { |
|
1656 pp = new PasswordProtection(destKeyPass); |
|
1657 } else if (objs[1] != null) { |
|
1658 pp = new PasswordProtection((char[])objs[1]); |
|
1659 } |
|
1660 |
|
1661 try { |
|
1662 keyStore.setEntry(newAlias, entry, pp); |
|
1663 return 1; |
|
1664 } catch (KeyStoreException kse) { |
|
1665 Object[] source2 = {alias, kse.toString()}; |
|
1666 MessageFormat form = new MessageFormat(rb.getString( |
|
1667 "Problem importing entry for alias <alias>: <exception>.\nEntry for alias <alias> not imported.")); |
|
1668 System.err.println(form.format(source2)); |
|
1669 return 2; |
|
1670 } |
|
1671 } |
|
1672 |
|
1673 private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception { |
|
1674 |
|
1675 int ok = 0; |
|
1676 int count = srckeystore.size(); |
|
1677 for (Enumeration<String> e = srckeystore.aliases(); |
|
1678 e.hasMoreElements(); ) { |
|
1679 String alias = e.nextElement(); |
|
1680 int result = doImportKeyStoreSingle(srckeystore, alias); |
|
1681 if (result == 1) { |
|
1682 ok++; |
|
1683 Object[] source = {alias}; |
|
1684 MessageFormat form = new MessageFormat(rb.getString("Entry for alias <alias> successfully imported.")); |
|
1685 System.err.println(form.format(source)); |
|
1686 } else if (result == 2) { |
|
1687 if (!noprompt) { |
|
1688 String reply = getYesNoReply("Do you want to quit the import process? [no]: "); |
|
1689 if ("YES".equals(reply)) { |
|
1690 break; |
|
1691 } |
|
1692 } |
|
1693 } |
|
1694 } |
|
1695 Object[] source = {ok, count-ok}; |
|
1696 MessageFormat form = new MessageFormat(rb.getString( |
|
1697 "Import command completed: <ok> entries successfully imported, <fail> entries failed or cancelled")); |
|
1698 System.err.println(form.format(source)); |
|
1699 } |
|
1700 |
|
1701 /** |
|
1702 * Prints all keystore entries. |
|
1703 */ |
|
1704 private void doPrintEntries(PrintStream out) |
|
1705 throws Exception |
|
1706 { |
|
1707 if (storePass == null |
|
1708 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { |
|
1709 printWarning(); |
|
1710 } else { |
|
1711 out.println(); |
|
1712 } |
|
1713 |
|
1714 out.println(rb.getString("Keystore type: ") + keyStore.getType()); |
|
1715 out.println(rb.getString("Keystore provider: ") + |
|
1716 keyStore.getProvider().getName()); |
|
1717 out.println(); |
|
1718 |
|
1719 MessageFormat form; |
|
1720 form = (keyStore.size() == 1) ? |
|
1721 new MessageFormat(rb.getString |
|
1722 ("Your keystore contains keyStore.size() entry")) : |
|
1723 new MessageFormat(rb.getString |
|
1724 ("Your keystore contains keyStore.size() entries")); |
|
1725 Object[] source = {new Integer(keyStore.size())}; |
|
1726 out.println(form.format(source)); |
|
1727 out.println(); |
|
1728 |
|
1729 for (Enumeration<String> e = keyStore.aliases(); |
|
1730 e.hasMoreElements(); ) { |
|
1731 String alias = e.nextElement(); |
|
1732 doPrintEntry(alias, out, false); |
|
1733 if (verbose || rfc) { |
|
1734 out.println(rb.getString("\n")); |
|
1735 out.println(rb.getString |
|
1736 ("*******************************************")); |
|
1737 out.println(rb.getString |
|
1738 ("*******************************************\n\n")); |
|
1739 } |
|
1740 } |
|
1741 } |
|
1742 |
|
1743 /** |
|
1744 * Reads a certificate (or certificate chain) and prints its contents in |
|
1745 * a human readbable format. |
|
1746 */ |
|
1747 private void doPrintCert(InputStream in, PrintStream out) |
|
1748 throws Exception |
|
1749 { |
|
1750 Collection<? extends Certificate> c = null; |
|
1751 try { |
|
1752 c = cf.generateCertificates(in); |
|
1753 } catch (CertificateException ce) { |
|
1754 throw new Exception(rb.getString("Failed to parse input"), ce); |
|
1755 } |
|
1756 if (c.isEmpty()) { |
|
1757 throw new Exception(rb.getString("Empty input")); |
|
1758 } |
|
1759 Certificate[] certs = c.toArray(new Certificate[c.size()]); |
|
1760 for (int i=0; i<certs.length; i++) { |
|
1761 X509Certificate x509Cert = null; |
|
1762 try { |
|
1763 x509Cert = (X509Certificate)certs[i]; |
|
1764 } catch (ClassCastException cce) { |
|
1765 throw new Exception(rb.getString("Not X.509 certificate")); |
|
1766 } |
|
1767 if (certs.length > 1) { |
|
1768 MessageFormat form = new MessageFormat |
|
1769 (rb.getString("Certificate[(i + 1)]:")); |
|
1770 Object[] source = {new Integer(i + 1)}; |
|
1771 out.println(form.format(source)); |
|
1772 } |
|
1773 printX509Cert(x509Cert, out); |
|
1774 if (i < (certs.length-1)) { |
|
1775 out.println(); |
|
1776 } |
|
1777 } |
|
1778 } |
|
1779 |
|
1780 /** |
|
1781 * Creates a self-signed certificate, and stores it as a single-element |
|
1782 * certificate chain. |
|
1783 */ |
|
1784 private void doSelfCert(String alias, String dname, String sigAlgName) |
|
1785 throws Exception |
|
1786 { |
|
1787 if (alias == null) { |
|
1788 alias = keyAlias; |
|
1789 } |
|
1790 |
|
1791 Object[] objs = recoverKey(alias, storePass, keyPass); |
|
1792 PrivateKey privKey = (PrivateKey)objs[0]; |
|
1793 if (keyPass == null) |
|
1794 keyPass = (char[])objs[1]; |
|
1795 |
|
1796 // Determine the signature algorithm |
|
1797 if (sigAlgName == null) { |
|
1798 // If no signature algorithm was specified at the command line, |
|
1799 // we choose one that is compatible with the selected private key |
|
1800 String keyAlgName = privKey.getAlgorithm(); |
|
1801 if ("DSA".equalsIgnoreCase(keyAlgName) |
|
1802 || "DSS".equalsIgnoreCase(keyAlgName)) { |
|
1803 sigAlgName = "SHA1WithDSA"; |
|
1804 } else if ("RSA".equalsIgnoreCase(keyAlgName)) { |
|
1805 sigAlgName = "SHA1WithRSA"; |
|
1806 } else if ("EC".equalsIgnoreCase(keyAlgName)) { |
|
1807 sigAlgName = "SHA1withECDSA"; |
|
1808 } else { |
|
1809 throw new Exception |
|
1810 (rb.getString("Cannot derive signature algorithm")); |
|
1811 } |
|
1812 } |
|
1813 |
|
1814 // Get the old certificate |
|
1815 Certificate oldCert = keyStore.getCertificate(alias); |
|
1816 if (oldCert == null) { |
|
1817 MessageFormat form = new MessageFormat |
|
1818 (rb.getString("alias has no public key")); |
|
1819 Object[] source = {alias}; |
|
1820 throw new Exception(form.format(source)); |
|
1821 } |
|
1822 if (!(oldCert instanceof X509Certificate)) { |
|
1823 MessageFormat form = new MessageFormat |
|
1824 (rb.getString("alias has no X.509 certificate")); |
|
1825 Object[] source = {alias}; |
|
1826 throw new Exception(form.format(source)); |
|
1827 } |
|
1828 |
|
1829 // convert to X509CertImpl, so that we can modify selected fields |
|
1830 // (no public APIs available yet) |
|
1831 byte[] encoded = oldCert.getEncoded(); |
|
1832 X509CertImpl certImpl = new X509CertImpl(encoded); |
|
1833 X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME |
|
1834 + "." + |
|
1835 X509CertImpl.INFO); |
|
1836 |
|
1837 // Extend its validity |
|
1838 Date firstDate = getStartDate(startDate); |
|
1839 Date lastDate = new Date(); |
|
1840 lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); |
|
1841 CertificateValidity interval = new CertificateValidity(firstDate, |
|
1842 lastDate); |
|
1843 certInfo.set(X509CertInfo.VALIDITY, interval); |
|
1844 |
|
1845 // Make new serial number |
|
1846 certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber |
|
1847 ((int)(firstDate.getTime()/1000))); |
|
1848 |
|
1849 // Set owner and issuer fields |
|
1850 X500Name owner; |
|
1851 if (dname == null) { |
|
1852 // Get the owner name from the certificate |
|
1853 owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." + |
|
1854 CertificateSubjectName.DN_NAME); |
|
1855 } else { |
|
1856 // Use the owner name specified at the command line |
|
1857 owner = new X500Name(dname); |
|
1858 certInfo.set(X509CertInfo.SUBJECT + "." + |
|
1859 CertificateSubjectName.DN_NAME, owner); |
|
1860 } |
|
1861 // Make issuer same as owner (self-signed!) |
|
1862 certInfo.set(X509CertInfo.ISSUER + "." + |
|
1863 CertificateIssuerName.DN_NAME, owner); |
|
1864 |
|
1865 // The inner and outer signature algorithms have to match. |
|
1866 // The way we achieve that is really ugly, but there seems to be no |
|
1867 // other solution: We first sign the cert, then retrieve the |
|
1868 // outer sigalg and use it to set the inner sigalg |
|
1869 X509CertImpl newCert = new X509CertImpl(certInfo); |
|
1870 newCert.sign(privKey, sigAlgName); |
|
1871 AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG); |
|
1872 certInfo.set(CertificateAlgorithmId.NAME + "." + |
|
1873 CertificateAlgorithmId.ALGORITHM, sigAlgid); |
|
1874 |
|
1875 // first upgrade to version 3 |
|
1876 |
|
1877 certInfo.set(X509CertInfo.VERSION, |
|
1878 new CertificateVersion(CertificateVersion.V3)); |
|
1879 |
|
1880 // Sign the new certificate |
|
1881 newCert = new X509CertImpl(certInfo); |
|
1882 newCert.sign(privKey, sigAlgName); |
|
1883 |
|
1884 // Store the new certificate as a single-element certificate chain |
|
1885 keyStore.setKeyEntry(alias, privKey, |
|
1886 (keyPass != null) ? keyPass : storePass, |
|
1887 new Certificate[] { newCert } ); |
|
1888 |
|
1889 if (verbose) { |
|
1890 System.err.println(rb.getString("New certificate (self-signed):")); |
|
1891 System.err.print(newCert.toString()); |
|
1892 System.err.println(); |
|
1893 } |
|
1894 } |
|
1895 |
|
1896 /** |
|
1897 * Processes a certificate reply from a certificate authority. |
|
1898 * |
|
1899 * <p>Builds a certificate chain on top of the certificate reply, |
|
1900 * using trusted certificates from the keystore. The chain is complete |
|
1901 * after a self-signed certificate has been encountered. The self-signed |
|
1902 * certificate is considered a root certificate authority, and is stored |
|
1903 * at the end of the chain. |
|
1904 * |
|
1905 * <p>The newly generated chain replaces the old chain associated with the |
|
1906 * key entry. |
|
1907 * |
|
1908 * @return true if the certificate reply was installed, otherwise false. |
|
1909 */ |
|
1910 private boolean installReply(String alias, InputStream in) |
|
1911 throws Exception |
|
1912 { |
|
1913 if (alias == null) { |
|
1914 alias = keyAlias; |
|
1915 } |
|
1916 |
|
1917 Object[] objs = recoverKey(alias, storePass, keyPass); |
|
1918 PrivateKey privKey = (PrivateKey)objs[0]; |
|
1919 if (keyPass == null) { |
|
1920 keyPass = (char[])objs[1]; |
|
1921 } |
|
1922 |
|
1923 Certificate userCert = keyStore.getCertificate(alias); |
|
1924 if (userCert == null) { |
|
1925 MessageFormat form = new MessageFormat |
|
1926 (rb.getString("alias has no public key (certificate)")); |
|
1927 Object[] source = {alias}; |
|
1928 throw new Exception(form.format(source)); |
|
1929 } |
|
1930 |
|
1931 // Read the certificates in the reply |
|
1932 Collection<? extends Certificate> c = cf.generateCertificates(in); |
|
1933 if (c.isEmpty()) { |
|
1934 throw new Exception(rb.getString("Reply has no certificates")); |
|
1935 } |
|
1936 Certificate[] replyCerts = c.toArray(new Certificate[c.size()]); |
|
1937 Certificate[] newChain; |
|
1938 if (replyCerts.length == 1) { |
|
1939 // single-cert reply |
|
1940 newChain = establishCertChain(userCert, replyCerts[0]); |
|
1941 } else { |
|
1942 // cert-chain reply (e.g., PKCS#7) |
|
1943 newChain = validateReply(alias, userCert, replyCerts); |
|
1944 } |
|
1945 |
|
1946 // Now store the newly established chain in the keystore. The new |
|
1947 // chain replaces the old one. |
|
1948 if (newChain != null) { |
|
1949 keyStore.setKeyEntry(alias, privKey, |
|
1950 (keyPass != null) ? keyPass : storePass, |
|
1951 newChain); |
|
1952 return true; |
|
1953 } else { |
|
1954 return false; |
|
1955 } |
|
1956 } |
|
1957 |
|
1958 /** |
|
1959 * Imports a certificate and adds it to the list of trusted certificates. |
|
1960 * |
|
1961 * @return true if the certificate was added, otherwise false. |
|
1962 */ |
|
1963 private boolean addTrustedCert(String alias, InputStream in) |
|
1964 throws Exception |
|
1965 { |
|
1966 if (alias == null) { |
|
1967 throw new Exception(rb.getString("Must specify alias")); |
|
1968 } |
|
1969 if (keyStore.containsAlias(alias)) { |
|
1970 MessageFormat form = new MessageFormat(rb.getString |
|
1971 ("Certificate not imported, alias <alias> already exists")); |
|
1972 Object[] source = {alias}; |
|
1973 throw new Exception(form.format(source)); |
|
1974 } |
|
1975 |
|
1976 // Read the certificate |
|
1977 X509Certificate cert = null; |
|
1978 try { |
|
1979 cert = (X509Certificate)cf.generateCertificate(in); |
|
1980 } catch (ClassCastException cce) { |
|
1981 throw new Exception(rb.getString("Input not an X.509 certificate")); |
|
1982 } catch (CertificateException ce) { |
|
1983 throw new Exception(rb.getString("Input not an X.509 certificate")); |
|
1984 } |
|
1985 |
|
1986 // if certificate is self-signed, make sure it verifies |
|
1987 boolean selfSigned = false; |
|
1988 if (isSelfSigned(cert)) { |
|
1989 cert.verify(cert.getPublicKey()); |
|
1990 selfSigned = true; |
|
1991 } |
|
1992 |
|
1993 if (noprompt) { |
|
1994 keyStore.setCertificateEntry(alias, cert); |
|
1995 return true; |
|
1996 } |
|
1997 |
|
1998 // check if cert already exists in keystore |
|
1999 String reply = null; |
|
2000 String trustalias = keyStore.getCertificateAlias(cert); |
|
2001 if (trustalias != null) { |
|
2002 MessageFormat form = new MessageFormat(rb.getString |
|
2003 ("Certificate already exists in keystore under alias <trustalias>")); |
|
2004 Object[] source = {trustalias}; |
|
2005 System.err.println(form.format(source)); |
|
2006 reply = getYesNoReply |
|
2007 (rb.getString("Do you still want to add it? [no]: ")); |
|
2008 } else if (selfSigned) { |
|
2009 if (trustcacerts && (caks != null) && |
|
2010 ((trustalias=caks.getCertificateAlias(cert)) != null)) { |
|
2011 MessageFormat form = new MessageFormat(rb.getString |
|
2012 ("Certificate already exists in system-wide CA keystore under alias <trustalias>")); |
|
2013 Object[] source = {trustalias}; |
|
2014 System.err.println(form.format(source)); |
|
2015 reply = getYesNoReply |
|
2016 (rb.getString("Do you still want to add it to your own keystore? [no]: ")); |
|
2017 } |
|
2018 if (trustalias == null) { |
|
2019 // Print the cert and ask user if they really want to add |
|
2020 // it to their keystore |
|
2021 printX509Cert(cert, System.out); |
|
2022 reply = getYesNoReply |
|
2023 (rb.getString("Trust this certificate? [no]: ")); |
|
2024 } |
|
2025 } |
|
2026 if (reply != null) { |
|
2027 if ("YES".equals(reply)) { |
|
2028 keyStore.setCertificateEntry(alias, cert); |
|
2029 return true; |
|
2030 } else { |
|
2031 return false; |
|
2032 } |
|
2033 } |
|
2034 |
|
2035 // Try to establish trust chain |
|
2036 try { |
|
2037 Certificate[] chain = establishCertChain(null, cert); |
|
2038 if (chain != null) { |
|
2039 keyStore.setCertificateEntry(alias, cert); |
|
2040 return true; |
|
2041 } |
|
2042 } catch (Exception e) { |
|
2043 // Print the cert and ask user if they really want to add it to |
|
2044 // their keystore |
|
2045 printX509Cert(cert, System.out); |
|
2046 reply = getYesNoReply |
|
2047 (rb.getString("Trust this certificate? [no]: ")); |
|
2048 if ("YES".equals(reply)) { |
|
2049 keyStore.setCertificateEntry(alias, cert); |
|
2050 return true; |
|
2051 } else { |
|
2052 return false; |
|
2053 } |
|
2054 } |
|
2055 |
|
2056 return false; |
|
2057 } |
|
2058 |
|
2059 /** |
|
2060 * Prompts user for new password. New password must be different from |
|
2061 * old one. |
|
2062 * |
|
2063 * @param prompt the message that gets prompted on the screen |
|
2064 * @param oldPasswd the current (i.e., old) password |
|
2065 */ |
|
2066 private char[] getNewPasswd(String prompt, char[] oldPasswd) |
|
2067 throws Exception |
|
2068 { |
|
2069 char[] entered = null; |
|
2070 char[] reentered = null; |
|
2071 |
|
2072 for (int count = 0; count < 3; count++) { |
|
2073 MessageFormat form = new MessageFormat |
|
2074 (rb.getString("New prompt: ")); |
|
2075 Object[] source = {prompt}; |
|
2076 System.err.print(form.format(source)); |
|
2077 entered = Password.readPassword(System.in); |
|
2078 passwords.add(entered); |
|
2079 if (entered == null || entered.length < 6) { |
|
2080 System.err.println(rb.getString |
|
2081 ("Password is too short - must be at least 6 characters")); |
|
2082 } else if (Arrays.equals(entered, oldPasswd)) { |
|
2083 System.err.println(rb.getString("Passwords must differ")); |
|
2084 } else { |
|
2085 form = new MessageFormat |
|
2086 (rb.getString("Re-enter new prompt: ")); |
|
2087 Object[] src = {prompt}; |
|
2088 System.err.print(form.format(src)); |
|
2089 reentered = Password.readPassword(System.in); |
|
2090 passwords.add(reentered); |
|
2091 if (!Arrays.equals(entered, reentered)) { |
|
2092 System.err.println |
|
2093 (rb.getString("They don't match. Try again")); |
|
2094 } else { |
|
2095 Arrays.fill(reentered, ' '); |
|
2096 return entered; |
|
2097 } |
|
2098 } |
|
2099 if (entered != null) { |
|
2100 Arrays.fill(entered, ' '); |
|
2101 entered = null; |
|
2102 } |
|
2103 if (reentered != null) { |
|
2104 Arrays.fill(reentered, ' '); |
|
2105 reentered = null; |
|
2106 } |
|
2107 } |
|
2108 throw new Exception(rb.getString("Too many failures - try later")); |
|
2109 } |
|
2110 |
|
2111 /** |
|
2112 * Prompts user for alias name. |
|
2113 * @param prompt the {0} of "Enter {0} alias name: " in prompt line |
|
2114 * @returns the string entered by the user, without the \n at the end |
|
2115 */ |
|
2116 private String getAlias(String prompt) throws Exception { |
|
2117 if (prompt != null) { |
|
2118 MessageFormat form = new MessageFormat |
|
2119 (rb.getString("Enter prompt alias name: ")); |
|
2120 Object[] source = {prompt}; |
|
2121 System.err.print(form.format(source)); |
|
2122 } else { |
|
2123 System.err.print(rb.getString("Enter alias name: ")); |
|
2124 } |
|
2125 return (new BufferedReader(new InputStreamReader( |
|
2126 System.in))).readLine(); |
|
2127 } |
|
2128 |
|
2129 /** |
|
2130 * Prompts user for an input string from the command line (System.in) |
|
2131 * @prompt the prompt string printed |
|
2132 * @returns the string entered by the user, without the \n at the end |
|
2133 */ |
|
2134 private String inputStringFromStdin(String prompt) throws Exception { |
|
2135 System.err.print(prompt); |
|
2136 return (new BufferedReader(new InputStreamReader( |
|
2137 System.in))).readLine(); |
|
2138 } |
|
2139 |
|
2140 /** |
|
2141 * Prompts user for key password. User may select to choose the same |
|
2142 * password (<code>otherKeyPass</code>) as for <code>otherAlias</code>. |
|
2143 */ |
|
2144 private char[] getKeyPasswd(String alias, String otherAlias, |
|
2145 char[] otherKeyPass) |
|
2146 throws Exception |
|
2147 { |
|
2148 int count = 0; |
|
2149 char[] keyPass = null; |
|
2150 |
|
2151 do { |
|
2152 if (otherKeyPass != null) { |
|
2153 MessageFormat form = new MessageFormat(rb.getString |
|
2154 ("Enter key password for <alias>")); |
|
2155 Object[] source = {alias}; |
|
2156 System.err.println(form.format(source)); |
|
2157 |
|
2158 form = new MessageFormat(rb.getString |
|
2159 ("\t(RETURN if same as for <otherAlias>)")); |
|
2160 Object[] src = {otherAlias}; |
|
2161 System.err.print(form.format(src)); |
|
2162 } else { |
|
2163 MessageFormat form = new MessageFormat(rb.getString |
|
2164 ("Enter key password for <alias>")); |
|
2165 Object[] source = {alias}; |
|
2166 System.err.print(form.format(source)); |
|
2167 } |
|
2168 System.err.flush(); |
|
2169 keyPass = Password.readPassword(System.in); |
|
2170 passwords.add(keyPass); |
|
2171 if (keyPass == null) { |
|
2172 keyPass = otherKeyPass; |
|
2173 } |
|
2174 count++; |
|
2175 } while ((keyPass == null) && count < 3); |
|
2176 |
|
2177 if (keyPass == null) { |
|
2178 throw new Exception(rb.getString("Too many failures - try later")); |
|
2179 } |
|
2180 |
|
2181 return keyPass; |
|
2182 } |
|
2183 |
|
2184 /** |
|
2185 * Prints a certificate in a human readable format. |
|
2186 */ |
|
2187 private void printX509Cert(X509Certificate cert, PrintStream out) |
|
2188 throws Exception |
|
2189 { |
|
2190 /* |
|
2191 out.println("Owner: " |
|
2192 + cert.getSubjectDN().toString() |
|
2193 + "\n" |
|
2194 + "Issuer: " |
|
2195 + cert.getIssuerDN().toString() |
|
2196 + "\n" |
|
2197 + "Serial number: " + cert.getSerialNumber().toString(16) |
|
2198 + "\n" |
|
2199 + "Valid from: " + cert.getNotBefore().toString() |
|
2200 + " until: " + cert.getNotAfter().toString() |
|
2201 + "\n" |
|
2202 + "Certificate fingerprints:\n" |
|
2203 + "\t MD5: " + getCertFingerPrint("MD5", cert) |
|
2204 + "\n" |
|
2205 + "\t SHA1: " + getCertFingerPrint("SHA1", cert)); |
|
2206 */ |
|
2207 |
|
2208 MessageFormat form = new MessageFormat |
|
2209 (rb.getString("*PATTERN* printX509Cert")); |
|
2210 Object[] source = {cert.getSubjectDN().toString(), |
|
2211 cert.getIssuerDN().toString(), |
|
2212 cert.getSerialNumber().toString(16), |
|
2213 cert.getNotBefore().toString(), |
|
2214 cert.getNotAfter().toString(), |
|
2215 getCertFingerPrint("MD5", cert), |
|
2216 getCertFingerPrint("SHA1", cert), |
|
2217 cert.getSigAlgName(), |
|
2218 cert.getVersion() |
|
2219 }; |
|
2220 out.println(form.format(source)); |
|
2221 |
|
2222 int extnum = 0; |
|
2223 if (cert instanceof X509CertImpl) { |
|
2224 X509CertImpl impl = (X509CertImpl)cert; |
|
2225 if (cert.getCriticalExtensionOIDs() != null) { |
|
2226 for (String extOID : cert.getCriticalExtensionOIDs()) { |
|
2227 if (extnum == 0) { |
|
2228 out.println(); |
|
2229 out.println(rb.getString("Extensions: ")); |
|
2230 out.println(); |
|
2231 } |
|
2232 out.println("#"+(++extnum)+": "+ |
|
2233 impl.getExtension(new ObjectIdentifier(extOID))); |
|
2234 } |
|
2235 } |
|
2236 if (cert.getNonCriticalExtensionOIDs() != null) { |
|
2237 for (String extOID : cert.getNonCriticalExtensionOIDs()) { |
|
2238 if (extnum == 0) { |
|
2239 out.println(); |
|
2240 out.println(rb.getString("Extensions: ")); |
|
2241 out.println(); |
|
2242 } |
|
2243 Extension ext = impl.getExtension(new ObjectIdentifier(extOID)); |
|
2244 if (ext != null) { |
|
2245 out.println("#"+(++extnum)+": "+ ext); |
|
2246 } else { |
|
2247 out.println("#"+(++extnum)+": "+ |
|
2248 impl.getUnparseableExtension(new ObjectIdentifier(extOID))); |
|
2249 } |
|
2250 } |
|
2251 } |
|
2252 } |
|
2253 } |
|
2254 |
|
2255 /** |
|
2256 * Returns true if the certificate is self-signed, false otherwise. |
|
2257 */ |
|
2258 private boolean isSelfSigned(X509Certificate cert) { |
|
2259 return cert.getSubjectDN().equals(cert.getIssuerDN()); |
|
2260 } |
|
2261 |
|
2262 /** |
|
2263 * Returns true if the given certificate is trusted, false otherwise. |
|
2264 */ |
|
2265 private boolean isTrusted(Certificate cert) |
|
2266 throws Exception |
|
2267 { |
|
2268 if (keyStore.getCertificateAlias(cert) != null) { |
|
2269 return true; // found in own keystore |
|
2270 } |
|
2271 if (trustcacerts && (caks != null) && |
|
2272 (caks.getCertificateAlias(cert) != null)) { |
|
2273 return true; // found in CA keystore |
|
2274 } |
|
2275 return false; |
|
2276 } |
|
2277 |
|
2278 /** |
|
2279 * Gets an X.500 name suitable for inclusion in a certification request. |
|
2280 */ |
|
2281 private X500Name getX500Name() throws IOException { |
|
2282 BufferedReader in; |
|
2283 in = new BufferedReader(new InputStreamReader(System.in)); |
|
2284 String commonName = "Unknown"; |
|
2285 String organizationalUnit = "Unknown"; |
|
2286 String organization = "Unknown"; |
|
2287 String city = "Unknown"; |
|
2288 String state = "Unknown"; |
|
2289 String country = "Unknown"; |
|
2290 X500Name name; |
|
2291 String userInput = null; |
|
2292 |
|
2293 int maxRetry = 20; |
|
2294 do { |
|
2295 if (maxRetry-- < 0) { |
|
2296 throw new RuntimeException(rb.getString( |
|
2297 "Too may retries, program terminated")); |
|
2298 } |
|
2299 commonName = inputString(in, |
|
2300 rb.getString("What is your first and last name?"), |
|
2301 commonName); |
|
2302 organizationalUnit = inputString(in, |
|
2303 rb.getString |
|
2304 ("What is the name of your organizational unit?"), |
|
2305 organizationalUnit); |
|
2306 organization = inputString(in, |
|
2307 rb.getString("What is the name of your organization?"), |
|
2308 organization); |
|
2309 city = inputString(in, |
|
2310 rb.getString("What is the name of your City or Locality?"), |
|
2311 city); |
|
2312 state = inputString(in, |
|
2313 rb.getString("What is the name of your State or Province?"), |
|
2314 state); |
|
2315 country = inputString(in, |
|
2316 rb.getString |
|
2317 ("What is the two-letter country code for this unit?"), |
|
2318 country); |
|
2319 name = new X500Name(commonName, organizationalUnit, organization, |
|
2320 city, state, country); |
|
2321 MessageFormat form = new MessageFormat |
|
2322 (rb.getString("Is <name> correct?")); |
|
2323 Object[] source = {name}; |
|
2324 userInput = inputString |
|
2325 (in, form.format(source), rb.getString("no")); |
|
2326 } while (collator.compare(userInput, rb.getString("yes")) != 0 && |
|
2327 collator.compare(userInput, rb.getString("y")) != 0); |
|
2328 |
|
2329 System.err.println(); |
|
2330 return name; |
|
2331 } |
|
2332 |
|
2333 private String inputString(BufferedReader in, String prompt, |
|
2334 String defaultValue) |
|
2335 throws IOException |
|
2336 { |
|
2337 System.err.println(prompt); |
|
2338 MessageFormat form = new MessageFormat |
|
2339 (rb.getString(" [defaultValue]: ")); |
|
2340 Object[] source = {defaultValue}; |
|
2341 System.err.print(form.format(source)); |
|
2342 System.err.flush(); |
|
2343 |
|
2344 String value = in.readLine(); |
|
2345 if (value == null || collator.compare(value, "") == 0) { |
|
2346 value = defaultValue; |
|
2347 } |
|
2348 return value; |
|
2349 } |
|
2350 |
|
2351 /** |
|
2352 * Writes an X.509 certificate in base64 or binary encoding to an output |
|
2353 * stream. |
|
2354 */ |
|
2355 private void dumpCert(Certificate cert, PrintStream out) |
|
2356 throws IOException, CertificateException |
|
2357 { |
|
2358 if (rfc) { |
|
2359 BASE64Encoder encoder = new BASE64Encoder(); |
|
2360 out.println(X509Factory.BEGIN_CERT); |
|
2361 encoder.encodeBuffer(cert.getEncoded(), out); |
|
2362 out.println(X509Factory.END_CERT); |
|
2363 } else { |
|
2364 out.write(cert.getEncoded()); // binary |
|
2365 } |
|
2366 } |
|
2367 |
|
2368 /** |
|
2369 * Converts a byte to hex digit and writes to the supplied buffer |
|
2370 */ |
|
2371 private void byte2hex(byte b, StringBuffer buf) { |
|
2372 char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', |
|
2373 '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
|
2374 int high = ((b & 0xf0) >> 4); |
|
2375 int low = (b & 0x0f); |
|
2376 buf.append(hexChars[high]); |
|
2377 buf.append(hexChars[low]); |
|
2378 } |
|
2379 |
|
2380 /** |
|
2381 * Converts a byte array to hex string |
|
2382 */ |
|
2383 private String toHexString(byte[] block) { |
|
2384 StringBuffer buf = new StringBuffer(); |
|
2385 int len = block.length; |
|
2386 for (int i = 0; i < len; i++) { |
|
2387 byte2hex(block[i], buf); |
|
2388 if (i < len-1) { |
|
2389 buf.append(":"); |
|
2390 } |
|
2391 } |
|
2392 return buf.toString(); |
|
2393 } |
|
2394 |
|
2395 /** |
|
2396 * Recovers (private) key associated with given alias. |
|
2397 * |
|
2398 * @return an array of objects, where the 1st element in the array is the |
|
2399 * recovered private key, and the 2nd element is the password used to |
|
2400 * recover it. |
|
2401 */ |
|
2402 private Object[] recoverKey(String alias, char[] storePass, |
|
2403 char[] keyPass) |
|
2404 throws Exception |
|
2405 { |
|
2406 Key key = null; |
|
2407 |
|
2408 if (keyStore.containsAlias(alias) == false) { |
|
2409 MessageFormat form = new MessageFormat |
|
2410 (rb.getString("Alias <alias> does not exist")); |
|
2411 Object[] source = {alias}; |
|
2412 throw new Exception(form.format(source)); |
|
2413 } |
|
2414 if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) && |
|
2415 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { |
|
2416 MessageFormat form = new MessageFormat |
|
2417 (rb.getString("Alias <alias> has no key")); |
|
2418 Object[] source = {alias}; |
|
2419 throw new Exception(form.format(source)); |
|
2420 } |
|
2421 |
|
2422 if (keyPass == null) { |
|
2423 // Try to recover the key using the keystore password |
|
2424 try { |
|
2425 key = keyStore.getKey(alias, storePass); |
|
2426 |
|
2427 keyPass = storePass; |
|
2428 passwords.add(keyPass); |
|
2429 } catch (UnrecoverableKeyException e) { |
|
2430 // Did not work out, so prompt user for key password |
|
2431 if (!token) { |
|
2432 keyPass = getKeyPasswd(alias, null, null); |
|
2433 key = keyStore.getKey(alias, keyPass); |
|
2434 } else { |
|
2435 throw e; |
|
2436 } |
|
2437 } |
|
2438 } else { |
|
2439 key = keyStore.getKey(alias, keyPass); |
|
2440 } |
|
2441 |
|
2442 return new Object[] {key, keyPass}; |
|
2443 } |
|
2444 |
|
2445 /** |
|
2446 * Recovers entry associated with given alias. |
|
2447 * |
|
2448 * @return an array of objects, where the 1st element in the array is the |
|
2449 * recovered entry, and the 2nd element is the password used to |
|
2450 * recover it (null if no password). |
|
2451 */ |
|
2452 private Object[] recoverEntry(KeyStore ks, |
|
2453 String alias, |
|
2454 char[] pstore, |
|
2455 char[] pkey) throws Exception { |
|
2456 |
|
2457 if (ks.containsAlias(alias) == false) { |
|
2458 MessageFormat form = new MessageFormat |
|
2459 (rb.getString("Alias <alias> does not exist")); |
|
2460 Object[] source = {alias}; |
|
2461 throw new Exception(form.format(source)); |
|
2462 } |
|
2463 |
|
2464 PasswordProtection pp = null; |
|
2465 Entry entry; |
|
2466 |
|
2467 try { |
|
2468 // First attempt to access entry without key password |
|
2469 // (PKCS11 entry or trusted certificate entry, for example) |
|
2470 |
|
2471 entry = ks.getEntry(alias, pp); |
|
2472 pkey = null; |
|
2473 } catch (UnrecoverableEntryException une) { |
|
2474 |
|
2475 if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) || |
|
2476 KeyStoreUtil.isWindowsKeyStore(ks.getType())) { |
|
2477 // should not happen, but a possibility |
|
2478 throw une; |
|
2479 } |
|
2480 |
|
2481 // entry is protected |
|
2482 |
|
2483 if (pkey != null) { |
|
2484 |
|
2485 // try provided key password |
|
2486 |
|
2487 pp = new PasswordProtection(pkey); |
|
2488 entry = ks.getEntry(alias, pp); |
|
2489 |
|
2490 } else { |
|
2491 |
|
2492 // try store pass |
|
2493 |
|
2494 try { |
|
2495 pp = new PasswordProtection(pstore); |
|
2496 entry = ks.getEntry(alias, pp); |
|
2497 pkey = pstore; |
|
2498 } catch (UnrecoverableEntryException une2) { |
|
2499 if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) { |
|
2500 |
|
2501 // P12 keystore currently does not support separate |
|
2502 // store and entry passwords |
|
2503 |
|
2504 throw une2; |
|
2505 } else { |
|
2506 |
|
2507 // prompt for entry password |
|
2508 |
|
2509 pkey = getKeyPasswd(alias, null, null); |
|
2510 pp = new PasswordProtection(pkey); |
|
2511 entry = ks.getEntry(alias, pp); |
|
2512 } |
|
2513 } |
|
2514 } |
|
2515 } |
|
2516 |
|
2517 return new Object[] {entry, pkey}; |
|
2518 } |
|
2519 /** |
|
2520 * Gets the requested finger print of the certificate. |
|
2521 */ |
|
2522 private String getCertFingerPrint(String mdAlg, Certificate cert) |
|
2523 throws Exception |
|
2524 { |
|
2525 byte[] encCertInfo = cert.getEncoded(); |
|
2526 MessageDigest md = MessageDigest.getInstance(mdAlg); |
|
2527 byte[] digest = md.digest(encCertInfo); |
|
2528 return toHexString(digest); |
|
2529 } |
|
2530 |
|
2531 /** |
|
2532 * Prints warning about missing integrity check. |
|
2533 */ |
|
2534 private void printWarning() { |
|
2535 System.err.println(); |
|
2536 System.err.println(rb.getString |
|
2537 ("***************** WARNING WARNING WARNING *****************")); |
|
2538 System.err.println(rb.getString |
|
2539 ("* The integrity of the information stored in your keystore *")); |
|
2540 System.err.println(rb.getString |
|
2541 ("* has NOT been verified! In order to verify its integrity, *")); |
|
2542 System.err.println(rb.getString |
|
2543 ("* you must provide your keystore password. *")); |
|
2544 System.err.println(rb.getString |
|
2545 ("***************** WARNING WARNING WARNING *****************")); |
|
2546 System.err.println(); |
|
2547 } |
|
2548 |
|
2549 /** |
|
2550 * Validates chain in certification reply, and returns the ordered |
|
2551 * elements of the chain (with user certificate first, and root |
|
2552 * certificate last in the array). |
|
2553 * |
|
2554 * @param alias the alias name |
|
2555 * @param userCert the user certificate of the alias |
|
2556 * @param replyCerts the chain provided in the reply |
|
2557 */ |
|
2558 private Certificate[] validateReply(String alias, |
|
2559 Certificate userCert, |
|
2560 Certificate[] replyCerts) |
|
2561 throws Exception |
|
2562 { |
|
2563 // order the certs in the reply (bottom-up). |
|
2564 // we know that all certs in the reply are of type X.509, because |
|
2565 // we parsed them using an X.509 certificate factory |
|
2566 int i; |
|
2567 PublicKey userPubKey = userCert.getPublicKey(); |
|
2568 for (i=0; i<replyCerts.length; i++) { |
|
2569 if (userPubKey.equals(replyCerts[i].getPublicKey())) { |
|
2570 break; |
|
2571 } |
|
2572 } |
|
2573 if (i == replyCerts.length) { |
|
2574 MessageFormat form = new MessageFormat(rb.getString |
|
2575 ("Certificate reply does not contain public key for <alias>")); |
|
2576 Object[] source = {alias}; |
|
2577 throw new Exception(form.format(source)); |
|
2578 } |
|
2579 |
|
2580 Certificate tmpCert = replyCerts[0]; |
|
2581 replyCerts[0] = replyCerts[i]; |
|
2582 replyCerts[i] = tmpCert; |
|
2583 Principal issuer = ((X509Certificate)replyCerts[0]).getIssuerDN(); |
|
2584 |
|
2585 for (i=1; i < replyCerts.length-1; i++) { |
|
2586 // find a cert in the reply whose "subject" is the same as the |
|
2587 // given "issuer" |
|
2588 int j; |
|
2589 for (j=i; j<replyCerts.length; j++) { |
|
2590 Principal subject; |
|
2591 subject = ((X509Certificate)replyCerts[j]).getSubjectDN(); |
|
2592 if (subject.equals(issuer)) { |
|
2593 tmpCert = replyCerts[i]; |
|
2594 replyCerts[i] = replyCerts[j]; |
|
2595 replyCerts[j] = tmpCert; |
|
2596 issuer = ((X509Certificate)replyCerts[i]).getIssuerDN(); |
|
2597 break; |
|
2598 } |
|
2599 } |
|
2600 if (j == replyCerts.length) { |
|
2601 throw new Exception |
|
2602 (rb.getString("Incomplete certificate chain in reply")); |
|
2603 } |
|
2604 } |
|
2605 |
|
2606 // now verify each cert in the ordered chain |
|
2607 for (i=0; i<replyCerts.length-1; i++) { |
|
2608 PublicKey pubKey = replyCerts[i+1].getPublicKey(); |
|
2609 try { |
|
2610 replyCerts[i].verify(pubKey); |
|
2611 } catch (Exception e) { |
|
2612 throw new Exception(rb.getString |
|
2613 ("Certificate chain in reply does not verify: ") + |
|
2614 e.getMessage()); |
|
2615 } |
|
2616 } |
|
2617 |
|
2618 if (noprompt) { |
|
2619 return replyCerts; |
|
2620 } |
|
2621 |
|
2622 // do we trust the (root) cert at the top? |
|
2623 Certificate topCert = replyCerts[replyCerts.length-1]; |
|
2624 if (!isTrusted(topCert)) { |
|
2625 boolean verified = false; |
|
2626 Certificate rootCert = null; |
|
2627 if (trustcacerts && (caks!= null)) { |
|
2628 for (Enumeration<String> aliases = caks.aliases(); |
|
2629 aliases.hasMoreElements(); ) { |
|
2630 String name = aliases.nextElement(); |
|
2631 rootCert = caks.getCertificate(name); |
|
2632 if (rootCert != null) { |
|
2633 try { |
|
2634 topCert.verify(rootCert.getPublicKey()); |
|
2635 verified = true; |
|
2636 break; |
|
2637 } catch (Exception e) { |
|
2638 } |
|
2639 } |
|
2640 } |
|
2641 } |
|
2642 if (!verified) { |
|
2643 System.err.println(); |
|
2644 System.err.println |
|
2645 (rb.getString("Top-level certificate in reply:\n")); |
|
2646 printX509Cert((X509Certificate)topCert, System.out); |
|
2647 System.err.println(); |
|
2648 System.err.print(rb.getString("... is not trusted. ")); |
|
2649 String reply = getYesNoReply |
|
2650 (rb.getString("Install reply anyway? [no]: ")); |
|
2651 if ("NO".equals(reply)) { |
|
2652 return null; |
|
2653 } |
|
2654 } else { |
|
2655 if (!isSelfSigned((X509Certificate)topCert)) { |
|
2656 // append the (self-signed) root CA cert to the chain |
|
2657 Certificate[] tmpCerts = |
|
2658 new Certificate[replyCerts.length+1]; |
|
2659 System.arraycopy(replyCerts, 0, tmpCerts, 0, |
|
2660 replyCerts.length); |
|
2661 tmpCerts[tmpCerts.length-1] = rootCert; |
|
2662 replyCerts = tmpCerts; |
|
2663 } |
|
2664 } |
|
2665 } |
|
2666 |
|
2667 return replyCerts; |
|
2668 } |
|
2669 |
|
2670 /** |
|
2671 * Establishes a certificate chain (using trusted certificates in the |
|
2672 * keystore), starting with the user certificate |
|
2673 * and ending at a self-signed certificate found in the keystore. |
|
2674 * |
|
2675 * @param userCert the user certificate of the alias |
|
2676 * @param certToVerify the single certificate provided in the reply |
|
2677 */ |
|
2678 private Certificate[] establishCertChain(Certificate userCert, |
|
2679 Certificate certToVerify) |
|
2680 throws Exception |
|
2681 { |
|
2682 if (userCert != null) { |
|
2683 // Make sure that the public key of the certificate reply matches |
|
2684 // the original public key in the keystore |
|
2685 PublicKey origPubKey = userCert.getPublicKey(); |
|
2686 PublicKey replyPubKey = certToVerify.getPublicKey(); |
|
2687 if (!origPubKey.equals(replyPubKey)) { |
|
2688 throw new Exception(rb.getString |
|
2689 ("Public keys in reply and keystore don't match")); |
|
2690 } |
|
2691 |
|
2692 // If the two certs are identical, we're done: no need to import |
|
2693 // anything |
|
2694 if (certToVerify.equals(userCert)) { |
|
2695 throw new Exception(rb.getString |
|
2696 ("Certificate reply and certificate in keystore are identical")); |
|
2697 } |
|
2698 } |
|
2699 |
|
2700 // Build a hash table of all certificates in the keystore. |
|
2701 // Use the subject distinguished name as the key into the hash table. |
|
2702 // All certificates associated with the same subject distinguished |
|
2703 // name are stored in the same hash table entry as a vector. |
|
2704 Hashtable<Principal, Vector<Certificate>> certs = null; |
|
2705 if (keyStore.size() > 0) { |
|
2706 certs = new Hashtable<Principal, Vector<Certificate>>(11); |
|
2707 keystorecerts2Hashtable(keyStore, certs); |
|
2708 } |
|
2709 if (trustcacerts) { |
|
2710 if (caks!=null && caks.size()>0) { |
|
2711 if (certs == null) { |
|
2712 certs = new Hashtable<Principal, Vector<Certificate>>(11); |
|
2713 } |
|
2714 keystorecerts2Hashtable(caks, certs); |
|
2715 } |
|
2716 } |
|
2717 |
|
2718 // start building chain |
|
2719 Vector<Certificate> chain = new Vector<Certificate>(2); |
|
2720 if (buildChain((X509Certificate)certToVerify, chain, certs)) { |
|
2721 Certificate[] newChain = new Certificate[chain.size()]; |
|
2722 // buildChain() returns chain with self-signed root-cert first and |
|
2723 // user-cert last, so we need to invert the chain before we store |
|
2724 // it |
|
2725 int j=0; |
|
2726 for (int i=chain.size()-1; i>=0; i--) { |
|
2727 newChain[j] = chain.elementAt(i); |
|
2728 j++; |
|
2729 } |
|
2730 return newChain; |
|
2731 } else { |
|
2732 throw new Exception |
|
2733 (rb.getString("Failed to establish chain from reply")); |
|
2734 } |
|
2735 } |
|
2736 |
|
2737 /** |
|
2738 * Recursively tries to establish chain from pool of trusted certs. |
|
2739 * |
|
2740 * @param certToVerify the cert that needs to be verified. |
|
2741 * @param chain the chain that's being built. |
|
2742 * @param certs the pool of trusted certs |
|
2743 * |
|
2744 * @return true if successful, false otherwise. |
|
2745 */ |
|
2746 private boolean buildChain(X509Certificate certToVerify, |
|
2747 Vector<Certificate> chain, |
|
2748 Hashtable<Principal, Vector<Certificate>> certs) { |
|
2749 Principal subject = certToVerify.getSubjectDN(); |
|
2750 Principal issuer = certToVerify.getIssuerDN(); |
|
2751 if (subject.equals(issuer)) { |
|
2752 // reached self-signed root cert; |
|
2753 // no verification needed because it's trusted. |
|
2754 chain.addElement(certToVerify); |
|
2755 return true; |
|
2756 } |
|
2757 |
|
2758 // Get the issuer's certificate(s) |
|
2759 Vector<Certificate> vec = certs.get(issuer); |
|
2760 if (vec == null) { |
|
2761 return false; |
|
2762 } |
|
2763 |
|
2764 // Try out each certificate in the vector, until we find one |
|
2765 // whose public key verifies the signature of the certificate |
|
2766 // in question. |
|
2767 for (Enumeration<Certificate> issuerCerts = vec.elements(); |
|
2768 issuerCerts.hasMoreElements(); ) { |
|
2769 X509Certificate issuerCert |
|
2770 = (X509Certificate)issuerCerts.nextElement(); |
|
2771 PublicKey issuerPubKey = issuerCert.getPublicKey(); |
|
2772 try { |
|
2773 certToVerify.verify(issuerPubKey); |
|
2774 } catch (Exception e) { |
|
2775 continue; |
|
2776 } |
|
2777 if (buildChain(issuerCert, chain, certs)) { |
|
2778 chain.addElement(certToVerify); |
|
2779 return true; |
|
2780 } |
|
2781 } |
|
2782 return false; |
|
2783 } |
|
2784 |
|
2785 /** |
|
2786 * Prompts user for yes/no decision. |
|
2787 * |
|
2788 * @return the user's decision, can only be "YES" or "NO" |
|
2789 */ |
|
2790 private String getYesNoReply(String prompt) |
|
2791 throws IOException |
|
2792 { |
|
2793 String reply = null; |
|
2794 int maxRetry = 20; |
|
2795 do { |
|
2796 if (maxRetry-- < 0) { |
|
2797 throw new RuntimeException(rb.getString( |
|
2798 "Too may retries, program terminated")); |
|
2799 } |
|
2800 System.err.print(prompt); |
|
2801 System.err.flush(); |
|
2802 reply = (new BufferedReader(new InputStreamReader |
|
2803 (System.in))).readLine(); |
|
2804 if (collator.compare(reply, "") == 0 || |
|
2805 collator.compare(reply, rb.getString("n")) == 0 || |
|
2806 collator.compare(reply, rb.getString("no")) == 0) { |
|
2807 reply = "NO"; |
|
2808 } else if (collator.compare(reply, rb.getString("y")) == 0 || |
|
2809 collator.compare(reply, rb.getString("yes")) == 0) { |
|
2810 reply = "YES"; |
|
2811 } else { |
|
2812 System.err.println(rb.getString("Wrong answer, try again")); |
|
2813 reply = null; |
|
2814 } |
|
2815 } while (reply == null); |
|
2816 return reply; |
|
2817 } |
|
2818 |
|
2819 /** |
|
2820 * Returns the keystore with the configured CA certificates. |
|
2821 */ |
|
2822 private KeyStore getCacertsKeyStore() |
|
2823 throws Exception |
|
2824 { |
|
2825 String sep = File.separator; |
|
2826 File file = new File(System.getProperty("java.home") + sep |
|
2827 + "lib" + sep + "security" + sep |
|
2828 + "cacerts"); |
|
2829 if (!file.exists()) { |
|
2830 return null; |
|
2831 } |
|
2832 FileInputStream fis = null; |
|
2833 KeyStore caks = null; |
|
2834 try { |
|
2835 fis = new FileInputStream(file); |
|
2836 caks = KeyStore.getInstance(JKS); |
|
2837 caks.load(fis, null); |
|
2838 } finally { |
|
2839 if (fis != null) { |
|
2840 fis.close(); |
|
2841 } |
|
2842 } |
|
2843 return caks; |
|
2844 } |
|
2845 |
|
2846 /** |
|
2847 * Stores the (leaf) certificates of a keystore in a hashtable. |
|
2848 * All certs belonging to the same CA are stored in a vector that |
|
2849 * in turn is stored in the hashtable, keyed by the CA's subject DN |
|
2850 */ |
|
2851 private void keystorecerts2Hashtable(KeyStore ks, |
|
2852 Hashtable<Principal, Vector<Certificate>> hash) |
|
2853 throws Exception { |
|
2854 |
|
2855 for (Enumeration<String> aliases = ks.aliases(); |
|
2856 aliases.hasMoreElements(); ) { |
|
2857 String alias = aliases.nextElement(); |
|
2858 Certificate cert = ks.getCertificate(alias); |
|
2859 if (cert != null) { |
|
2860 Principal subjectDN = ((X509Certificate)cert).getSubjectDN(); |
|
2861 Vector<Certificate> vec = hash.get(subjectDN); |
|
2862 if (vec == null) { |
|
2863 vec = new Vector<Certificate>(); |
|
2864 vec.addElement(cert); |
|
2865 } else { |
|
2866 if (!vec.contains(cert)) { |
|
2867 vec.addElement(cert); |
|
2868 } |
|
2869 } |
|
2870 hash.put(subjectDN, vec); |
|
2871 } |
|
2872 } |
|
2873 } |
|
2874 |
|
2875 /** |
|
2876 * Returns the issue time that's specified the -startdate option |
|
2877 * @param s the value of -startdate option |
|
2878 */ |
|
2879 private static Date getStartDate(String s) throws IOException { |
|
2880 Calendar c = new GregorianCalendar(); |
|
2881 if (s != null) { |
|
2882 IOException ioe = new IOException( |
|
2883 rb.getString("Illegal startdate value")); |
|
2884 int len = s.length(); |
|
2885 if (len == 0) { |
|
2886 throw ioe; |
|
2887 } |
|
2888 if (s.charAt(0) == '-' || s.charAt(0) == '+') { |
|
2889 // Form 1: ([+-]nnn[ymdHMS])+ |
|
2890 int start = 0; |
|
2891 while (start < len) { |
|
2892 int sign = 0; |
|
2893 switch (s.charAt(start)) { |
|
2894 case '+': sign = 1; break; |
|
2895 case '-': sign = -1; break; |
|
2896 default: throw ioe; |
|
2897 } |
|
2898 int i = start+1; |
|
2899 for (; i<len; i++) { |
|
2900 char ch = s.charAt(i); |
|
2901 if (ch < '0' || ch > '9') break; |
|
2902 } |
|
2903 if (i == start+1) throw ioe; |
|
2904 int number = Integer.parseInt(s.substring(start+1, i)); |
|
2905 if (i >= len) throw ioe; |
|
2906 int unit = 0; |
|
2907 switch (s.charAt(i)) { |
|
2908 case 'y': unit = Calendar.YEAR; break; |
|
2909 case 'm': unit = Calendar.MONTH; break; |
|
2910 case 'd': unit = Calendar.DATE; break; |
|
2911 case 'H': unit = Calendar.HOUR; break; |
|
2912 case 'M': unit = Calendar.MINUTE; break; |
|
2913 case 'S': unit = Calendar.SECOND; break; |
|
2914 default: throw ioe; |
|
2915 } |
|
2916 c.add(unit, sign * number); |
|
2917 start = i + 1; |
|
2918 } |
|
2919 } else { |
|
2920 // Form 2: [yyyy/mm/dd] [HH:MM:SS] |
|
2921 String date = null, time = null; |
|
2922 if (len == 19) { |
|
2923 date = s.substring(0, 10); |
|
2924 time = s.substring(11); |
|
2925 if (s.charAt(10) != ' ') |
|
2926 throw ioe; |
|
2927 } else if (len == 10) { |
|
2928 date = s; |
|
2929 } else if (len == 8) { |
|
2930 time = s; |
|
2931 } else { |
|
2932 throw ioe; |
|
2933 } |
|
2934 if (date != null) { |
|
2935 if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) { |
|
2936 c.set(Integer.valueOf(date.substring(0, 4)), |
|
2937 Integer.valueOf(date.substring(5, 7))-1, |
|
2938 Integer.valueOf(date.substring(8, 10))); |
|
2939 } else { |
|
2940 throw ioe; |
|
2941 } |
|
2942 } |
|
2943 if (time != null) { |
|
2944 if (time.matches("\\d\\d:\\d\\d:\\d\\d")) { |
|
2945 c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2))); |
|
2946 c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2))); |
|
2947 c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2))); |
|
2948 c.set(Calendar.MILLISECOND, 0); |
|
2949 } else { |
|
2950 throw ioe; |
|
2951 } |
|
2952 } |
|
2953 } |
|
2954 } |
|
2955 return c.getTime(); |
|
2956 } |
|
2957 |
|
2958 /** |
|
2959 * Prints the usage of this tool. |
|
2960 */ |
|
2961 private void usage() { |
|
2962 System.err.println(rb.getString("keytool usage:\n")); |
|
2963 |
|
2964 System.err.println(rb.getString |
|
2965 ("-certreq [-v] [-protected]")); |
|
2966 System.err.println(rb.getString |
|
2967 ("\t [-alias <alias>] [-sigalg <sigalg>]")); |
|
2968 System.err.println(rb.getString |
|
2969 ("\t [-file <csr_file>] [-keypass <keypass>]")); |
|
2970 System.err.println(rb.getString |
|
2971 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
2972 System.err.println(rb.getString |
|
2973 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
2974 System.err.println(rb.getString |
|
2975 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
2976 System.err.println(rb.getString |
|
2977 ("\t [-providerpath <pathlist>]")); |
|
2978 System.err.println(); |
|
2979 |
|
2980 System.err.println(rb.getString |
|
2981 ("-changealias [-v] [-protected] -alias <alias> -destalias <destalias>")); |
|
2982 System.err.println(rb.getString |
|
2983 ("\t [-keypass <keypass>]")); |
|
2984 System.err.println(rb.getString |
|
2985 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
2986 System.err.println(rb.getString |
|
2987 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
2988 System.err.println(rb.getString |
|
2989 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
2990 System.err.println(rb.getString |
|
2991 ("\t [-providerpath <pathlist>]")); |
|
2992 System.err.println(); |
|
2993 |
|
2994 System.err.println(rb.getString |
|
2995 ("-delete [-v] [-protected] -alias <alias>")); |
|
2996 System.err.println(rb.getString |
|
2997 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
2998 System.err.println(rb.getString |
|
2999 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3000 System.err.println(rb.getString |
|
3001 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3002 System.err.println(rb.getString |
|
3003 ("\t [-providerpath <pathlist>]")); |
|
3004 System.err.println(); |
|
3005 |
|
3006 System.err.println(rb.getString |
|
3007 ("-exportcert [-v] [-rfc] [-protected]")); |
|
3008 System.err.println(rb.getString |
|
3009 ("\t [-alias <alias>] [-file <cert_file>]")); |
|
3010 System.err.println(rb.getString |
|
3011 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3012 System.err.println(rb.getString |
|
3013 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3014 System.err.println(rb.getString |
|
3015 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3016 System.err.println(rb.getString |
|
3017 ("\t [-providerpath <pathlist>]")); |
|
3018 System.err.println(); |
|
3019 |
|
3020 System.err.println(rb.getString |
|
3021 ("-genkeypair [-v] [-protected]")); |
|
3022 System.err.println(rb.getString |
|
3023 ("\t [-alias <alias>]")); |
|
3024 System.err.println(rb.getString |
|
3025 ("\t [-keyalg <keyalg>] [-keysize <keysize>]")); |
|
3026 System.err.println(rb.getString |
|
3027 ("\t [-sigalg <sigalg>] [-dname <dname>]")); |
|
3028 System.err.println(rb.getString |
|
3029 ("\t [-startdate <startdate>]")); |
|
3030 System.err.println(rb.getString |
|
3031 ("\t [-validity <valDays>] [-keypass <keypass>]")); |
|
3032 System.err.println(rb.getString |
|
3033 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3034 System.err.println(rb.getString |
|
3035 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3036 System.err.println(rb.getString |
|
3037 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3038 System.err.println(rb.getString |
|
3039 ("\t [-providerpath <pathlist>]")); |
|
3040 System.err.println(); |
|
3041 |
|
3042 System.err.println(rb.getString |
|
3043 ("-genseckey [-v] [-protected]")); |
|
3044 System.err.println(rb.getString |
|
3045 ("\t [-alias <alias>] [-keypass <keypass>]")); |
|
3046 System.err.println(rb.getString |
|
3047 ("\t [-keyalg <keyalg>] [-keysize <keysize>]")); |
|
3048 System.err.println(rb.getString |
|
3049 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3050 System.err.println(rb.getString |
|
3051 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3052 System.err.println(rb.getString |
|
3053 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3054 System.err.println(rb.getString |
|
3055 ("\t [-providerpath <pathlist>]")); |
|
3056 System.err.println(); |
|
3057 |
|
3058 System.err.println(rb.getString("-help")); |
|
3059 System.err.println(); |
|
3060 |
|
3061 System.err.println(rb.getString |
|
3062 ("-importcert [-v] [-noprompt] [-trustcacerts] [-protected]")); |
|
3063 System.err.println(rb.getString |
|
3064 ("\t [-alias <alias>]")); |
|
3065 System.err.println(rb.getString |
|
3066 ("\t [-file <cert_file>] [-keypass <keypass>]")); |
|
3067 System.err.println(rb.getString |
|
3068 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3069 System.err.println(rb.getString |
|
3070 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3071 System.err.println(rb.getString |
|
3072 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3073 System.err.println(rb.getString |
|
3074 ("\t [-providerpath <pathlist>]")); |
|
3075 System.err.println(); |
|
3076 |
|
3077 System.err.println(rb.getString |
|
3078 ("-importkeystore [-v] ")); |
|
3079 System.err.println(rb.getString |
|
3080 ("\t [-srckeystore <srckeystore>] [-destkeystore <destkeystore>]")); |
|
3081 System.err.println(rb.getString |
|
3082 ("\t [-srcstoretype <srcstoretype>] [-deststoretype <deststoretype>]")); |
|
3083 System.err.println(rb.getString |
|
3084 ("\t [-srcstorepass <srcstorepass>] [-deststorepass <deststorepass>]")); |
|
3085 System.err.println(rb.getString |
|
3086 ("\t [-srcprotected] [-destprotected]")); |
|
3087 System.err.println(rb.getString |
|
3088 ("\t [-srcprovidername <srcprovidername>]\n\t [-destprovidername <destprovidername>]")); |
|
3089 System.err.println(rb.getString |
|
3090 ("\t [-srcalias <srcalias> [-destalias <destalias>]")); |
|
3091 System.err.println(rb.getString |
|
3092 ("\t [-srckeypass <srckeypass>] [-destkeypass <destkeypass>]]")); |
|
3093 System.err.println(rb.getString |
|
3094 ("\t [-noprompt]")); |
|
3095 System.err.println(rb.getString |
|
3096 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3097 System.err.println(rb.getString |
|
3098 ("\t [-providerpath <pathlist>]")); |
|
3099 System.err.println(); |
|
3100 |
|
3101 System.err.println(rb.getString |
|
3102 ("-keypasswd [-v] [-alias <alias>]")); |
|
3103 System.err.println(rb.getString |
|
3104 ("\t [-keypass <old_keypass>] [-new <new_keypass>]")); |
|
3105 System.err.println(rb.getString |
|
3106 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3107 System.err.println(rb.getString |
|
3108 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3109 System.err.println(rb.getString |
|
3110 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3111 System.err.println(rb.getString |
|
3112 ("\t [-providerpath <pathlist>]")); |
|
3113 System.err.println(); |
|
3114 |
|
3115 System.err.println(rb.getString |
|
3116 ("-list [-v | -rfc] [-protected]")); |
|
3117 System.err.println(rb.getString |
|
3118 ("\t [-alias <alias>]")); |
|
3119 System.err.println(rb.getString |
|
3120 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3121 System.err.println(rb.getString |
|
3122 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3123 System.err.println(rb.getString |
|
3124 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3125 System.err.println(rb.getString |
|
3126 ("\t [-providerpath <pathlist>]")); |
|
3127 System.err.println(); |
|
3128 |
|
3129 System.err.println(rb.getString |
|
3130 ("-printcert [-v] [-file <cert_file>]")); |
|
3131 System.err.println(); |
|
3132 |
|
3133 System.err.println(rb.getString |
|
3134 ("-storepasswd [-v] [-new <new_storepass>]")); |
|
3135 System.err.println(rb.getString |
|
3136 ("\t [-keystore <keystore>] [-storepass <storepass>]")); |
|
3137 System.err.println(rb.getString |
|
3138 ("\t [-storetype <storetype>] [-providername <name>]")); |
|
3139 System.err.println(rb.getString |
|
3140 ("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ...")); |
|
3141 System.err.println(rb.getString |
|
3142 ("\t [-providerpath <pathlist>]")); |
|
3143 |
|
3144 if (debug) { |
|
3145 throw new RuntimeException("NO ERROR, SORRY"); |
|
3146 } else { |
|
3147 System.exit(1); |
|
3148 } |
|
3149 } |
|
3150 |
|
3151 private void tinyHelp() { |
|
3152 System.err.println(rb.getString("Try keytool -help")); |
|
3153 |
|
3154 // do not drown user with the help lines. |
|
3155 if (debug) { |
|
3156 throw new RuntimeException("NO BIG ERROR, SORRY"); |
|
3157 } else { |
|
3158 System.exit(1); |
|
3159 } |
|
3160 } |
|
3161 |
|
3162 private void errorNeedArgument(String flag) { |
|
3163 Object[] source = {flag}; |
|
3164 System.err.println(new MessageFormat( |
|
3165 rb.getString("Command option <flag> needs an argument.")).format(source)); |
|
3166 tinyHelp(); |
|
3167 } |
|
3168 } |
|
3169 |
|
3170 // This class is exactly the same as com.sun.tools.javac.util.Pair, |
|
3171 // it's copied here since the original one is not included in JRE. |
|
3172 class Pair<A, B> { |
|
3173 |
|
3174 public final A fst; |
|
3175 public final B snd; |
|
3176 |
|
3177 public Pair(A fst, B snd) { |
|
3178 this.fst = fst; |
|
3179 this.snd = snd; |
|
3180 } |
|
3181 |
|
3182 public String toString() { |
|
3183 return "Pair[" + fst + "," + snd + "]"; |
|
3184 } |
|
3185 |
|
3186 private static boolean equals(Object x, Object y) { |
|
3187 return (x == null && y == null) || (x != null && x.equals(y)); |
|
3188 } |
|
3189 |
|
3190 public boolean equals(Object other) { |
|
3191 return |
|
3192 other instanceof Pair && |
|
3193 equals(fst, ((Pair)other).fst) && |
|
3194 equals(snd, ((Pair)other).snd); |
|
3195 } |
|
3196 |
|
3197 public int hashCode() { |
|
3198 if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1; |
|
3199 else if (snd == null) return fst.hashCode() + 2; |
|
3200 else return fst.hashCode() * 17 + snd.hashCode(); |
|
3201 } |
|
3202 } |