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 }