1 /*
2  * Copyright (c) 1997, 2020, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.tools.keytool;
27 
28 import java.io.*;
29 import java.nio.file.Files;
30 import java.nio.file.Paths;
31 import java.security.CodeSigner;
32 import java.security.CryptoPrimitive;
33 import java.security.KeyStore;
34 import java.security.KeyStoreException;
35 import java.security.MessageDigest;
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.Timestamp;
42 import java.security.UnrecoverableEntryException;
43 import java.security.UnrecoverableKeyException;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.Principal;
46 import java.security.Provider;
47 import java.security.cert.Certificate;
48 import java.security.cert.CertificateFactory;
49 import java.security.cert.CertStoreException;
50 import java.security.cert.CRL;
51 import java.security.cert.X509Certificate;
52 import java.security.cert.CertificateException;
53 import java.security.interfaces.ECKey;
54 import java.security.spec.AlgorithmParameterSpec;
55 import java.security.spec.ECParameterSpec;
56 import java.text.Collator;
57 import java.text.MessageFormat;
58 import java.util.*;
59 import java.util.jar.JarEntry;
60 import java.util.jar.JarFile;
61 import java.lang.reflect.Constructor;
62 import java.math.BigInteger;
63 import java.net.URI;
64 import java.net.URL;
65 import java.net.URLClassLoader;
66 import java.security.cert.CertStore;
67 
68 import java.security.cert.X509CRL;
69 import java.security.cert.X509CRLEntry;
70 import java.security.cert.X509CRLSelector;
71 import javax.security.auth.x500.X500Principal;
72 import java.util.Base64;
73 
74 import sun.security.util.DisabledAlgorithmConstraints;
75 import sun.security.util.KeyUtil;
76 import sun.security.util.NamedCurve;
77 import sun.security.util.ObjectIdentifier;
78 import sun.security.pkcs10.PKCS10;
79 import sun.security.pkcs10.PKCS10Attribute;
80 import sun.security.provider.X509Factory;
81 import sun.security.provider.certpath.CertStoreHelper;
82 import sun.security.util.Password;
83 import sun.security.util.SecurityProviderConstants;
84 import sun.security.util.SignatureUtil;
85 import javax.crypto.KeyGenerator;
86 import javax.crypto.SecretKey;
87 import javax.crypto.SecretKeyFactory;
88 import javax.crypto.spec.PBEKeySpec;
89 
90 import sun.security.pkcs.PKCS9Attribute;
91 import sun.security.tools.KeyStoreUtil;
92 import sun.security.tools.PathList;
93 import sun.security.util.DerValue;
94 import sun.security.util.Pem;
95 import sun.security.x509.*;
96 
97 import static java.security.KeyStore.*;
98 import static sun.security.tools.keytool.Main.Command.*;
99 import static sun.security.tools.keytool.Main.Option.*;
100 
101 /**
102  * This tool manages keystores.
103  *
104  * @author Jan Luehe
105  *
106  *
107  * @see java.security.KeyStore
108  * @see sun.security.provider.KeyProtector
109  * @see sun.security.provider.JavaKeyStore
110  *
111  * @since 1.2
112  */
113 public final class Main {
114 
115     private static final byte[] CRLF = new byte[] {'\r', '\n'};
116 
117     private boolean debug = false;
118     private Command command = null;
119     private String sigAlgName = null;
120     private String keyAlgName = null;
121     private boolean verbose = false;
122     private int keysize = -1;
123     private boolean rfc = false;
124     private long validity = (long)90;
125     private String alias = null;
126     private String dname = null;
127     private String dest = null;
128     private String filename = null;
129     private String infilename = null;
130     private String outfilename = null;
131     private String srcksfname = null;
132 
133     // User-specified providers are added before any command is called.
134     // However, they are not removed before the end of the main() method.
135     // If you're calling KeyTool.main() directly in your own Java program,
136     // please programtically add any providers you need and do not specify
137     // them through the command line.
138 
139     private Set<Pair <String, String>> providers = null;
140     private String storetype = null;
141     private String srcProviderName = null;
142     private String providerName = null;
143     private String pathlist = null;
144     private char[] storePass = null;
145     private char[] storePassNew = null;
146     private char[] keyPass = null;
147     private char[] keyPassNew = null;
148     private char[] newPass = null;
149     private char[] destKeyPass = null;
150     private char[] srckeyPass = null;
151     private String ksfname = null;
152     private File ksfile = null;
153     private InputStream ksStream = null; // keystore stream
154     private String sslserver = null;
155     private String jarfile = null;
156     private KeyStore keyStore = null;
157     private boolean token = false;
158     private boolean nullStream = false;
159     private boolean kssave = false;
160     private boolean noprompt = false;
161     private boolean trustcacerts = false;
162     private boolean nowarn = false;
163     private boolean protectedPath = false;
164     private boolean srcprotectedPath = false;
165     private CertificateFactory cf = null;
166     private KeyStore caks = null; // "cacerts" keystore
167     private char[] srcstorePass = null;
168     private String srcstoretype = null;
169     private Set<char[]> passwords = new HashSet<>();
170     private String startDate = null;
171 
172     private List<String> ids = new ArrayList<>();   // used in GENCRL
173     private List<String> v3ext = new ArrayList<>();
174 
175     // In-place importkeystore is special.
176     // A backup is needed, and no need to prompt for deststorepass.
177     private boolean inplaceImport = false;
178     private String inplaceBackupName = null;
179 
180     // Warnings on weak algorithms etc
181     private List<String> weakWarnings = new ArrayList<>();
182 
183     private static final DisabledAlgorithmConstraints DISABLED_CHECK =
184             new DisabledAlgorithmConstraints(
185                     DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
186 
187     private static final DisabledAlgorithmConstraints LEGACY_CHECK =
188             new DisabledAlgorithmConstraints(
189                     DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);
190 
191     private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
192             .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
193 
194     enum Command {
195         CERTREQ("Generates.a.certificate.request",
196             ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
197             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
198             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
199         CHANGEALIAS("Changes.an.entry.s.alias",
200             ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS,
201             STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
202             PROVIDERPATH, V, PROTECTED),
203         DELETE("Deletes.an.entry",
204             ALIAS, KEYSTORE, STOREPASS, STORETYPE,
205             PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
206             PROVIDERPATH, V, PROTECTED),
207         EXPORTCERT("Exports.certificate",
208             RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS,
209             STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
210             PROVIDERPATH, V, PROTECTED),
211         GENKEYPAIR("Generates.a.key.pair",
212             ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME,
213             STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,
214             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
215             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
216         GENSECKEY("Generates.a.secret.key",
217             ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,
218             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
219             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
220         GENCERT("Generates.certificate.from.a.certificate.request",
221             RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME,
222             STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,
223             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
224             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
225         IMPORTCERT("Imports.a.certificate.or.a.certificate.chain",
226             NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN,
227             KEYPASS, KEYSTORE, STOREPASS, STORETYPE,
228             PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
229             PROVIDERPATH, V),
230         IMPORTPASS("Imports.a.password",
231             ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,
232             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
233             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
234         IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore",
235             SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE,
236             DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS,
237             SRCPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME,
238             SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS,
239             NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH,
240             V),
241         KEYPASSWD("Changes.the.key.password.of.an.entry",
242             ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS,
243             STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
244             PROVIDERPATH, V),
245         LIST("Lists.entries.in.a.keystore",
246             RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE,
247             PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
248             PROVIDERPATH, V, PROTECTED),
249         PRINTCERT("Prints.the.content.of.a.certificate",
250             RFC, FILEIN, SSLSERVER, JARFILE, V),
251         PRINTCERTREQ("Prints.the.content.of.a.certificate.request",
252             FILEIN, V),
253         PRINTCRL("Prints.the.content.of.a.CRL.file",
254             FILEIN, V),
255         STOREPASSWD("Changes.the.store.password.of.a.keystore",
256             NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME,
257             PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),
258 
259         // Undocumented start here, KEYCLONE is used a marker in -help;
260 
261         KEYCLONE("Clones.a.key.entry",
262             ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE,
263             KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS,
264             PROVIDERARG, PROVIDERPATH, V),
265         SELFCERT("Generates.a.self.signed.certificate",
266             ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS,
267             STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,
268             PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),
269         GENCRL("Generates.CRL",
270             RFC, FILEOUT, ID,
271             ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE,
272             STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
273             PROVIDERARG, PROVIDERPATH, V, PROTECTED),
274         IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database",
275             FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,
276             PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V);
277 
278         final String description;
279         final Option[] options;
Command(String d, Option... o)280         Command(String d, Option... o) {
281             description = d;
282             options = o;
283         }
284         @Override
toString()285         public String toString() {
286             return "-" + name().toLowerCase(Locale.ENGLISH);
287         }
288     };
289 
290     enum Option {
291         ALIAS("alias", "<alias>", "alias.name.of.the.entry.to.process"),
292         DESTALIAS("destalias", "<destalias>", "destination.alias"),
293         DESTKEYPASS("destkeypass", "<arg>", "destination.key.password"),
294         DESTKEYSTORE("destkeystore", "<destkeystore>", "destination.keystore.name"),
295         DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"),
296         DESTPROVIDERNAME("destprovidername", "<destprovidername>", "destination.keystore.provider.name"),
297         DESTSTOREPASS("deststorepass", "<arg>", "destination.keystore.password"),
298         DESTSTORETYPE("deststoretype", "<deststoretype>", "destination.keystore.type"),
299         DNAME("dname", "<dname>", "distinguished.name"),
300         EXT("ext", "<value>", "X.509.extension"),
301         FILEOUT("file", "<filename>", "output.file.name"),
302         FILEIN("file", "<filename>", "input.file.name"),
303         ID("id", "<id:reason>", "Serial.ID.of.cert.to.revoke"),
304         INFILE("infile", "<filename>", "input.file.name"),
305         KEYALG("keyalg", "<keyalg>", "key.algorithm.name"),
306         KEYPASS("keypass", "<arg>", "key.password"),
307         KEYSIZE("keysize", "<keysize>", "key.bit.size"),
308         KEYSTORE("keystore", "<keystore>", "keystore.name"),
309         NEW("new", "<arg>", "new.password"),
310         NOPROMPT("noprompt", null, "do.not.prompt"),
311         OUTFILE("outfile", "<filename>", "output.file.name"),
312         PROTECTED("protected", null, "password.through.protected.mechanism"),
313         PROVIDERARG("providerarg", "<arg>", "provider.argument"),
314         PROVIDERCLASS("providerclass", "<providerclass>", "provider.class.name"),
315         PROVIDERNAME("providername", "<providername>", "provider.name"),
316         PROVIDERPATH("providerpath", "<pathlist>", "provider.classpath"),
317         RFC("rfc", null, "output.in.RFC.style"),
318         SIGALG("sigalg", "<sigalg>", "signature.algorithm.name"),
319         SRCALIAS("srcalias", "<srcalias>", "source.alias"),
320         SRCKEYPASS("srckeypass", "<arg>", "source.key.password"),
321         SRCKEYSTORE("srckeystore", "<srckeystore>", "source.keystore.name"),
322         SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"),
323         SRCPROVIDERNAME("srcprovidername", "<srcprovidername>", "source.keystore.provider.name"),
324         SRCSTOREPASS("srcstorepass", "<arg>", "source.keystore.password"),
325         SRCSTORETYPE("srcstoretype", "<srcstoretype>", "source.keystore.type"),
326         SSLSERVER("sslserver", "<server[:port]>", "SSL.server.host.and.port"),
327         JARFILE("jarfile", "<filename>", "signed.jar.file"),
328         STARTDATE("startdate", "<startdate>", "certificate.validity.start.date.time"),
329         STOREPASS("storepass", "<arg>", "keystore.password"),
330         STORETYPE("storetype", "<storetype>", "keystore.type"),
331         TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"),
332         V("v", null, "verbose.output"),
333         VALIDITY("validity", "<valDays>", "validity.number.of.days");
334 
335         final String name, arg, description;
Option(String name, String arg, String description)336         Option(String name, String arg, String description) {
337             this.name = name;
338             this.arg = arg;
339             this.description = description;
340         }
341         @Override
toString()342         public String toString() {
343             return "-" + name;
344         }
345     };
346 
347     private static final Class<?>[] PARAM_STRING = { String.class };
348 
349     private static final String NONE = "NONE";
350     private static final String P11KEYSTORE = "PKCS11";
351     private static final String P12KEYSTORE = "PKCS12";
352     private static final String keyAlias = "mykey";
353 
354     // for i18n
355     private static final java.util.ResourceBundle rb =
356         java.util.ResourceBundle.getBundle(
357             "sun.security.tools.keytool.Resources");
358     private static final Collator collator = Collator.getInstance();
359     static {
360         // this is for case insensitive string comparisons
361         collator.setStrength(Collator.PRIMARY);
362     };
363 
Main()364     private Main() { }
365 
main(String[] args)366     public static void main(String[] args) throws Exception {
367         Main kt = new Main();
368         kt.run(args, System.out);
369     }
370 
run(String[] args, PrintStream out)371     private void run(String[] args, PrintStream out) throws Exception {
372         try {
373             parseArgs(args);
374             if (command != null) {
375                 doCommands(out);
376             }
377         } catch (Exception e) {
378             System.out.println(rb.getString("keytool.error.") + e);
379             if (verbose) {
380                 e.printStackTrace(System.out);
381             }
382             if (!debug) {
383                 System.exit(1);
384             } else {
385                 throw e;
386             }
387         } finally {
388             printWeakWarnings(false);
389             for (char[] pass : passwords) {
390                 if (pass != null) {
391                     Arrays.fill(pass, ' ');
392                     pass = null;
393                 }
394             }
395 
396             if (ksStream != null) {
397                 ksStream.close();
398             }
399         }
400     }
401 
402     /**
403      * Parse command line arguments.
404      */
parseArgs(String[] args)405     void parseArgs(String[] args) {
406 
407         int i=0;
408         boolean help = args.length == 0;
409 
410         for (i=0; (i < args.length) && args[i].startsWith("-"); i++) {
411 
412             String flags = args[i];
413 
414             // Check if the last option needs an arg
415             if (i == args.length - 1) {
416                 for (Option option: Option.values()) {
417                     // Only options with an arg need to be checked
418                     if (collator.compare(flags, option.toString()) == 0) {
419                         if (option.arg != null) errorNeedArgument(flags);
420                         break;
421                     }
422                 }
423             }
424 
425             /*
426              * Check modifiers
427              */
428             String modifier = null;
429             int pos = flags.indexOf(':');
430             if (pos > 0) {
431                 modifier = flags.substring(pos+1);
432                 flags = flags.substring(0, pos);
433             }
434             /*
435              * command modes
436              */
437             boolean isCommand = false;
438             for (Command c: Command.values()) {
439                 if (collator.compare(flags, c.toString()) == 0) {
440                     command = c;
441                     isCommand = true;
442                     break;
443                 }
444             }
445 
446             if (isCommand) {
447                 // already recognized as a command
448             } else if (collator.compare(flags, "-export") == 0) {
449                 command = EXPORTCERT;
450             } else if (collator.compare(flags, "-genkey") == 0) {
451                 command = GENKEYPAIR;
452             } else if (collator.compare(flags, "-import") == 0) {
453                 command = IMPORTCERT;
454             } else if (collator.compare(flags, "-importpassword") == 0) {
455                 command = IMPORTPASS;
456             } else if (collator.compare(flags, "-help") == 0) {
457                 help = true;
458             } else if (collator.compare(flags, "-nowarn") == 0) {
459                 nowarn = true;
460             }
461 
462             /*
463              * specifiers
464              */
465             else if (collator.compare(flags, "-keystore") == 0 ||
466                     collator.compare(flags, "-destkeystore") == 0) {
467                 ksfname = args[++i];
468             } else if (collator.compare(flags, "-storepass") == 0 ||
469                     collator.compare(flags, "-deststorepass") == 0) {
470                 storePass = getPass(modifier, args[++i]);
471                 passwords.add(storePass);
472             } else if (collator.compare(flags, "-storetype") == 0 ||
473                     collator.compare(flags, "-deststoretype") == 0) {
474                 storetype = KeyStoreUtil.niceStoreTypeName(args[++i]);
475             } else if (collator.compare(flags, "-srcstorepass") == 0) {
476                 srcstorePass = getPass(modifier, args[++i]);
477                 passwords.add(srcstorePass);
478             } else if (collator.compare(flags, "-srcstoretype") == 0) {
479                 srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]);
480             } else if (collator.compare(flags, "-srckeypass") == 0) {
481                 srckeyPass = getPass(modifier, args[++i]);
482                 passwords.add(srckeyPass);
483             } else if (collator.compare(flags, "-srcprovidername") == 0) {
484                 srcProviderName = args[++i];
485             } else if (collator.compare(flags, "-providername") == 0 ||
486                     collator.compare(flags, "-destprovidername") == 0) {
487                 providerName = args[++i];
488             } else if (collator.compare(flags, "-providerpath") == 0) {
489                 pathlist = args[++i];
490             } else if (collator.compare(flags, "-keypass") == 0) {
491                 keyPass = getPass(modifier, args[++i]);
492                 passwords.add(keyPass);
493             } else if (collator.compare(flags, "-new") == 0) {
494                 newPass = getPass(modifier, args[++i]);
495                 passwords.add(newPass);
496             } else if (collator.compare(flags, "-destkeypass") == 0) {
497                 destKeyPass = getPass(modifier, args[++i]);
498                 passwords.add(destKeyPass);
499             } else if (collator.compare(flags, "-alias") == 0 ||
500                     collator.compare(flags, "-srcalias") == 0) {
501                 alias = args[++i];
502             } else if (collator.compare(flags, "-dest") == 0 ||
503                     collator.compare(flags, "-destalias") == 0) {
504                 dest = args[++i];
505             } else if (collator.compare(flags, "-dname") == 0) {
506                 dname = args[++i];
507             } else if (collator.compare(flags, "-keysize") == 0) {
508                 keysize = Integer.parseInt(args[++i]);
509             } else if (collator.compare(flags, "-keyalg") == 0) {
510                 keyAlgName = args[++i];
511             } else if (collator.compare(flags, "-sigalg") == 0) {
512                 sigAlgName = args[++i];
513             } else if (collator.compare(flags, "-startdate") == 0) {
514                 startDate = args[++i];
515             } else if (collator.compare(flags, "-validity") == 0) {
516                 validity = Long.parseLong(args[++i]);
517             } else if (collator.compare(flags, "-ext") == 0) {
518                 v3ext.add(args[++i]);
519             } else if (collator.compare(flags, "-id") == 0) {
520                 ids.add(args[++i]);
521             } else if (collator.compare(flags, "-file") == 0) {
522                 filename = args[++i];
523             } else if (collator.compare(flags, "-infile") == 0) {
524                 infilename = args[++i];
525             } else if (collator.compare(flags, "-outfile") == 0) {
526                 outfilename = args[++i];
527             } else if (collator.compare(flags, "-sslserver") == 0) {
528                 sslserver = args[++i];
529             } else if (collator.compare(flags, "-jarfile") == 0) {
530                 jarfile = args[++i];
531             } else if (collator.compare(flags, "-srckeystore") == 0) {
532                 srcksfname = args[++i];
533             } else if ((collator.compare(flags, "-provider") == 0) ||
534                         (collator.compare(flags, "-providerclass") == 0)) {
535                 if (providers == null) {
536                     providers = new HashSet<Pair <String, String>> (3);
537                 }
538                 String providerClass = args[++i];
539                 String providerArg = null;
540 
541                 if (args.length > (i+1)) {
542                     flags = args[i+1];
543                     if (collator.compare(flags, "-providerarg") == 0) {
544                         if (args.length == (i+2)) errorNeedArgument(flags);
545                         providerArg = args[i+2];
546                         i += 2;
547                     }
548                 }
549                 providers.add(
550                         Pair.of(providerClass, providerArg));
551             }
552 
553             /*
554              * options
555              */
556             else if (collator.compare(flags, "-v") == 0) {
557                 verbose = true;
558             } else if (collator.compare(flags, "-debug") == 0) {
559                 debug = true;
560             } else if (collator.compare(flags, "-rfc") == 0) {
561                 rfc = true;
562             } else if (collator.compare(flags, "-noprompt") == 0) {
563                 noprompt = true;
564             } else if (collator.compare(flags, "-trustcacerts") == 0) {
565                 trustcacerts = true;
566             } else if (collator.compare(flags, "-protected") == 0 ||
567                     collator.compare(flags, "-destprotected") == 0) {
568                 protectedPath = true;
569             } else if (collator.compare(flags, "-srcprotected") == 0) {
570                 srcprotectedPath = true;
571             } else  {
572                 System.err.println(rb.getString("Illegal.option.") + flags);
573                 tinyHelp();
574             }
575         }
576 
577         if (i<args.length) {
578             System.err.println(rb.getString("Illegal.option.") + args[i]);
579             tinyHelp();
580         }
581 
582         if (command == null) {
583             if (help) {
584                 usage();
585             } else {
586                 System.err.println(rb.getString("Usage.error.no.command.provided"));
587                 tinyHelp();
588             }
589         } else if (help) {
590             usage();
591             command = null;
592         }
593     }
594 
isKeyStoreRelated(Command cmd)595     boolean isKeyStoreRelated(Command cmd) {
596         return cmd != PRINTCERT && cmd != PRINTCERTREQ;
597     }
598 
599 
600     /**
601      * Execute the commands.
602      */
doCommands(PrintStream out)603     void doCommands(PrintStream out) throws Exception {
604         if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
605                 KeyStoreUtil.isWindowsKeyStore(storetype)) {
606             token = true;
607             if (ksfname == null) {
608                 ksfname = NONE;
609             }
610         }
611         if (NONE.equals(ksfname)) {
612             nullStream = true;
613         }
614 
615         if (token && !nullStream) {
616             System.err.println(MessageFormat.format(rb.getString
617                 (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
618             System.err.println();
619             tinyHelp();
620         }
621 
622         if (token &&
623             (command == KEYPASSWD || command == STOREPASSWD)) {
624             throw new UnsupportedOperationException(MessageFormat.format(rb.getString
625                         (".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype));
626         }
627 
628         if (token && (keyPass != null || newPass != null || destKeyPass != null)) {
629             throw new IllegalArgumentException(MessageFormat.format(rb.getString
630                 (".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype));
631         }
632 
633         if (protectedPath) {
634             if (storePass != null || keyPass != null ||
635                     newPass != null || destKeyPass != null) {
636                 throw new IllegalArgumentException(rb.getString
637                         ("if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified"));
638             }
639         }
640 
641         if (srcprotectedPath) {
642             if (srcstorePass != null || srckeyPass != null) {
643                 throw new IllegalArgumentException(rb.getString
644                         ("if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified"));
645             }
646         }
647 
648         if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
649             if (storePass != null || keyPass != null ||
650                     newPass != null || destKeyPass != null) {
651                 throw new IllegalArgumentException(rb.getString
652                         ("if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified"));
653             }
654         }
655 
656         if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
657             if (srcstorePass != null || srckeyPass != null) {
658                 throw new IllegalArgumentException(rb.getString
659                         ("if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified"));
660             }
661         }
662 
663         if (validity <= (long)0) {
664             throw new Exception
665                 (rb.getString("Validity.must.be.greater.than.zero"));
666         }
667 
668         // Try to load and install specified provider
669         if (providers != null) {
670             ClassLoader cl = null;
671             if (pathlist != null) {
672                 String path = null;
673                 path = PathList.appendPath(
674                         path, System.getProperty("java.class.path"));
675                 path = PathList.appendPath(
676                         path, System.getProperty("env.class.path"));
677                 path = PathList.appendPath(path, pathlist);
678 
679                 URL[] urls = PathList.pathToURLs(path);
680                 cl = new URLClassLoader(urls);
681             } else {
682                 cl = ClassLoader.getSystemClassLoader();
683             }
684 
685             for (Pair <String, String> provider: providers) {
686                 String provName = provider.fst;
687                 Class<?> provClass;
688                 if (cl != null) {
689                     provClass = cl.loadClass(provName);
690                 } else {
691                     provClass = Class.forName(provName);
692                 }
693 
694                 String provArg = provider.snd;
695                 Object obj;
696                 if (provArg == null) {
697                     obj = provClass.newInstance();
698                 } else {
699                     Constructor<?> c = provClass.getConstructor(PARAM_STRING);
700                     obj = c.newInstance(provArg);
701                 }
702                 if (!(obj instanceof Provider)) {
703                     MessageFormat form = new MessageFormat
704                         (rb.getString("provName.not.a.provider"));
705                     Object[] source = {provName};
706                     throw new Exception(form.format(source));
707                 }
708                 Security.addProvider((Provider)obj);
709             }
710         }
711 
712         if (command == LIST && verbose && rfc) {
713             System.err.println(rb.getString
714                 ("Must.not.specify.both.v.and.rfc.with.list.command"));
715             tinyHelp();
716         }
717 
718         // Make sure provided passwords are at least 6 characters long
719         if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) {
720             throw new Exception(rb.getString
721                 ("Key.password.must.be.at.least.6.characters"));
722         }
723         if (newPass != null && newPass.length < 6) {
724             throw new Exception(rb.getString
725                 ("New.password.must.be.at.least.6.characters"));
726         }
727         if (destKeyPass != null && destKeyPass.length < 6) {
728             throw new Exception(rb.getString
729                 ("New.password.must.be.at.least.6.characters"));
730         }
731 
732         // Set this before inplaceImport check so we can compare name.
733         if (ksfname == null) {
734             ksfname = System.getProperty("user.home") + File.separator
735                     + ".keystore";
736         }
737 
738         KeyStore srcKeyStore = null;
739         if (command == IMPORTKEYSTORE) {
740             inplaceImport = inplaceImportCheck();
741             if (inplaceImport) {
742                 // We load srckeystore first so we have srcstorePass that
743                 // can be assigned to storePass
744                 srcKeyStore = loadSourceKeyStore();
745                 if (storePass == null) {
746                     storePass = srcstorePass;
747                 }
748             }
749         }
750 
751         // Check if keystore exists.
752         // If no keystore has been specified at the command line, try to use
753         // the default, which is located in $HOME/.keystore.
754         // If the command is "genkey", "identitydb", "import", or "printcert",
755         // it is OK not to have a keystore.
756 
757         // DO NOT open the existing keystore if this is an in-place import.
758         // The keystore should be created as brand new.
759         if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {
760                 try {
761                     ksfile = new File(ksfname);
762                     // Check if keystore file is empty
763                     if (ksfile.exists() && ksfile.length() == 0) {
764                         throw new Exception(rb.getString
765                         ("Keystore.file.exists.but.is.empty.") + ksfname);
766                     }
767                     ksStream = new FileInputStream(ksfile);
768                 } catch (FileNotFoundException e) {
769                     if (command != GENKEYPAIR &&
770                         command != GENSECKEY &&
771                         command != IDENTITYDB &&
772                         command != IMPORTCERT &&
773                         command != IMPORTPASS &&
774                         command != IMPORTKEYSTORE &&
775                         command != PRINTCRL) {
776                         throw new Exception(rb.getString
777                                 ("Keystore.file.does.not.exist.") + ksfname);
778                     }
779                 }
780             }
781 
782         if ((command == KEYCLONE || command == CHANGEALIAS)
783                 && dest == null) {
784             dest = getAlias("destination");
785             if ("".equals(dest)) {
786                 throw new Exception(rb.getString
787                         ("Must.specify.destination.alias"));
788             }
789         }
790 
791         if (command == DELETE && alias == null) {
792             alias = getAlias(null);
793             if ("".equals(alias)) {
794                 throw new Exception(rb.getString("Must.specify.alias"));
795             }
796         }
797 
798         // Create new keystore
799         if (storetype == null) {
800             storetype = KeyStore.getDefaultType();
801         }
802         if (providerName == null) {
803             keyStore = KeyStore.getInstance(storetype);
804         } else {
805             keyStore = KeyStore.getInstance(storetype, providerName);
806         }
807 
808         /*
809          * Load the keystore data.
810          *
811          * At this point, it's OK if no keystore password has been provided.
812          * We want to make sure that we can load the keystore data, i.e.,
813          * the keystore data has the right format. If we cannot load the
814          * keystore, why bother asking the user for his or her password?
815          * Only if we were able to load the keystore, and no keystore
816          * password has been provided, will we prompt the user for the
817          * keystore password to verify the keystore integrity.
818          * This means that the keystore is loaded twice: first load operation
819          * checks the keystore format, second load operation verifies the
820          * keystore integrity.
821          *
822          * If the keystore password has already been provided (at the
823          * command line), however, the keystore is loaded only once, and the
824          * keystore format and integrity are checked "at the same time".
825          *
826          * Null stream keystores are loaded later.
827          */
828         if (!nullStream) {
829             if (inplaceImport) {
830                 keyStore.load(null, storePass);
831             } else {
832             keyStore.load(ksStream, storePass);
833             }
834             if (ksStream != null) {
835                 ksStream.close();
836             }
837         }
838 
839         if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {
840             throw new UnsupportedOperationException(rb.getString
841                     (".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));
842         }
843 
844         // All commands that create or modify the keystore require a keystore
845         // password.
846 
847         if (nullStream && storePass != null) {
848             keyStore.load(null, storePass);
849         } else if (!nullStream && storePass != null) {
850             // If we are creating a new non nullStream-based keystore,
851             // insist that the password be at least 6 characters
852             if (ksStream == null && storePass.length < 6) {
853                 throw new Exception(rb.getString
854                         ("Keystore.password.must.be.at.least.6.characters"));
855             }
856         } else if (storePass == null) {
857 
858             // only prompt if (protectedPath == false)
859 
860             if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) &&
861                 (command == CERTREQ ||
862                         command == DELETE ||
863                         command == GENKEYPAIR ||
864                         command == GENSECKEY ||
865                         command == IMPORTCERT ||
866                         command == IMPORTPASS ||
867                         command == IMPORTKEYSTORE ||
868                         command == KEYCLONE ||
869                         command == CHANGEALIAS ||
870                         command == SELFCERT ||
871                         command == STOREPASSWD ||
872                         command == KEYPASSWD ||
873                         command == IDENTITYDB)) {
874                 int count = 0;
875                 do {
876                     if (command == IMPORTKEYSTORE) {
877                         System.err.print
878                                 (rb.getString("Enter.destination.keystore.password."));
879                     } else {
880                         System.err.print
881                                 (rb.getString("Enter.keystore.password."));
882                     }
883                     System.err.flush();
884                     storePass = Password.readPassword(System.in);
885                     passwords.add(storePass);
886 
887                     // If we are creating a new non nullStream-based keystore,
888                     // insist that the password be at least 6 characters
889                     if (!nullStream && (storePass == null || storePass.length < 6)) {
890                         System.err.println(rb.getString
891                                 ("Keystore.password.is.too.short.must.be.at.least.6.characters"));
892                         storePass = null;
893                     }
894 
895                     // If the keystore file does not exist and needs to be
896                     // created, the storepass should be prompted twice.
897                     if (storePass != null && !nullStream && ksStream == null) {
898                         System.err.print(rb.getString("Re.enter.new.password."));
899                         char[] storePassAgain = Password.readPassword(System.in);
900                         passwords.add(storePassAgain);
901                         if (!Arrays.equals(storePass, storePassAgain)) {
902                             System.err.println
903                                 (rb.getString("They.don.t.match.Try.again"));
904                             storePass = null;
905                         }
906                     }
907 
908                     count++;
909                 } while ((storePass == null) && count < 3);
910 
911 
912                 if (storePass == null) {
913                     System.err.println
914                         (rb.getString("Too.many.failures.try.later"));
915                     return;
916                 }
917             } else if (!protectedPath
918                     && !KeyStoreUtil.isWindowsKeyStore(storetype)
919                     && isKeyStoreRelated(command)) {
920                 // here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
921                 if (command != PRINTCRL) {
922                     System.err.print(rb.getString("Enter.keystore.password."));
923                     System.err.flush();
924                     storePass = Password.readPassword(System.in);
925                     passwords.add(storePass);
926                 }
927             }
928 
929             // Now load a nullStream-based keystore,
930             // or verify the integrity of an input stream-based keystore
931             if (nullStream) {
932                 keyStore.load(null, storePass);
933             } else if (ksStream != null) {
934                 ksStream = new FileInputStream(ksfile);
935                 keyStore.load(ksStream, storePass);
936                 ksStream.close();
937             }
938         }
939 
940         if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {
941             MessageFormat form = new MessageFormat(rb.getString(
942                 "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));
943             if (keyPass != null && !Arrays.equals(storePass, keyPass)) {
944                 Object[] source = {"-keypass"};
945                 System.err.println(form.format(source));
946                 keyPass = storePass;
947             }
948             if (newPass != null && !Arrays.equals(storePass, newPass)) {
949                 Object[] source = {"-new"};
950                 System.err.println(form.format(source));
951                 newPass = storePass;
952             }
953             if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) {
954                 Object[] source = {"-destkeypass"};
955                 System.err.println(form.format(source));
956                 destKeyPass = storePass;
957             }
958         }
959 
960         // Create a certificate factory
961         if (command == PRINTCERT || command == IMPORTCERT
962                 || command == IDENTITYDB || command == PRINTCRL) {
963             cf = CertificateFactory.getInstance("X509");
964         }
965 
966         // -trustcacerts can only be specified on -importcert.
967         // Reset it so that warnings on CA cert will remain for
968         // -printcert, etc.
969         if (command != IMPORTCERT) {
970             trustcacerts = false;
971         }
972 
973         if (trustcacerts) {
974             caks = KeyStoreUtil.getCacertsKeyStore();
975         }
976 
977         // Perform the specified command
978         if (command == CERTREQ) {
979             if (filename != null) {
980                 try (PrintStream ps = new PrintStream(new FileOutputStream
981                                                       (filename))) {
982                     doCertReq(alias, sigAlgName, ps);
983                 }
984             } else {
985                 doCertReq(alias, sigAlgName, out);
986             }
987             if (verbose && filename != null) {
988                 MessageFormat form = new MessageFormat(rb.getString
989                         ("Certification.request.stored.in.file.filename."));
990                 Object[] source = {filename};
991                 System.err.println(form.format(source));
992                 System.err.println(rb.getString("Submit.this.to.your.CA"));
993             }
994         } else if (command == DELETE) {
995             doDeleteEntry(alias);
996             kssave = true;
997         } else if (command == EXPORTCERT) {
998             if (filename != null) {
999                 try (PrintStream ps = new PrintStream(new FileOutputStream
1000                                                    (filename))) {
1001                     doExportCert(alias, ps);
1002                 }
1003             } else {
1004                 doExportCert(alias, out);
1005             }
1006             if (filename != null) {
1007                 MessageFormat form = new MessageFormat(rb.getString
1008                         ("Certificate.stored.in.file.filename."));
1009                 Object[] source = {filename};
1010                 System.err.println(form.format(source));
1011             }
1012         } else if (command == GENKEYPAIR) {
1013             if (keyAlgName == null) {
1014                 keyAlgName = "DSA";
1015             }
1016             doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName);
1017             kssave = true;
1018         } else if (command == GENSECKEY) {
1019             if (keyAlgName == null) {
1020                 keyAlgName = "DES";
1021             }
1022             doGenSecretKey(alias, keyAlgName, keysize);
1023             kssave = true;
1024         } else if (command == IMPORTPASS) {
1025             if (keyAlgName == null) {
1026                 keyAlgName = "PBE";
1027             }
1028             // password is stored as a secret key
1029             doGenSecretKey(alias, keyAlgName, keysize);
1030             kssave = true;
1031         } else if (command == IDENTITYDB) {
1032             if (filename != null) {
1033                 try (InputStream inStream = new FileInputStream(filename)) {
1034                     doImportIdentityDatabase(inStream);
1035                 }
1036             } else {
1037                 doImportIdentityDatabase(System.in);
1038             }
1039         } else if (command == IMPORTCERT) {
1040             InputStream inStream = System.in;
1041             if (filename != null) {
1042                 inStream = new FileInputStream(filename);
1043             }
1044             String importAlias = (alias!=null)?alias:keyAlias;
1045             try {
1046                 if (keyStore.entryInstanceOf(
1047                         importAlias, KeyStore.PrivateKeyEntry.class)) {
1048                     kssave = installReply(importAlias, inStream);
1049                     if (kssave) {
1050                         System.err.println(rb.getString
1051                             ("Certificate.reply.was.installed.in.keystore"));
1052                     } else {
1053                         System.err.println(rb.getString
1054                             ("Certificate.reply.was.not.installed.in.keystore"));
1055                     }
1056                 } else if (!keyStore.containsAlias(importAlias) ||
1057                         keyStore.entryInstanceOf(importAlias,
1058                             KeyStore.TrustedCertificateEntry.class)) {
1059                     kssave = addTrustedCert(importAlias, inStream);
1060                     if (kssave) {
1061                         System.err.println(rb.getString
1062                             ("Certificate.was.added.to.keystore"));
1063                     } else {
1064                         System.err.println(rb.getString
1065                             ("Certificate.was.not.added.to.keystore"));
1066                     }
1067                 }
1068             } finally {
1069                 if (inStream != System.in) {
1070                     inStream.close();
1071                 }
1072             }
1073         } else if (command == IMPORTKEYSTORE) {
1074             // When not in-place import, srcKeyStore is not loaded yet.
1075             if (srcKeyStore == null) {
1076                 srcKeyStore = loadSourceKeyStore();
1077             }
1078             doImportKeyStore(srcKeyStore);
1079             kssave = true;
1080         } else if (command == KEYCLONE) {
1081             keyPassNew = newPass;
1082 
1083             // added to make sure only key can go thru
1084             if (alias == null) {
1085                 alias = keyAlias;
1086             }
1087             if (keyStore.containsAlias(alias) == false) {
1088                 MessageFormat form = new MessageFormat
1089                     (rb.getString("Alias.alias.does.not.exist"));
1090                 Object[] source = {alias};
1091                 throw new Exception(form.format(source));
1092             }
1093             if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
1094                 MessageFormat form = new MessageFormat(rb.getString(
1095                         "Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key"));
1096                 Object[] source = {alias};
1097                 throw new Exception(form.format(source));
1098             }
1099 
1100             doCloneEntry(alias, dest, true);  // Now everything can be cloned
1101             kssave = true;
1102         } else if (command == CHANGEALIAS) {
1103             if (alias == null) {
1104                 alias = keyAlias;
1105             }
1106             doCloneEntry(alias, dest, false);
1107             // in PKCS11, clone a PrivateKeyEntry will delete the old one
1108             if (keyStore.containsAlias(alias)) {
1109                 doDeleteEntry(alias);
1110             }
1111             kssave = true;
1112         } else if (command == KEYPASSWD) {
1113             keyPassNew = newPass;
1114             doChangeKeyPasswd(alias);
1115             kssave = true;
1116         } else if (command == LIST) {
1117             if (storePass == null
1118                     && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1119                 printNoIntegrityWarning();
1120             }
1121 
1122             if (alias != null) {
1123                 doPrintEntry(rb.getString("the.certificate"), alias, out);
1124             } else {
1125                 doPrintEntries(out);
1126             }
1127         } else if (command == PRINTCERT) {
1128             doPrintCert(out);
1129         } else if (command == SELFCERT) {
1130             doSelfCert(alias, dname, sigAlgName);
1131             kssave = true;
1132         } else if (command == STOREPASSWD) {
1133             storePassNew = newPass;
1134             if (storePassNew == null) {
1135                 storePassNew = getNewPasswd("keystore password", storePass);
1136             }
1137             kssave = true;
1138         } else if (command == GENCERT) {
1139             if (alias == null) {
1140                 alias = keyAlias;
1141             }
1142             InputStream inStream = System.in;
1143             if (infilename != null) {
1144                 inStream = new FileInputStream(infilename);
1145             }
1146             PrintStream ps = null;
1147             if (outfilename != null) {
1148                 ps = new PrintStream(new FileOutputStream(outfilename));
1149                 out = ps;
1150             }
1151             try {
1152                 doGenCert(alias, sigAlgName, inStream, out);
1153             } finally {
1154                 if (inStream != System.in) {
1155                     inStream.close();
1156                 }
1157                 if (ps != null) {
1158                     ps.close();
1159                 }
1160             }
1161         } else if (command == GENCRL) {
1162             if (alias == null) {
1163                 alias = keyAlias;
1164             }
1165             if (filename != null) {
1166                 try (PrintStream ps =
1167                          new PrintStream(new FileOutputStream(filename))) {
1168                     doGenCRL(ps);
1169                 }
1170             } else {
1171                 doGenCRL(out);
1172             }
1173         } else if (command == PRINTCERTREQ) {
1174             if (filename != null) {
1175                 try (InputStream inStream = new FileInputStream(filename)) {
1176                     doPrintCertReq(inStream, out);
1177                 }
1178             } else {
1179                 doPrintCertReq(System.in, out);
1180             }
1181         } else if (command == PRINTCRL) {
1182             doPrintCRL(filename, out);
1183         }
1184 
1185         // If we need to save the keystore, do so.
1186         if (kssave) {
1187             if (verbose) {
1188                 MessageFormat form = new MessageFormat
1189                         (rb.getString(".Storing.ksfname."));
1190                 Object[] source = {nullStream ? "keystore" : ksfname};
1191                 System.err.println(form.format(source));
1192             }
1193 
1194             if (token) {
1195                 keyStore.store(null, null);
1196             } else {
1197                 char[] pass = (storePassNew!=null) ? storePassNew : storePass;
1198                 if (nullStream) {
1199                     keyStore.store(null, pass);
1200                 } else {
1201                     ByteArrayOutputStream bout = new ByteArrayOutputStream();
1202                     keyStore.store(bout, pass);
1203                     try (FileOutputStream fout = new FileOutputStream(ksfname)) {
1204                         fout.write(bout.toByteArray());
1205                     }
1206                 }
1207             }
1208         }
1209 
1210         if (isKeyStoreRelated(command)
1211                 && !token && !nullStream && ksfname != null) {
1212 
1213             // JKS storetype warning on the final result keystore
1214             File f = new File(ksfname);
1215             if (f.exists()) {
1216                 // Read the first 4 bytes to determine
1217                 // if we're dealing with JKS/JCEKS type store
1218                 String realType = keyStoreType(f);
1219                 if (realType.equalsIgnoreCase("JKS")
1220                     || realType.equalsIgnoreCase("JCEKS")) {
1221                     boolean allCerts = true;
1222                     for (String a : Collections.list(keyStore.aliases())) {
1223                         if (!keyStore.entryInstanceOf(
1224                                 a, TrustedCertificateEntry.class)) {
1225                             allCerts = false;
1226                             break;
1227                         }
1228                     }
1229                     // Don't warn for "cacerts" style keystore.
1230                     if (!allCerts) {
1231                         weakWarnings.add(String.format(
1232                                 rb.getString("jks.storetype.warning"),
1233                                 realType, ksfname));
1234                     }
1235                 }
1236                 if (inplaceImport) {
1237                     String realSourceStoreType =
1238                         keyStoreType(new File(inplaceBackupName));
1239                     String format =
1240                             realType.equalsIgnoreCase(realSourceStoreType) ?
1241                             rb.getString("backup.keystore.warning") :
1242                             rb.getString("migrate.keystore.warning");
1243                     weakWarnings.add(
1244                             String.format(format,
1245                                     srcksfname,
1246                                     realSourceStoreType,
1247                                     inplaceBackupName,
1248                                     realType));
1249                 }
1250             }
1251         }
1252     }
1253 
keyStoreType(File f)1254     private String keyStoreType(File f) throws IOException {
1255         int MAGIC = 0xfeedfeed;
1256         int JCEKS_MAGIC = 0xcececece;
1257         try (DataInputStream dis = new DataInputStream(
1258             new FileInputStream(f))) {
1259             int xMagic = dis.readInt();
1260             if (xMagic == MAGIC) {
1261                 return "JKS";
1262             } else if (xMagic == JCEKS_MAGIC) {
1263                 return "JCEKS";
1264             } else {
1265                 return "Non JKS/JCEKS";
1266             }
1267         }
1268     }
1269 
1270     /**
1271      * Generate a certificate: Read PKCS10 request from in, and print
1272      * certificate to out. Use alias as CA, sigAlgName as the signature
1273      * type.
1274      */
doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)1275     private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)
1276             throws Exception {
1277 
1278 
1279         if (keyStore.containsAlias(alias) == false) {
1280             MessageFormat form = new MessageFormat
1281                     (rb.getString("Alias.alias.does.not.exist"));
1282             Object[] source = {alias};
1283             throw new Exception(form.format(source));
1284         }
1285         Certificate signerCert = keyStore.getCertificate(alias);
1286         byte[] encoded = signerCert.getEncoded();
1287         X509CertImpl signerCertImpl = new X509CertImpl(encoded);
1288         X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
1289                 X509CertImpl.NAME + "." + X509CertImpl.INFO);
1290         X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
1291                                            X509CertInfo.DN_NAME);
1292 
1293         Date firstDate = getStartDate(startDate);
1294         Date lastDate = getLastDate(firstDate, validity);
1295         CertificateValidity interval = new CertificateValidity(firstDate,
1296                                                                lastDate);
1297 
1298         PrivateKey privateKey =
1299                 (PrivateKey)recoverKey(alias, storePass, keyPass).fst;
1300         if (sigAlgName == null) {
1301             sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());
1302         }
1303         Signature signature = Signature.getInstance(sigAlgName);
1304         AlgorithmParameterSpec params = AlgorithmId
1305                 .getDefaultAlgorithmParameterSpec(sigAlgName, privateKey);
1306 
1307         SignatureUtil.initSignWithParam(signature, privateKey, params, null);
1308 
1309         X509CertInfo info = new X509CertInfo();
1310         AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params);
1311         info.set(X509CertInfo.VALIDITY, interval);
1312         info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
1313                     new java.util.Random().nextInt() & 0x7fffffff));
1314         info.set(X509CertInfo.VERSION,
1315                     new CertificateVersion(CertificateVersion.V3));
1316         info.set(X509CertInfo.ALGORITHM_ID,
1317                     new CertificateAlgorithmId(algID));
1318         info.set(X509CertInfo.ISSUER, issuer);
1319 
1320         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
1321         boolean canRead = false;
1322         StringBuffer sb = new StringBuffer();
1323         while (true) {
1324             String s = reader.readLine();
1325             if (s == null) break;
1326             // OpenSSL does not use NEW
1327             //if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {
1328             if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {
1329                 canRead = true;
1330             //} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {
1331             } else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {
1332                 break;
1333             } else if (canRead) {
1334                 sb.append(s);
1335             }
1336         }
1337         byte[] rawReq = Pem.decode(new String(sb));
1338         PKCS10 req = new PKCS10(rawReq);
1339 
1340         checkWeak(rb.getString("the.certificate.request"), req);
1341 
1342         info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
1343         info.set(X509CertInfo.SUBJECT,
1344                     dname==null?req.getSubjectName():new X500Name(dname));
1345         CertificateExtensions reqex = null;
1346         Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();
1347         while (attrs.hasNext()) {
1348             PKCS10Attribute attr = attrs.next();
1349             if (attr.getAttributeId().equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
1350                 reqex = (CertificateExtensions)attr.getAttributeValue();
1351             }
1352         }
1353         CertificateExtensions ext = createV3Extensions(
1354                 reqex,
1355                 null,
1356                 v3ext,
1357                 req.getSubjectPublicKeyInfo(),
1358                 signerCert.getPublicKey());
1359         info.set(X509CertInfo.EXTENSIONS, ext);
1360         X509CertImpl cert = new X509CertImpl(info);
1361         cert.sign(privateKey, params, sigAlgName, null);
1362         dumpCert(cert, out);
1363         for (Certificate ca: keyStore.getCertificateChain(alias)) {
1364             if (ca instanceof X509Certificate) {
1365                 X509Certificate xca = (X509Certificate)ca;
1366                 if (!KeyStoreUtil.isSelfSigned(xca)) {
1367                     dumpCert(xca, out);
1368                 }
1369             }
1370         }
1371 
1372         checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));
1373         checkWeak(rb.getString("the.generated.certificate"), cert);
1374     }
1375 
doGenCRL(PrintStream out)1376     private void doGenCRL(PrintStream out)
1377             throws Exception {
1378         if (ids == null) {
1379             throw new Exception("Must provide -id when -gencrl");
1380         }
1381         Certificate signerCert = keyStore.getCertificate(alias);
1382         byte[] encoded = signerCert.getEncoded();
1383         X509CertImpl signerCertImpl = new X509CertImpl(encoded);
1384         X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
1385                 X509CertImpl.NAME + "." + X509CertImpl.INFO);
1386         X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
1387                                                       X509CertInfo.DN_NAME);
1388 
1389         Date firstDate = getStartDate(startDate);
1390         Date lastDate = getLastDate(firstDate, validity);
1391         CertificateValidity interval = new CertificateValidity(firstDate,
1392                                                                lastDate);
1393 
1394         PrivateKey privateKey =
1395                 (PrivateKey)recoverKey(alias, storePass, keyPass).fst;
1396         if (sigAlgName == null) {
1397             sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());
1398         }
1399 
1400         X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];
1401         for (int i=0; i<ids.size(); i++) {
1402             String id = ids.get(i);
1403             int d = id.indexOf(':');
1404             if (d >= 0) {
1405                 CRLExtensions ext = new CRLExtensions();
1406                 ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));
1407                 badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),
1408                         firstDate, ext);
1409             } else {
1410                 badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);
1411             }
1412         }
1413         X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);
1414         crl.sign(privateKey, sigAlgName);
1415         if (rfc) {
1416             out.println("-----BEGIN X509 CRL-----");
1417             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));
1418             out.println("-----END X509 CRL-----");
1419         } else {
1420             out.write(crl.getEncodedInternal());
1421         }
1422         checkWeak(rb.getString("the.generated.crl"), crl, privateKey);
1423     }
1424 
1425     /**
1426      * Creates a PKCS#10 cert signing request, corresponding to the
1427      * keys (and name) associated with a given alias.
1428      */
doCertReq(String alias, String sigAlgName, PrintStream out)1429     private void doCertReq(String alias, String sigAlgName, PrintStream out)
1430         throws Exception
1431     {
1432         if (alias == null) {
1433             alias = keyAlias;
1434         }
1435 
1436         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
1437         PrivateKey privKey = (PrivateKey)objs.fst;
1438         if (keyPass == null) {
1439             keyPass = objs.snd;
1440         }
1441 
1442         Certificate cert = keyStore.getCertificate(alias);
1443         if (cert == null) {
1444             MessageFormat form = new MessageFormat
1445                 (rb.getString("alias.has.no.public.key.certificate."));
1446             Object[] source = {alias};
1447             throw new Exception(form.format(source));
1448         }
1449         PKCS10 request = new PKCS10(cert.getPublicKey());
1450         CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);
1451         // Attribute name is not significant
1452         request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,
1453                 new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));
1454 
1455         // Construct a Signature object, so that we can sign the request
1456         if (sigAlgName == null) {
1457             sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
1458         }
1459 
1460         Signature signature = Signature.getInstance(sigAlgName);
1461         AlgorithmParameterSpec params = AlgorithmId
1462                 .getDefaultAlgorithmParameterSpec(sigAlgName, privKey);
1463         SignatureUtil.initSignWithParam(signature, privKey, params, null);
1464 
1465         X500Name subject = dname == null?
1466                 new X500Name(((X509Certificate)cert).getSubjectDN().toString()):
1467                 new X500Name(dname);
1468 
1469         // Sign the request and base-64 encode it
1470         request.encodeAndSign(subject, signature);
1471         request.print(out);
1472 
1473         checkWeak(rb.getString("the.generated.certificate.request"), request);
1474     }
1475 
1476     /**
1477      * Deletes an entry from the keystore.
1478      */
doDeleteEntry(String alias)1479     private void doDeleteEntry(String alias) throws Exception {
1480         if (keyStore.containsAlias(alias) == false) {
1481             MessageFormat form = new MessageFormat
1482                 (rb.getString("Alias.alias.does.not.exist"));
1483             Object[] source = {alias};
1484             throw new Exception(form.format(source));
1485         }
1486         keyStore.deleteEntry(alias);
1487     }
1488 
1489     /**
1490      * Exports a certificate from the keystore.
1491      */
doExportCert(String alias, PrintStream out)1492     private void doExportCert(String alias, PrintStream out)
1493         throws Exception
1494     {
1495         if (storePass == null
1496                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1497             printNoIntegrityWarning();
1498         }
1499         if (alias == null) {
1500             alias = keyAlias;
1501         }
1502         if (keyStore.containsAlias(alias) == false) {
1503             MessageFormat form = new MessageFormat
1504                 (rb.getString("Alias.alias.does.not.exist"));
1505             Object[] source = {alias};
1506             throw new Exception(form.format(source));
1507         }
1508 
1509         X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
1510         if (cert == null) {
1511             MessageFormat form = new MessageFormat
1512                 (rb.getString("Alias.alias.has.no.certificate"));
1513             Object[] source = {alias};
1514             throw new Exception(form.format(source));
1515         }
1516         dumpCert(cert, out);
1517         checkWeak(rb.getString("the.certificate"), cert);
1518     }
1519 
1520     /**
1521      * Prompt the user for a keypass when generating a key entry.
1522      * @param alias the entry we will set password for
1523      * @param orig the original entry of doing a dup, null if generate new
1524      * @param origPass the password to copy from if user press ENTER
1525      */
promptForKeyPass(String alias, String orig, char[] origPass)1526     private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{
1527         if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
1528             return origPass;
1529         } else if (!token && !protectedPath) {
1530             // Prompt for key password
1531             int count;
1532             for (count = 0; count < 3; count++) {
1533                 MessageFormat form = new MessageFormat(rb.getString
1534                         ("Enter.key.password.for.alias."));
1535                 Object[] source = {alias};
1536                 System.err.println(form.format(source));
1537                 if (orig == null) {
1538                     System.err.print(rb.getString
1539                             (".RETURN.if.same.as.keystore.password."));
1540                 } else {
1541                     form = new MessageFormat(rb.getString
1542                             (".RETURN.if.same.as.for.otherAlias."));
1543                     Object[] src = {orig};
1544                     System.err.print(form.format(src));
1545                 }
1546                 System.err.flush();
1547                 char[] entered = Password.readPassword(System.in);
1548                 passwords.add(entered);
1549                 if (entered == null) {
1550                     return origPass;
1551                 } else if (entered.length >= 6) {
1552                     System.err.print(rb.getString("Re.enter.new.password."));
1553                     char[] passAgain = Password.readPassword(System.in);
1554                     passwords.add(passAgain);
1555                     if (!Arrays.equals(entered, passAgain)) {
1556                         System.err.println
1557                             (rb.getString("They.don.t.match.Try.again"));
1558                         continue;
1559                     }
1560                     return entered;
1561                 } else {
1562                     System.err.println(rb.getString
1563                         ("Key.password.is.too.short.must.be.at.least.6.characters"));
1564                 }
1565             }
1566             if (count == 3) {
1567                 if (command == KEYCLONE) {
1568                     throw new Exception(rb.getString
1569                         ("Too.many.failures.Key.entry.not.cloned"));
1570                 } else {
1571                     throw new Exception(rb.getString
1572                             ("Too.many.failures.key.not.added.to.keystore"));
1573                 }
1574             }
1575         }
1576         return null;    // PKCS11, MSCAPI, or -protected
1577     }
1578 
1579     /*
1580      * Prompt the user for the password credential to be stored.
1581      */
promptForCredential()1582     private char[] promptForCredential() throws Exception {
1583         // Handle password supplied via stdin
1584         if (System.console() == null) {
1585             char[] importPass = Password.readPassword(System.in);
1586             passwords.add(importPass);
1587             return importPass;
1588         }
1589 
1590         int count;
1591         for (count = 0; count < 3; count++) {
1592             System.err.print(
1593                 rb.getString("Enter.the.password.to.be.stored."));
1594             System.err.flush();
1595             char[] entered = Password.readPassword(System.in);
1596             passwords.add(entered);
1597             System.err.print(rb.getString("Re.enter.password."));
1598             char[] passAgain = Password.readPassword(System.in);
1599             passwords.add(passAgain);
1600             if (!Arrays.equals(entered, passAgain)) {
1601                 System.err.println(rb.getString("They.don.t.match.Try.again"));
1602                 continue;
1603             }
1604             return entered;
1605         }
1606 
1607         if (count == 3) {
1608             throw new Exception(rb.getString
1609                 ("Too.many.failures.key.not.added.to.keystore"));
1610         }
1611 
1612         return null;
1613     }
1614 
1615     /**
1616      * Creates a new secret key.
1617      */
doGenSecretKey(String alias, String keyAlgName, int keysize)1618     private void doGenSecretKey(String alias, String keyAlgName,
1619                               int keysize)
1620         throws Exception
1621     {
1622         if (alias == null) {
1623             alias = keyAlias;
1624         }
1625         if (keyStore.containsAlias(alias)) {
1626             MessageFormat form = new MessageFormat(rb.getString
1627                 ("Secret.key.not.generated.alias.alias.already.exists"));
1628             Object[] source = {alias};
1629             throw new Exception(form.format(source));
1630         }
1631 
1632         // Use the keystore's default PBE algorithm for entry protection
1633         boolean useDefaultPBEAlgorithm = true;
1634         SecretKey secKey = null;
1635 
1636         if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) {
1637             SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
1638 
1639             // User is prompted for PBE credential
1640             secKey =
1641                 factory.generateSecret(new PBEKeySpec(promptForCredential()));
1642 
1643             // Check whether a specific PBE algorithm was specified
1644             if (!"PBE".equalsIgnoreCase(keyAlgName)) {
1645                 useDefaultPBEAlgorithm = false;
1646             }
1647 
1648             if (verbose) {
1649                 MessageFormat form = new MessageFormat(rb.getString(
1650                     "Generated.keyAlgName.secret.key"));
1651                 Object[] source =
1652                     {useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};
1653                 System.err.println(form.format(source));
1654             }
1655         } else {
1656             KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);
1657             if (keysize == -1) {
1658                 if ("DES".equalsIgnoreCase(keyAlgName)) {
1659                     keysize = 56;
1660                 } else if ("DESede".equalsIgnoreCase(keyAlgName)) {
1661                     keysize = 168;
1662                 } else {
1663                     throw new Exception(rb.getString
1664                         ("Please.provide.keysize.for.secret.key.generation"));
1665                 }
1666             }
1667             keygen.init(keysize);
1668             secKey = keygen.generateKey();
1669 
1670             if (verbose) {
1671                 MessageFormat form = new MessageFormat(rb.getString
1672                     ("Generated.keysize.bit.keyAlgName.secret.key"));
1673                 Object[] source = {new Integer(keysize),
1674                                     secKey.getAlgorithm()};
1675                 System.err.println(form.format(source));
1676             }
1677         }
1678 
1679         if (keyPass == null) {
1680             keyPass = promptForKeyPass(alias, null, storePass);
1681         }
1682 
1683         if (useDefaultPBEAlgorithm) {
1684             keyStore.setKeyEntry(alias, secKey, keyPass, null);
1685         } else {
1686             keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),
1687                 new KeyStore.PasswordProtection(keyPass, keyAlgName, null));
1688         }
1689     }
1690 
1691     /**
1692      * If no signature algorithm was specified at the command line,
1693      * we choose one that is compatible with the selected private key
1694      */
getCompatibleSigAlgName(String keyAlgName)1695     private static String getCompatibleSigAlgName(String keyAlgName)
1696             throws Exception {
1697         if ("DSA".equalsIgnoreCase(keyAlgName)) {
1698             return "SHA256WithDSA";
1699         } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
1700             return "SHA256WithRSA";
1701         } else if ("EC".equalsIgnoreCase(keyAlgName)) {
1702             return "SHA256withECDSA";
1703         } else {
1704             throw new Exception(rb.getString
1705                     ("Cannot.derive.signature.algorithm"));
1706         }
1707     }
1708     /**
1709      * Creates a new key pair and self-signed certificate.
1710      */
doGenKeyPair(String alias, String dname, String keyAlgName, int keysize, String sigAlgName)1711     private void doGenKeyPair(String alias, String dname, String keyAlgName,
1712                               int keysize, String sigAlgName)
1713         throws Exception
1714     {
1715         if (keysize == -1) {
1716             if ("EC".equalsIgnoreCase(keyAlgName)) {
1717                 keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE;
1718             } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
1719                 keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;
1720             } else if ("RSASSA-PSS".equalsIgnoreCase(keyAlgName)) {
1721                 keysize = SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE;
1722             } else if ("DSA".equalsIgnoreCase(keyAlgName)) {
1723                 keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;
1724             }
1725         }
1726 
1727         if (alias == null) {
1728             alias = keyAlias;
1729         }
1730 
1731         if (keyStore.containsAlias(alias)) {
1732             MessageFormat form = new MessageFormat(rb.getString
1733                 ("Key.pair.not.generated.alias.alias.already.exists"));
1734             Object[] source = {alias};
1735             throw new Exception(form.format(source));
1736         }
1737 
1738         if (sigAlgName == null) {
1739             sigAlgName = getCompatibleSigAlgName(keyAlgName);
1740         }
1741         CertAndKeyGen keypair =
1742                 new CertAndKeyGen(keyAlgName, sigAlgName, providerName);
1743 
1744 
1745         // If DN is provided, parse it. Otherwise, prompt the user for it.
1746         X500Name x500Name;
1747         if (dname == null) {
1748             x500Name = getX500Name();
1749         } else {
1750             x500Name = new X500Name(dname);
1751         }
1752 
1753         keypair.generate(keysize);
1754         PrivateKey privKey = keypair.getPrivateKey();
1755 
1756         CertificateExtensions ext = createV3Extensions(
1757                 null,
1758                 null,
1759                 v3ext,
1760                 keypair.getPublicKeyAnyway(),
1761                 null);
1762 
1763         X509Certificate[] chain = new X509Certificate[1];
1764         chain[0] = keypair.getSelfCertificate(
1765                 x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);
1766 
1767         if (verbose) {
1768             MessageFormat form = new MessageFormat(rb.getString
1769                 ("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));
1770             Object[] source = {new Integer(keysize),
1771                                 privKey.getAlgorithm(),
1772                                 chain[0].getSigAlgName(),
1773                                 new Long(validity),
1774                                 x500Name};
1775             System.err.println(form.format(source));
1776         }
1777 
1778         if (keyPass == null) {
1779             keyPass = promptForKeyPass(alias, null, storePass);
1780         }
1781         checkWeak(rb.getString("the.generated.certificate"), chain[0]);
1782         keyStore.setKeyEntry(alias, privKey, keyPass, chain);
1783     }
1784 
1785     /**
1786      * Clones an entry
1787      * @param orig original alias
1788      * @param dest destination alias
1789      * @changePassword if the password can be changed
1790      */
doCloneEntry(String orig, String dest, boolean changePassword)1791     private void doCloneEntry(String orig, String dest, boolean changePassword)
1792         throws Exception
1793     {
1794         if (orig == null) {
1795             orig = keyAlias;
1796         }
1797 
1798         if (keyStore.containsAlias(dest)) {
1799             MessageFormat form = new MessageFormat
1800                 (rb.getString("Destination.alias.dest.already.exists"));
1801             Object[] source = {dest};
1802             throw new Exception(form.format(source));
1803         }
1804 
1805         Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);
1806         Entry entry = objs.fst;
1807         keyPass = objs.snd;
1808 
1809         PasswordProtection pp = null;
1810 
1811         if (keyPass != null) {  // protected
1812             if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {
1813                 keyPassNew = keyPass;
1814             } else {
1815                 if (keyPassNew == null) {
1816                     keyPassNew = promptForKeyPass(dest, orig, keyPass);
1817                 }
1818             }
1819             pp = new PasswordProtection(keyPassNew);
1820         }
1821         keyStore.setEntry(dest, entry, pp);
1822     }
1823 
1824     /**
1825      * Changes a key password.
1826      */
doChangeKeyPasswd(String alias)1827     private void doChangeKeyPasswd(String alias) throws Exception
1828     {
1829 
1830         if (alias == null) {
1831             alias = keyAlias;
1832         }
1833         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
1834         Key privKey = objs.fst;
1835         if (keyPass == null) {
1836             keyPass = objs.snd;
1837         }
1838 
1839         if (keyPassNew == null) {
1840             MessageFormat form = new MessageFormat
1841                 (rb.getString("key.password.for.alias."));
1842             Object[] source = {alias};
1843             keyPassNew = getNewPasswd(form.format(source), keyPass);
1844         }
1845         keyStore.setKeyEntry(alias, privKey, keyPassNew,
1846                              keyStore.getCertificateChain(alias));
1847     }
1848 
1849     /**
1850      * Imports a JDK 1.1-style identity database. We can only store one
1851      * certificate per identity, because we use the identity's name as the
1852      * alias (which references a keystore entry), and aliases must be unique.
1853      */
doImportIdentityDatabase(InputStream in)1854     private void doImportIdentityDatabase(InputStream in)
1855         throws Exception
1856     {
1857         System.err.println(rb.getString
1858             ("No.entries.from.identity.database.added"));
1859     }
1860 
1861     /**
1862      * Prints a single keystore entry.
1863      */
doPrintEntry(String label, String alias, PrintStream out)1864     private void doPrintEntry(String label, String alias, PrintStream out)
1865         throws Exception
1866     {
1867         if (keyStore.containsAlias(alias) == false) {
1868             MessageFormat form = new MessageFormat
1869                 (rb.getString("Alias.alias.does.not.exist"));
1870             Object[] source = {alias};
1871             throw new Exception(form.format(source));
1872         }
1873 
1874         if (verbose || rfc || debug) {
1875             MessageFormat form = new MessageFormat
1876                 (rb.getString("Alias.name.alias"));
1877             Object[] source = {alias};
1878             out.println(form.format(source));
1879 
1880             if (!token) {
1881                 form = new MessageFormat(rb.getString
1882                     ("Creation.date.keyStore.getCreationDate.alias."));
1883                 Object[] src = {keyStore.getCreationDate(alias)};
1884                 out.println(form.format(src));
1885             }
1886         } else {
1887             if (!token) {
1888                 MessageFormat form = new MessageFormat
1889                     (rb.getString("alias.keyStore.getCreationDate.alias."));
1890                 Object[] source = {alias, keyStore.getCreationDate(alias)};
1891                 out.print(form.format(source));
1892             } else {
1893                 MessageFormat form = new MessageFormat
1894                     (rb.getString("alias."));
1895                 Object[] source = {alias};
1896                 out.print(form.format(source));
1897             }
1898         }
1899 
1900         if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
1901             if (verbose || rfc || debug) {
1902                 Object[] source = {"SecretKeyEntry"};
1903                 out.println(new MessageFormat(
1904                         rb.getString("Entry.type.type.")).format(source));
1905             } else {
1906                 out.println("SecretKeyEntry, ");
1907             }
1908         } else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
1909             if (verbose || rfc || debug) {
1910                 Object[] source = {"PrivateKeyEntry"};
1911                 out.println(new MessageFormat(
1912                         rb.getString("Entry.type.type.")).format(source));
1913             } else {
1914                 out.println("PrivateKeyEntry, ");
1915             }
1916 
1917             // Get the chain
1918             Certificate[] chain = keyStore.getCertificateChain(alias);
1919             if (chain != null) {
1920                 if (verbose || rfc || debug) {
1921                     out.println(rb.getString
1922                         ("Certificate.chain.length.") + chain.length);
1923                     for (int i = 0; i < chain.length; i ++) {
1924                         MessageFormat form = new MessageFormat
1925                                 (rb.getString("Certificate.i.1."));
1926                         Object[] source = {new Integer((i + 1))};
1927                         out.println(form.format(source));
1928                         if (verbose && (chain[i] instanceof X509Certificate)) {
1929                             printX509Cert((X509Certificate)(chain[i]), out);
1930                         } else if (debug) {
1931                             out.println(chain[i].toString());
1932                         } else {
1933                             dumpCert(chain[i], out);
1934                         }
1935                         checkWeak(label, chain[i]);
1936                     }
1937                 } else {
1938                     // Print the digest of the user cert only
1939                     out.println
1940                         (rb.getString("Certificate.fingerprint.SHA.256.") +
1941                         getCertFingerPrint("SHA-256", chain[0]));
1942                     checkWeak(label, chain[0]);
1943                 }
1944             }
1945         } else if (keyStore.entryInstanceOf(alias,
1946                 KeyStore.TrustedCertificateEntry.class)) {
1947             // We have a trusted certificate entry
1948             Certificate cert = keyStore.getCertificate(alias);
1949             Object[] source = {"trustedCertEntry"};
1950             String mf = new MessageFormat(
1951                     rb.getString("Entry.type.type.")).format(source) + "\n";
1952             if (verbose && (cert instanceof X509Certificate)) {
1953                 out.println(mf);
1954                 printX509Cert((X509Certificate)cert, out);
1955             } else if (rfc) {
1956                 out.println(mf);
1957                 dumpCert(cert, out);
1958             } else if (debug) {
1959                 out.println(cert.toString());
1960             } else {
1961                 out.println("trustedCertEntry, ");
1962                 out.println(rb.getString("Certificate.fingerprint.SHA.256.")
1963                             + getCertFingerPrint("SHA-256", cert));
1964             }
1965             checkWeak(label, cert);
1966         } else {
1967             out.println(rb.getString("Unknown.Entry.Type"));
1968         }
1969     }
1970 
inplaceImportCheck()1971     boolean inplaceImportCheck() throws Exception {
1972         if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
1973                 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
1974             return false;
1975         }
1976 
1977         if (srcksfname != null) {
1978             File srcksfile = new File(srcksfname);
1979             if (srcksfile.exists() && srcksfile.length() == 0) {
1980                 throw new Exception(rb.getString
1981                         ("Source.keystore.file.exists.but.is.empty.") +
1982                         srcksfname);
1983             }
1984             if (srcksfile.getCanonicalFile()
1985                     .equals(new File(ksfname).getCanonicalFile())) {
1986                 return true;
1987             } else {
1988                 // Informational, especially if destkeystore is not
1989                 // provided, which default to ~/.keystore.
1990                 System.err.println(String.format(rb.getString(
1991                         "importing.keystore.status"), srcksfname, ksfname));
1992                 return false;
1993             }
1994         } else {
1995             throw new Exception(rb.getString
1996                     ("Please.specify.srckeystore"));
1997         }
1998     }
1999 
2000     /**
2001      * Load the srckeystore from a stream, used in -importkeystore
2002      * @returns the src KeyStore
2003      */
loadSourceKeyStore()2004     KeyStore loadSourceKeyStore() throws Exception {
2005 
2006         InputStream is = null;
2007         File srcksfile = null;
2008 
2009         if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
2010                 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2011             if (!NONE.equals(srcksfname)) {
2012                 System.err.println(MessageFormat.format(rb.getString
2013                     (".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype));
2014                 System.err.println();
2015                 tinyHelp();
2016             }
2017         } else {
2018             srcksfile = new File(srcksfname);
2019                 is = new FileInputStream(srcksfile);
2020         }
2021 
2022         KeyStore store;
2023         try {
2024             if (srcstoretype == null) {
2025                 srcstoretype = KeyStore.getDefaultType();
2026             }
2027             if (srcProviderName == null) {
2028                 store = KeyStore.getInstance(srcstoretype);
2029             } else {
2030                 store = KeyStore.getInstance(srcstoretype, srcProviderName);
2031             }
2032 
2033             if (srcstorePass == null
2034                     && !srcprotectedPath
2035                     && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2036                 System.err.print(rb.getString("Enter.source.keystore.password."));
2037                 System.err.flush();
2038                 srcstorePass = Password.readPassword(System.in);
2039                 passwords.add(srcstorePass);
2040             }
2041 
2042             // always let keypass be storepass when using pkcs12
2043             if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {
2044                 if (srckeyPass != null && srcstorePass != null &&
2045                         !Arrays.equals(srcstorePass, srckeyPass)) {
2046                     MessageFormat form = new MessageFormat(rb.getString(
2047                         "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));
2048                     Object[] source = {"-srckeypass"};
2049                     System.err.println(form.format(source));
2050                     srckeyPass = srcstorePass;
2051                 }
2052             }
2053 
2054             store.load(is, srcstorePass);   // "is" already null in PKCS11
2055         } finally {
2056             if (is != null) {
2057                 is.close();
2058             }
2059         }
2060 
2061         if (srcstorePass == null
2062                 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2063             // anti refactoring, copied from printNoIntegrityWarning(),
2064             // but change 2 lines
2065             System.err.println();
2066             System.err.println(rb.getString
2067                 (".WARNING.WARNING.WARNING."));
2068             System.err.println(rb.getString
2069                 (".The.integrity.of.the.information.stored.in.the.srckeystore."));
2070             System.err.println(rb.getString
2071                 (".WARNING.WARNING.WARNING."));
2072             System.err.println();
2073         }
2074 
2075         return store;
2076     }
2077 
2078     /**
2079      * import all keys and certs from importkeystore.
2080      * keep alias unchanged if no name conflict, otherwise, prompt.
2081      * keep keypass unchanged for keys
2082      */
doImportKeyStore(KeyStore srcKS)2083     private void doImportKeyStore(KeyStore srcKS) throws Exception {
2084 
2085         if (alias != null) {
2086             doImportKeyStoreSingle(srcKS, alias);
2087         } else {
2088             if (dest != null || srckeyPass != null) {
2089                 throw new Exception(rb.getString(
2090                         "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));
2091             }
2092             doImportKeyStoreAll(srcKS);
2093         }
2094 
2095         if (inplaceImport) {
2096             // Backup to file.old or file.old2...
2097             // The keystore is not rewritten yet now.
2098             for (int n = 1; /* forever */; n++) {
2099                 inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);
2100                 File bkFile = new File(inplaceBackupName);
2101                 if (!bkFile.exists()) {
2102                     Files.copy(Paths.get(srcksfname), bkFile.toPath());
2103                     break;
2104                 }
2105             }
2106 
2107         }
2108 
2109         /*
2110          * Information display rule of -importkeystore
2111          * 1. inside single, shows failure
2112          * 2. inside all, shows sucess
2113          * 3. inside all where there is a failure, prompt for continue
2114          * 4. at the final of all, shows summary
2115          */
2116     }
2117 
2118     /**
2119      * Import a single entry named alias from srckeystore
2120      * @returns 1 if the import action succeed
2121      *          0 if user choose to ignore an alias-dumplicated entry
2122      *          2 if setEntry throws Exception
2123      */
doImportKeyStoreSingle(KeyStore srckeystore, String alias)2124     private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)
2125             throws Exception {
2126 
2127         String newAlias = (dest==null) ? alias : dest;
2128 
2129         if (keyStore.containsAlias(newAlias)) {
2130             Object[] source = {alias};
2131             if (noprompt) {
2132                 System.err.println(new MessageFormat(rb.getString(
2133                         "Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source));
2134             } else {
2135                 String reply = getYesNoReply(new MessageFormat(rb.getString(
2136                         "Existing.entry.alias.alias.exists.overwrite.no.")).format(source));
2137                 if ("NO".equals(reply)) {
2138                     newAlias = inputStringFromStdin(rb.getString
2139                             ("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry."));
2140                     if ("".equals(newAlias)) {
2141                         System.err.println(new MessageFormat(rb.getString(
2142                                 "Entry.for.alias.alias.not.imported.")).format(
2143                                 source));
2144                         return 0;
2145                     }
2146                 }
2147             }
2148         }
2149 
2150         Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
2151         Entry entry = objs.fst;
2152 
2153         PasswordProtection pp = null;
2154 
2155         // According to keytool.html, "The destination entry will be protected
2156         // using destkeypass. If destkeypass is not provided, the destination
2157         // entry will be protected with the source entry password."
2158         // so always try to protect with destKeyPass.
2159         char[] newPass = null;
2160         if (destKeyPass != null) {
2161             newPass = destKeyPass;
2162             pp = new PasswordProtection(destKeyPass);
2163         } else if (objs.snd != null) {
2164             newPass = objs.snd;
2165             pp = new PasswordProtection(objs.snd);
2166         }
2167 
2168         try {
2169             Certificate c = srckeystore.getCertificate(alias);
2170             if (c != null) {
2171                 checkWeak("<" + newAlias + ">", c);
2172             }
2173             keyStore.setEntry(newAlias, entry, pp);
2174             // Place the check so that only successful imports are blocked.
2175             // For example, we don't block a failed SecretEntry import.
2176             if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
2177                 if (newPass != null && !Arrays.equals(newPass, storePass)) {
2178                     throw new Exception(rb.getString(
2179                             "The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));
2180                 }
2181             }
2182             return 1;
2183         } catch (KeyStoreException kse) {
2184             Object[] source2 = {alias, kse.toString()};
2185             MessageFormat form = new MessageFormat(rb.getString(
2186                     "Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported."));
2187             System.err.println(form.format(source2));
2188             return 2;
2189         }
2190     }
2191 
doImportKeyStoreAll(KeyStore srckeystore)2192     private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {
2193 
2194         int ok = 0;
2195         int count = srckeystore.size();
2196         for (Enumeration<String> e = srckeystore.aliases();
2197                                         e.hasMoreElements(); ) {
2198             String alias = e.nextElement();
2199             int result = doImportKeyStoreSingle(srckeystore, alias);
2200             if (result == 1) {
2201                 ok++;
2202                 Object[] source = {alias};
2203                 MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));
2204                 System.err.println(form.format(source));
2205             } else if (result == 2) {
2206                 if (!noprompt) {
2207                     String reply = getYesNoReply("Do you want to quit the import process? [no]:  ");
2208                     if ("YES".equals(reply)) {
2209                         break;
2210                     }
2211                 }
2212             }
2213         }
2214         Object[] source = {ok, count-ok};
2215         MessageFormat form = new MessageFormat(rb.getString(
2216                 "Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled"));
2217         System.err.println(form.format(source));
2218     }
2219 
2220     /**
2221      * Prints all keystore entries.
2222      */
doPrintEntries(PrintStream out)2223     private void doPrintEntries(PrintStream out)
2224         throws Exception
2225     {
2226         // Adjust displayed keystore type if needed.
2227         String keystoreTypeToPrint = keyStore.getType();
2228         if ("JKS".equalsIgnoreCase(keystoreTypeToPrint)) {
2229             if (ksfile != null && ksfile.exists()) {
2230                 String realType = keyStoreType(ksfile);
2231                 // If the magic number does not conform to JKS
2232                 // then it must be PKCS12
2233                 if (!"JKS".equalsIgnoreCase(realType)) {
2234                     keystoreTypeToPrint = P12KEYSTORE;
2235                 }
2236             }
2237         }
2238         out.println(rb.getString("Keystore.type.") + keystoreTypeToPrint);
2239         out.println(rb.getString("Keystore.provider.") +
2240                 keyStore.getProvider().getName());
2241         out.println();
2242 
2243         MessageFormat form;
2244         form = (keyStore.size() == 1) ?
2245                 new MessageFormat(rb.getString
2246                         ("Your.keystore.contains.keyStore.size.entry")) :
2247                 new MessageFormat(rb.getString
2248                         ("Your.keystore.contains.keyStore.size.entries"));
2249         Object[] source = {new Integer(keyStore.size())};
2250         out.println(form.format(source));
2251         out.println();
2252 
2253         List<String> aliases = Collections.list(keyStore.aliases());
2254         aliases.sort(String::compareTo);
2255         for (String alias : aliases) {
2256             doPrintEntry("<" + alias + ">", alias, out);
2257             if (verbose || rfc) {
2258                 out.println(rb.getString("NEWLINE"));
2259                 out.println(rb.getString
2260                         ("STAR"));
2261                 out.println(rb.getString
2262                         ("STARNN"));
2263             }
2264         }
2265     }
2266 
e2i(final Enumeration<T> e)2267     private static <T> Iterable<T> e2i(final Enumeration<T> e) {
2268         return new Iterable<T>() {
2269             @Override
2270             public Iterator<T> iterator() {
2271                 return new Iterator<T>() {
2272                     @Override
2273                     public boolean hasNext() {
2274                         return e.hasMoreElements();
2275                     }
2276                     @Override
2277                     public T next() {
2278                         return e.nextElement();
2279                     }
2280                     public void remove() {
2281                         throw new UnsupportedOperationException("Not supported yet.");
2282                     }
2283                 };
2284             }
2285         };
2286     }
2287 
2288     /**
2289      * Loads CRLs from a source. This method is also called in JarSigner.
2290      * @param src the source, which means System.in if null, or a URI,
2291      *        or a bare file path name
2292      */
2293     public static Collection<? extends CRL> loadCRLs(String src) throws Exception {
2294         InputStream in = null;
2295         URI uri = null;
2296         if (src == null) {
2297             in = System.in;
2298         } else {
2299             try {
2300                 uri = new URI(src);
2301                 if (uri.getScheme().equals("ldap")) {
2302                     // No input stream for LDAP
2303                 } else {
2304                     in = uri.toURL().openStream();
2305                 }
2306             } catch (Exception e) {
2307                 try {
2308                     in = new FileInputStream(src);
2309                 } catch (Exception e2) {
2310                     if (uri == null || uri.getScheme() == null) {
2311                         throw e2;   // More likely a bare file path
2312                     } else {
2313                         throw e;    // More likely a protocol or network problem
2314                     }
2315                 }
2316             }
2317         }
2318         if (in != null) {
2319             try {
2320                 // Read the full stream before feeding to X509Factory,
2321                 // otherwise, keytool -gencrl | keytool -printcrl
2322                 // might not work properly, since -gencrl is slow
2323                 // and there's no data in the pipe at the beginning.
2324                 ByteArrayOutputStream bout = new ByteArrayOutputStream();
2325                 byte[] b = new byte[4096];
2326                 while (true) {
2327                     int len = in.read(b);
2328                     if (len < 0) break;
2329                     bout.write(b, 0, len);
2330                 }
2331                 return CertificateFactory.getInstance("X509").generateCRLs(
2332                         new ByteArrayInputStream(bout.toByteArray()));
2333             } finally {
2334                 if (in != System.in) {
2335                     in.close();
2336                 }
2337             }
2338         } else {    // must be LDAP, and uri is not null
2339             // Lazily load LDAPCertStoreHelper if present
2340             CertStoreHelper helper = CertStoreHelper.getInstance("LDAP");
2341             String path = uri.getPath();
2342             if (path.charAt(0) == '/') path = path.substring(1);
2343             CertStore s = helper.getCertStore(uri);
2344             X509CRLSelector sel =
2345                     helper.wrap(new X509CRLSelector(), null, path);
2346             return s.getCRLs(sel);
2347         }
2348     }
2349 
2350     /**
2351      * Returns CRLs described in a X509Certificate's CRLDistributionPoints
2352      * Extension. Only those containing a general name of type URI are read.
2353      */
2354     public static List<CRL> readCRLsFromCert(X509Certificate cert)
2355             throws Exception {
2356         List<CRL> crls = new ArrayList<>();
2357         CRLDistributionPointsExtension ext =
2358                 X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension();
2359         if (ext == null) return crls;
2360         List<DistributionPoint> distPoints =
2361                 ext.get(CRLDistributionPointsExtension.POINTS);
2362         for (DistributionPoint o: distPoints) {
2363             GeneralNames names = o.getFullName();
2364             if (names != null) {
2365                 for (GeneralName name: names.names()) {
2366                     if (name.getType() == GeneralNameInterface.NAME_URI) {
2367                         URIName uriName = (URIName)name.getName();
2368                         for (CRL crl: loadCRLs(uriName.getName())) {
2369                             if (crl instanceof X509CRL) {
2370                                 crls.add((X509CRL)crl);
2371                             }
2372                         }
2373                         break;  // Different name should point to same CRL
2374                     }
2375                 }
2376             }
2377         }
2378         return crls;
2379     }
2380 
2381     private static String verifyCRL(KeyStore ks, CRL crl)
2382             throws Exception {
2383         X509CRLImpl xcrl = (X509CRLImpl)crl;
2384         X500Principal issuer = xcrl.getIssuerX500Principal();
2385         for (String s: e2i(ks.aliases())) {
2386             Certificate cert = ks.getCertificate(s);
2387             if (cert instanceof X509Certificate) {
2388                 X509Certificate xcert = (X509Certificate)cert;
2389                 if (xcert.getSubjectX500Principal().equals(issuer)) {
2390                     try {
2391                         ((X509CRLImpl)crl).verify(cert.getPublicKey());
2392                         return s;
2393                     } catch (Exception e) {
2394                     }
2395                 }
2396             }
2397         }
2398         return null;
2399     }
2400 
2401     private void doPrintCRL(String src, PrintStream out)
2402             throws Exception {
2403         for (CRL crl: loadCRLs(src)) {
2404             printCRL(crl, out);
2405             String issuer = null;
2406             Certificate signer = null;
2407             if (caks != null) {
2408                 issuer = verifyCRL(caks, crl);
2409                 if (issuer != null) {
2410                     signer = caks.getCertificate(issuer);
2411                     out.printf(rb.getString(
2412                             "verified.by.s.in.s.weak"),
2413                             issuer,
2414                             "cacerts",
2415                             withWeak(signer.getPublicKey()));
2416                     out.println();
2417                 }
2418             }
2419             if (issuer == null && keyStore != null) {
2420                 issuer = verifyCRL(keyStore, crl);
2421                 if (issuer != null) {
2422                     signer = keyStore.getCertificate(issuer);
2423                     out.printf(rb.getString(
2424                             "verified.by.s.in.s.weak"),
2425                             issuer,
2426                             "keystore",
2427                             withWeak(signer.getPublicKey()));
2428                     out.println();
2429                 }
2430             }
2431             if (issuer == null) {
2432                 out.println(rb.getString
2433                         ("STAR"));
2434                 out.println(rb.getString
2435                         ("warning.not.verified.make.sure.keystore.is.correct"));
2436                 out.println(rb.getString
2437                         ("STARNN"));
2438             }
2439             checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());
2440         }
2441     }
2442 
2443     private void printCRL(CRL crl, PrintStream out)
2444             throws Exception {
2445         X509CRL xcrl = (X509CRL)crl;
2446         if (rfc) {
2447             out.println("-----BEGIN X509 CRL-----");
2448             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
2449             out.println("-----END X509 CRL-----");
2450         } else {
2451             String s;
2452             if (crl instanceof X509CRLImpl) {
2453                 X509CRLImpl x509crl = (X509CRLImpl) crl;
2454                 s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));
2455             } else {
2456                 s = crl.toString();
2457             }
2458             out.println(s);
2459         }
2460     }
2461 
2462     private void doPrintCertReq(InputStream in, PrintStream out)
2463             throws Exception {
2464 
2465         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
2466         StringBuffer sb = new StringBuffer();
2467         boolean started = false;
2468         while (true) {
2469             String s = reader.readLine();
2470             if (s == null) break;
2471             if (!started) {
2472                 if (s.startsWith("-----")) {
2473                     started = true;
2474                 }
2475             } else {
2476                 if (s.startsWith("-----")) {
2477                     break;
2478                 }
2479                 sb.append(s);
2480             }
2481         }
2482         PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
2483 
2484         PublicKey pkey = req.getSubjectPublicKeyInfo();
2485         out.printf(rb.getString("PKCS.10.with.weak"),
2486                 req.getSubjectName(),
2487                 pkey.getFormat(),
2488                 withWeak(pkey),
2489                 withWeak(req.getSigAlg()));
2490         for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
2491             ObjectIdentifier oid = attr.getAttributeId();
2492             if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
2493                 CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();
2494                 if (exts != null) {
2495                     printExtensions(rb.getString("Extension.Request."), exts, out);
2496                 }
2497             } else {
2498                 out.println("Attribute: " + attr.getAttributeId());
2499                 PKCS9Attribute pkcs9Attr =
2500                         new PKCS9Attribute(attr.getAttributeId(),
2501                                            attr.getAttributeValue());
2502                 out.print(pkcs9Attr.getName() + ": ");
2503                 Object attrVal = attr.getAttributeValue();
2504                 out.println(attrVal instanceof String[] ?
2505                             Arrays.toString((String[]) attrVal) :
2506                             attrVal);
2507             }
2508         }
2509         if (debug) {
2510             out.println(req);   // Just to see more, say, public key length...
2511         }
2512         checkWeak(rb.getString("the.certificate.request"), req);
2513     }
2514 
2515     /**
2516      * Reads a certificate (or certificate chain) and prints its contents in
2517      * a human readable format.
2518      */
2519     private void printCertFromStream(InputStream in, PrintStream out)
2520         throws Exception
2521     {
2522         Collection<? extends Certificate> c = null;
2523         try {
2524             c = cf.generateCertificates(in);
2525         } catch (CertificateException ce) {
2526             throw new Exception(rb.getString("Failed.to.parse.input"), ce);
2527         }
2528         if (c.isEmpty()) {
2529             throw new Exception(rb.getString("Empty.input"));
2530         }
2531         Certificate[] certs = c.toArray(new Certificate[c.size()]);
2532         for (int i=0; i<certs.length; i++) {
2533             X509Certificate x509Cert = null;
2534             try {
2535                 x509Cert = (X509Certificate)certs[i];
2536             } catch (ClassCastException cce) {
2537                 throw new Exception(rb.getString("Not.X.509.certificate"));
2538             }
2539             if (certs.length > 1) {
2540                 MessageFormat form = new MessageFormat
2541                         (rb.getString("Certificate.i.1."));
2542                 Object[] source = {new Integer(i + 1)};
2543                 out.println(form.format(source));
2544             }
2545             if (rfc)
2546                 dumpCert(x509Cert, out);
2547             else
2548                 printX509Cert(x509Cert, out);
2549             if (i < (certs.length-1)) {
2550                 out.println();
2551             }
2552             checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);
2553         }
2554     }
2555 
2556     private static String oneInMany(String label, int i, int num) {
2557         if (num == 1) {
2558             return label;
2559         } else {
2560             return String.format(rb.getString("one.in.many"), label, i+1, num);
2561         }
2562     }
2563 
2564     private void doPrintCert(final PrintStream out) throws Exception {
2565         if (jarfile != null) {
2566             JarFile jf = new JarFile(jarfile, true);
2567             Enumeration<JarEntry> entries = jf.entries();
2568             Set<CodeSigner> ss = new HashSet<>();
2569             byte[] buffer = new byte[8192];
2570             int pos = 0;
2571             while (entries.hasMoreElements()) {
2572                 JarEntry je = entries.nextElement();
2573                 try (InputStream is = jf.getInputStream(je)) {
2574                     while (is.read(buffer) != -1) {
2575                         // we just read. this will throw a SecurityException
2576                         // if a signature/digest check fails. This also
2577                         // populate the signers
2578                     }
2579                 }
2580                 CodeSigner[] signers = je.getCodeSigners();
2581                 if (signers != null) {
2582                     for (CodeSigner signer: signers) {
2583                         if (!ss.contains(signer)) {
2584                             ss.add(signer);
2585                             out.printf(rb.getString("Signer.d."), ++pos);
2586                             out.println();
2587                             out.println();
2588                             out.println(rb.getString("Signature."));
2589                             out.println();
2590 
2591                             List<? extends Certificate> certs
2592                                     = signer.getSignerCertPath().getCertificates();
2593                             int cc = 0;
2594                             for (Certificate cert: certs) {
2595                                 X509Certificate x = (X509Certificate)cert;
2596                                 if (rfc) {
2597                                     out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2598                                     dumpCert(x, out);
2599                                 } else {
2600                                     printX509Cert(x, out);
2601                                 }
2602                                 out.println();
2603                                 checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);
2604                             }
2605                             Timestamp ts = signer.getTimestamp();
2606                             if (ts != null) {
2607                                 out.println(rb.getString("Timestamp."));
2608                                 out.println();
2609                                 certs = ts.getSignerCertPath().getCertificates();
2610                                 cc = 0;
2611                                 for (Certificate cert: certs) {
2612                                     X509Certificate x = (X509Certificate)cert;
2613                                     if (rfc) {
2614                                         out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2615                                         dumpCert(x, out);
2616                                     } else {
2617                                         printX509Cert(x, out);
2618                                     }
2619                                     out.println();
2620                                     checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);
2621                                 }
2622                             }
2623                         }
2624                     }
2625                 }
2626             }
2627             jf.close();
2628             if (ss.isEmpty()) {
2629                 out.println(rb.getString("Not.a.signed.jar.file"));
2630             }
2631         } else if (sslserver != null) {
2632             // Lazily load SSLCertStoreHelper if present
2633             CertStoreHelper helper = CertStoreHelper.getInstance("SSLServer");
2634             CertStore cs = helper.getCertStore(new URI("https://" + sslserver));
2635             Collection<? extends Certificate> chain;
2636             try {
2637                 chain = cs.getCertificates(null);
2638                 if (chain.isEmpty()) {
2639                     // If the certs are not retrieved, we consider it an error
2640                     // even if the URL connection is successful.
2641                     throw new Exception(rb.getString(
2642                                         "No.certificate.from.the.SSL.server"));
2643                 }
2644             } catch (CertStoreException cse) {
2645                 if (cse.getCause() instanceof IOException) {
2646                     throw new Exception(rb.getString(
2647                                         "No.certificate.from.the.SSL.server"),
2648                                         cse.getCause());
2649                 } else {
2650                     throw cse;
2651                 }
2652             }
2653 
2654             int i = 0;
2655             for (Certificate cert : chain) {
2656                 try {
2657                     if (rfc) {
2658                         dumpCert(cert, out);
2659                     } else {
2660                         out.println("Certificate #" + i);
2661                         out.println("====================================");
2662                         printX509Cert((X509Certificate)cert, out);
2663                         out.println();
2664                     }
2665                     checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert);
2666                 } catch (Exception e) {
2667                     if (debug) {
2668                         e.printStackTrace();
2669                     }
2670                 }
2671             }
2672         } else {
2673             if (filename != null) {
2674                 try (FileInputStream inStream = new FileInputStream(filename)) {
2675                     printCertFromStream(inStream, out);
2676                 }
2677             } else {
2678                 printCertFromStream(System.in, out);
2679             }
2680         }
2681     }
2682     /**
2683      * Creates a self-signed certificate, and stores it as a single-element
2684      * certificate chain.
2685      */
2686     private void doSelfCert(String alias, String dname, String sigAlgName)
2687         throws Exception
2688     {
2689         if (alias == null) {
2690             alias = keyAlias;
2691         }
2692 
2693         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2694         PrivateKey privKey = (PrivateKey)objs.fst;
2695         if (keyPass == null)
2696             keyPass = objs.snd;
2697 
2698         // Determine the signature algorithm
2699         if (sigAlgName == null) {
2700             sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
2701         }
2702 
2703         // Get the old certificate
2704         Certificate oldCert = keyStore.getCertificate(alias);
2705         if (oldCert == null) {
2706             MessageFormat form = new MessageFormat
2707                 (rb.getString("alias.has.no.public.key"));
2708             Object[] source = {alias};
2709             throw new Exception(form.format(source));
2710         }
2711         if (!(oldCert instanceof X509Certificate)) {
2712             MessageFormat form = new MessageFormat
2713                 (rb.getString("alias.has.no.X.509.certificate"));
2714             Object[] source = {alias};
2715             throw new Exception(form.format(source));
2716         }
2717 
2718         // convert to X509CertImpl, so that we can modify selected fields
2719         // (no public APIs available yet)
2720         byte[] encoded = oldCert.getEncoded();
2721         X509CertImpl certImpl = new X509CertImpl(encoded);
2722         X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME
2723                                                            + "." +
2724                                                            X509CertImpl.INFO);
2725 
2726         // Extend its validity
2727         Date firstDate = getStartDate(startDate);
2728         Date lastDate = getLastDate(firstDate, validity);
2729         CertificateValidity interval = new CertificateValidity(firstDate,
2730                                                                lastDate);
2731         certInfo.set(X509CertInfo.VALIDITY, interval);
2732 
2733         // Make new serial number
2734         certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
2735                     new java.util.Random().nextInt() & 0x7fffffff));
2736 
2737         // Set owner and issuer fields
2738         X500Name owner;
2739         if (dname == null) {
2740             // Get the owner name from the certificate
2741             owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +
2742                                            X509CertInfo.DN_NAME);
2743         } else {
2744             // Use the owner name specified at the command line
2745             owner = new X500Name(dname);
2746             certInfo.set(X509CertInfo.SUBJECT + "." +
2747                          X509CertInfo.DN_NAME, owner);
2748         }
2749         // Make issuer same as owner (self-signed!)
2750         certInfo.set(X509CertInfo.ISSUER + "." +
2751                      X509CertInfo.DN_NAME, owner);
2752 
2753         // The inner and outer signature algorithms have to match.
2754         // The way we achieve that is really ugly, but there seems to be no
2755         // other solution: We first sign the cert, then retrieve the
2756         // outer sigalg and use it to set the inner sigalg
2757         X509CertImpl newCert = new X509CertImpl(certInfo);
2758         AlgorithmParameterSpec params = AlgorithmId
2759                 .getDefaultAlgorithmParameterSpec(sigAlgName, privKey);
2760         newCert.sign(privKey, params, sigAlgName, null);
2761         AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
2762         certInfo.set(CertificateAlgorithmId.NAME + "." +
2763                      CertificateAlgorithmId.ALGORITHM, sigAlgid);
2764 
2765         certInfo.set(X509CertInfo.VERSION,
2766                         new CertificateVersion(CertificateVersion.V3));
2767 
2768         CertificateExtensions ext = createV3Extensions(
2769                 null,
2770                 (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),
2771                 v3ext,
2772                 oldCert.getPublicKey(),
2773                 null);
2774         certInfo.set(X509CertInfo.EXTENSIONS, ext);
2775         // Sign the new certificate
2776         newCert = new X509CertImpl(certInfo);
2777         newCert.sign(privKey, params, sigAlgName, null);
2778 
2779         // Store the new certificate as a single-element certificate chain
2780         keyStore.setKeyEntry(alias, privKey,
2781                              (keyPass != null) ? keyPass : storePass,
2782                              new Certificate[] { newCert } );
2783 
2784         if (verbose) {
2785             System.err.println(rb.getString("New.certificate.self.signed."));
2786             System.err.print(newCert.toString());
2787             System.err.println();
2788         }
2789     }
2790 
2791     /**
2792      * Processes a certificate reply from a certificate authority.
2793      *
2794      * <p>Builds a certificate chain on top of the certificate reply,
2795      * using trusted certificates from the keystore. The chain is complete
2796      * after a self-signed certificate has been encountered. The self-signed
2797      * certificate is considered a root certificate authority, and is stored
2798      * at the end of the chain.
2799      *
2800      * <p>The newly generated chain replaces the old chain associated with the
2801      * key entry.
2802      *
2803      * @return true if the certificate reply was installed, otherwise false.
2804      */
2805     private boolean installReply(String alias, InputStream in)
2806         throws Exception
2807     {
2808         if (alias == null) {
2809             alias = keyAlias;
2810         }
2811 
2812         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2813         PrivateKey privKey = (PrivateKey)objs.fst;
2814         if (keyPass == null) {
2815             keyPass = objs.snd;
2816         }
2817 
2818         Certificate userCert = keyStore.getCertificate(alias);
2819         if (userCert == null) {
2820             MessageFormat form = new MessageFormat
2821                 (rb.getString("alias.has.no.public.key.certificate."));
2822             Object[] source = {alias};
2823             throw new Exception(form.format(source));
2824         }
2825 
2826         // Read the certificates in the reply
2827         Collection<? extends Certificate> c = cf.generateCertificates(in);
2828         if (c.isEmpty()) {
2829             throw new Exception(rb.getString("Reply.has.no.certificates"));
2830         }
2831         Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);
2832         Certificate[] newChain;
2833         if (replyCerts.length == 1) {
2834             // single-cert reply
2835             newChain = establishCertChain(userCert, replyCerts[0]);
2836         } else {
2837             // cert-chain reply (e.g., PKCS#7)
2838             newChain = validateReply(alias, userCert, replyCerts);
2839         }
2840 
2841         // Now store the newly established chain in the keystore. The new
2842         // chain replaces the old one. The chain can be null if user chooses no.
2843         if (newChain != null) {
2844             keyStore.setKeyEntry(alias, privKey,
2845                                  (keyPass != null) ? keyPass : storePass,
2846                                  newChain);
2847             return true;
2848         } else {
2849             return false;
2850         }
2851     }
2852 
2853     /**
2854      * Imports a certificate and adds it to the list of trusted certificates.
2855      *
2856      * @return true if the certificate was added, otherwise false.
2857      */
2858     private boolean addTrustedCert(String alias, InputStream in)
2859         throws Exception
2860     {
2861         if (alias == null) {
2862             throw new Exception(rb.getString("Must.specify.alias"));
2863         }
2864         if (keyStore.containsAlias(alias)) {
2865             MessageFormat form = new MessageFormat(rb.getString
2866                 ("Certificate.not.imported.alias.alias.already.exists"));
2867             Object[] source = {alias};
2868             throw new Exception(form.format(source));
2869         }
2870 
2871         // Read the certificate
2872         X509Certificate cert = null;
2873         try {
2874             cert = (X509Certificate)cf.generateCertificate(in);
2875         } catch (ClassCastException | CertificateException ce) {
2876             throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
2877         }
2878 
2879         if (noprompt) {
2880             checkWeak(rb.getString("the.input"), cert);
2881             keyStore.setCertificateEntry(alias, cert);
2882             return true;
2883         }
2884 
2885         // if certificate is self-signed, make sure it verifies
2886         boolean selfSigned = false;
2887         if (KeyStoreUtil.isSelfSigned(cert)) {
2888             cert.verify(cert.getPublicKey());
2889             selfSigned = true;
2890         }
2891 
2892         // check if cert already exists in keystore
2893         String reply = null;
2894         String trustalias = keyStore.getCertificateAlias(cert);
2895         if (trustalias != null) {
2896             MessageFormat form = new MessageFormat(rb.getString
2897                 ("Certificate.already.exists.in.keystore.under.alias.trustalias."));
2898             Object[] source = {trustalias};
2899             System.err.println(form.format(source));
2900             checkWeak(rb.getString("the.input"), cert);
2901             printWeakWarnings(true);
2902             reply = getYesNoReply
2903                 (rb.getString("Do.you.still.want.to.add.it.no."));
2904         } else if (selfSigned) {
2905             if (trustcacerts && (caks != null) &&
2906                     ((trustalias=caks.getCertificateAlias(cert)) != null)) {
2907                 MessageFormat form = new MessageFormat(rb.getString
2908                         ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
2909                 Object[] source = {trustalias};
2910                 System.err.println(form.format(source));
2911                 checkWeak(rb.getString("the.input"), cert);
2912                 printWeakWarnings(true);
2913                 reply = getYesNoReply
2914                         (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
2915             }
2916             if (trustalias == null) {
2917                 // Print the cert and ask user if they really want to add
2918                 // it to their keystore
2919                 printX509Cert(cert, System.out);
2920                 checkWeak(rb.getString("the.input"), cert);
2921                 printWeakWarnings(true);
2922                 reply = getYesNoReply
2923                         (rb.getString("Trust.this.certificate.no."));
2924             }
2925         }
2926         if (reply != null) {
2927             if ("YES".equals(reply)) {
2928                 keyStore.setCertificateEntry(alias, cert);
2929                 return true;
2930             } else {
2931                 return false;
2932             }
2933         }
2934 
2935         // Not found in this keystore and not self-signed
2936         // Try to establish trust chain
2937         try {
2938             Certificate[] chain = establishCertChain(null, cert);
2939             if (chain != null) {
2940                 keyStore.setCertificateEntry(alias, cert);
2941                 return true;
2942             }
2943         } catch (Exception e) {
2944             // Print the cert and ask user if they really want to add it to
2945             // their keystore
2946             printX509Cert(cert, System.out);
2947             checkWeak(rb.getString("the.input"), cert);
2948             printWeakWarnings(true);
2949             reply = getYesNoReply
2950                 (rb.getString("Trust.this.certificate.no."));
2951             if ("YES".equals(reply)) {
2952                 keyStore.setCertificateEntry(alias, cert);
2953                 return true;
2954             } else {
2955                 return false;
2956             }
2957         }
2958 
2959         return false;
2960     }
2961 
2962     /**
2963      * Prompts user for new password. New password must be different from
2964      * old one.
2965      *
2966      * @param prompt the message that gets prompted on the screen
2967      * @param oldPasswd the current (i.e., old) password
2968      */
2969     private char[] getNewPasswd(String prompt, char[] oldPasswd)
2970         throws Exception
2971     {
2972         char[] entered = null;
2973         char[] reentered = null;
2974 
2975         for (int count = 0; count < 3; count++) {
2976             MessageFormat form = new MessageFormat
2977                 (rb.getString("New.prompt."));
2978             Object[] source = {prompt};
2979             System.err.print(form.format(source));
2980             entered = Password.readPassword(System.in);
2981             passwords.add(entered);
2982             if (entered == null || entered.length < 6) {
2983                 System.err.println(rb.getString
2984                     ("Password.is.too.short.must.be.at.least.6.characters"));
2985             } else if (Arrays.equals(entered, oldPasswd)) {
2986                 System.err.println(rb.getString("Passwords.must.differ"));
2987             } else {
2988                 form = new MessageFormat
2989                         (rb.getString("Re.enter.new.prompt."));
2990                 Object[] src = {prompt};
2991                 System.err.print(form.format(src));
2992                 reentered = Password.readPassword(System.in);
2993                 passwords.add(reentered);
2994                 if (!Arrays.equals(entered, reentered)) {
2995                     System.err.println
2996                         (rb.getString("They.don.t.match.Try.again"));
2997                 } else {
2998                     Arrays.fill(reentered, ' ');
2999                     return entered;
3000                 }
3001             }
3002             if (entered != null) {
3003                 Arrays.fill(entered, ' ');
3004                 entered = null;
3005             }
3006             if (reentered != null) {
3007                 Arrays.fill(reentered, ' ');
3008                 reentered = null;
3009             }
3010         }
3011         throw new Exception(rb.getString("Too.many.failures.try.later"));
3012     }
3013 
3014     /**
3015      * Prompts user for alias name.
3016      * @param prompt the {0} of "Enter {0} alias name:  " in prompt line
3017      * @returns the string entered by the user, without the \n at the end
3018      */
3019     private String getAlias(String prompt) throws Exception {
3020         if (prompt != null) {
3021             MessageFormat form = new MessageFormat
3022                 (rb.getString("Enter.prompt.alias.name."));
3023             Object[] source = {prompt};
3024             System.err.print(form.format(source));
3025         } else {
3026             System.err.print(rb.getString("Enter.alias.name."));
3027         }
3028         return (new BufferedReader(new InputStreamReader(
3029                                         System.in))).readLine();
3030     }
3031 
3032     /**
3033      * Prompts user for an input string from the command line (System.in)
3034      * @prompt the prompt string printed
3035      * @returns the string entered by the user, without the \n at the end
3036      */
3037     private String inputStringFromStdin(String prompt) throws Exception {
3038         System.err.print(prompt);
3039         return (new BufferedReader(new InputStreamReader(
3040                                         System.in))).readLine();
3041     }
3042 
3043     /**
3044      * Prompts user for key password. User may select to choose the same
3045      * password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.
3046      */
3047     private char[] getKeyPasswd(String alias, String otherAlias,
3048                                 char[] otherKeyPass)
3049         throws Exception
3050     {
3051         int count = 0;
3052         char[] keyPass = null;
3053 
3054         do {
3055             if (otherKeyPass != null) {
3056                 MessageFormat form = new MessageFormat(rb.getString
3057                         ("Enter.key.password.for.alias."));
3058                 Object[] source = {alias};
3059                 System.err.println(form.format(source));
3060 
3061                 form = new MessageFormat(rb.getString
3062                         (".RETURN.if.same.as.for.otherAlias."));
3063                 Object[] src = {otherAlias};
3064                 System.err.print(form.format(src));
3065             } else {
3066                 MessageFormat form = new MessageFormat(rb.getString
3067                         ("Enter.key.password.for.alias."));
3068                 Object[] source = {alias};
3069                 System.err.print(form.format(source));
3070             }
3071             System.err.flush();
3072             keyPass = Password.readPassword(System.in);
3073             passwords.add(keyPass);
3074             if (keyPass == null) {
3075                 keyPass = otherKeyPass;
3076             }
3077             count++;
3078         } while ((keyPass == null) && count < 3);
3079 
3080         if (keyPass == null) {
3081             throw new Exception(rb.getString("Too.many.failures.try.later"));
3082         }
3083 
3084         return keyPass;
3085     }
3086 
3087     private String withWeak(String alg) {
3088         if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
3089             if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
3090                 return alg;
3091             } else {
3092                 return String.format(rb.getString("with.weak"), alg);
3093             }
3094         } else {
3095             return String.format(rb.getString("with.disabled"), alg);
3096         }
3097     }
3098 
3099     private String fullDisplayAlgName(Key key) {
3100         String result = key.getAlgorithm();
3101         if (key instanceof ECKey) {
3102             ECParameterSpec paramSpec = ((ECKey) key).getParams();
3103             if (paramSpec instanceof NamedCurve) {
3104                 result += " (" + paramSpec.toString().split(" ")[0] + ")";
3105             }
3106         }
3107         return result;
3108     }
3109 
3110     private String withWeak(PublicKey key) {
3111         int kLen = KeyUtil.getKeySize(key);
3112         String displayAlg = fullDisplayAlgName(key);
3113         if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
3114             if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
3115                 if (kLen >= 0) {
3116                     return String.format(rb.getString("key.bit"), kLen, displayAlg);
3117                 } else {
3118                     return String.format(rb.getString("unknown.size.1"), displayAlg);
3119                 }
3120             } else {
3121                 return String.format(rb.getString("key.bit.weak"), kLen, displayAlg);
3122             }
3123         } else {
3124             return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg);
3125         }
3126     }
3127 
3128     /**
3129      * Prints a certificate in a human readable format.
3130      */
3131     private void printX509Cert(X509Certificate cert, PrintStream out)
3132         throws Exception
3133     {
3134 
3135         MessageFormat form = new MessageFormat
3136                 (rb.getString(".PATTERN.printX509Cert.with.weak"));
3137         PublicKey pkey = cert.getPublicKey();
3138         String sigName = cert.getSigAlgName();
3139         // No need to warn about sigalg of a trust anchor
3140         if (!isTrustedCert(cert)) {
3141             sigName = withWeak(sigName);
3142         }
3143         Object[] source = {cert.getSubjectDN().toString(),
3144                         cert.getIssuerDN().toString(),
3145                         cert.getSerialNumber().toString(16),
3146                         cert.getNotBefore().toString(),
3147                         cert.getNotAfter().toString(),
3148                         getCertFingerPrint("SHA-1", cert),
3149                         getCertFingerPrint("SHA-256", cert),
3150                         sigName,
3151                         withWeak(pkey),
3152                         cert.getVersion()
3153                         };
3154         out.println(form.format(source));
3155 
3156         if (cert instanceof X509CertImpl) {
3157             X509CertImpl impl = (X509CertImpl)cert;
3158             X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME
3159                                                            + "." +
3160                                                            X509CertImpl.INFO);
3161             CertificateExtensions exts = (CertificateExtensions)
3162                     certInfo.get(X509CertInfo.EXTENSIONS);
3163             if (exts != null) {
3164                 printExtensions(rb.getString("Extensions."), exts, out);
3165             }
3166         }
3167     }
3168 
3169     private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)
3170             throws Exception {
3171         int extnum = 0;
3172         Iterator<Extension> i1 = exts.getAllExtensions().iterator();
3173         Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();
3174         while (i1.hasNext() || i2.hasNext()) {
3175             Extension ext = i1.hasNext()?i1.next():i2.next();
3176             if (extnum == 0) {
3177                 out.println();
3178                 out.println(title);
3179                 out.println();
3180             }
3181             out.print("#"+(++extnum)+": "+ ext);
3182             if (ext.getClass() == Extension.class) {
3183                 byte[] v = ext.getExtensionValue();
3184                 if (v.length == 0) {
3185                     out.println(rb.getString(".Empty.value."));
3186                 } else {
3187                     new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);
3188                     out.println();
3189                 }
3190             }
3191             out.println();
3192         }
3193     }
3194 
3195     /**
3196      * Locates a signer for a given certificate from a given keystore and
3197      * returns the signer's certificate.
3198      * @param cert the certificate whose signer is searched, not null
3199      * @param ks the keystore to search with, not null
3200      * @return <code>cert</code> itself if it's already inside <code>ks</code>,
3201      * or a certificate inside <code>ks</code> who signs <code>cert</code>,
3202      * or null otherwise. A label is added.
3203      */
3204     private static Pair<String,Certificate>
3205             getSigner(Certificate cert, KeyStore ks) throws Exception {
3206         if (ks.getCertificateAlias(cert) != null) {
3207             return new Pair<>("", cert);
3208         }
3209         for (Enumeration<String> aliases = ks.aliases();
3210                 aliases.hasMoreElements(); ) {
3211             String name = aliases.nextElement();
3212             Certificate trustedCert = ks.getCertificate(name);
3213             if (trustedCert != null) {
3214                 try {
3215                     cert.verify(trustedCert.getPublicKey());
3216                     return new Pair<>(name, trustedCert);
3217                 } catch (Exception e) {
3218                     // Not verified, skip to the next one
3219                 }
3220             }
3221         }
3222         return null;
3223     }
3224 
3225     /**
3226      * Gets an X.500 name suitable for inclusion in a certification request.
3227      */
3228     private X500Name getX500Name() throws IOException {
3229         BufferedReader in;
3230         in = new BufferedReader(new InputStreamReader(System.in));
3231         String commonName = "Unknown";
3232         String organizationalUnit = "Unknown";
3233         String organization = "Unknown";
3234         String city = "Unknown";
3235         String state = "Unknown";
3236         String country = "Unknown";
3237         X500Name name;
3238         String userInput = null;
3239 
3240         int maxRetry = 20;
3241         do {
3242             if (maxRetry-- < 0) {
3243                 throw new RuntimeException(rb.getString(
3244                         "Too.many.retries.program.terminated"));
3245             }
3246             commonName = inputString(in,
3247                     rb.getString("What.is.your.first.and.last.name."),
3248                     commonName);
3249             organizationalUnit = inputString(in,
3250                     rb.getString
3251                         ("What.is.the.name.of.your.organizational.unit."),
3252                     organizationalUnit);
3253             organization = inputString(in,
3254                     rb.getString("What.is.the.name.of.your.organization."),
3255                     organization);
3256             city = inputString(in,
3257                     rb.getString("What.is.the.name.of.your.City.or.Locality."),
3258                     city);
3259             state = inputString(in,
3260                     rb.getString("What.is.the.name.of.your.State.or.Province."),
3261                     state);
3262             country = inputString(in,
3263                     rb.getString
3264                         ("What.is.the.two.letter.country.code.for.this.unit."),
3265                     country);
3266             name = new X500Name(commonName, organizationalUnit, organization,
3267                                 city, state, country);
3268             MessageFormat form = new MessageFormat
3269                 (rb.getString("Is.name.correct."));
3270             Object[] source = {name};
3271             userInput = inputString
3272                 (in, form.format(source), rb.getString("no"));
3273         } while (collator.compare(userInput, rb.getString("yes")) != 0 &&
3274                  collator.compare(userInput, rb.getString("y")) != 0);
3275 
3276         System.err.println();
3277         return name;
3278     }
3279 
3280     private String inputString(BufferedReader in, String prompt,
3281                                String defaultValue)
3282         throws IOException
3283     {
3284         System.err.println(prompt);
3285         MessageFormat form = new MessageFormat
3286                 (rb.getString(".defaultValue."));
3287         Object[] source = {defaultValue};
3288         System.err.print(form.format(source));
3289         System.err.flush();
3290 
3291         String value = in.readLine();
3292         if (value == null || collator.compare(value, "") == 0) {
3293             value = defaultValue;
3294         }
3295         return value;
3296     }
3297 
3298     /**
3299      * Writes an X.509 certificate in base64 or binary encoding to an output
3300      * stream.
3301      */
3302     private void dumpCert(Certificate cert, PrintStream out)
3303         throws IOException, CertificateException
3304     {
3305         if (rfc) {
3306             out.println(X509Factory.BEGIN_CERT);
3307             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));
3308             out.println(X509Factory.END_CERT);
3309         } else {
3310             out.write(cert.getEncoded()); // binary
3311         }
3312     }
3313 
3314     /**
3315      * Converts a byte to hex digit and writes to the supplied buffer
3316      */
3317     private void byte2hex(byte b, StringBuffer buf) {
3318         char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
3319                             '9', 'A', 'B', 'C', 'D', 'E', 'F' };
3320         int high = ((b & 0xf0) >> 4);
3321         int low = (b & 0x0f);
3322         buf.append(hexChars[high]);
3323         buf.append(hexChars[low]);
3324     }
3325 
3326     /**
3327      * Converts a byte array to hex string
3328      */
3329     private String toHexString(byte[] block) {
3330         StringBuffer buf = new StringBuffer();
3331         int len = block.length;
3332         for (int i = 0; i < len; i++) {
3333              byte2hex(block[i], buf);
3334              if (i < len-1) {
3335                  buf.append(":");
3336              }
3337         }
3338         return buf.toString();
3339     }
3340 
3341     /**
3342      * Recovers (private) key associated with given alias.
3343      *
3344      * @return an array of objects, where the 1st element in the array is the
3345      * recovered private key, and the 2nd element is the password used to
3346      * recover it.
3347      */
3348     private Pair<Key,char[]> recoverKey(String alias, char[] storePass,
3349                                        char[] keyPass)
3350         throws Exception
3351     {
3352         Key key = null;
3353 
3354         if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
3355             key = keyStore.getKey(alias, null);
3356             return Pair.of(key, null);
3357         }
3358 
3359         if (keyStore.containsAlias(alias) == false) {
3360             MessageFormat form = new MessageFormat
3361                 (rb.getString("Alias.alias.does.not.exist"));
3362             Object[] source = {alias};
3363             throw new Exception(form.format(source));
3364         }
3365         if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&
3366                 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
3367             MessageFormat form = new MessageFormat
3368                 (rb.getString("Alias.alias.has.no.key"));
3369             Object[] source = {alias};
3370             throw new Exception(form.format(source));
3371         }
3372 
3373         if (keyPass == null) {
3374             // Try to recover the key using the keystore password
3375             try {
3376                 key = keyStore.getKey(alias, storePass);
3377 
3378                 keyPass = storePass;
3379                 passwords.add(keyPass);
3380             } catch (UnrecoverableKeyException e) {
3381                 // Did not work out, so prompt user for key password
3382                 if (!token) {
3383                     keyPass = getKeyPasswd(alias, null, null);
3384                     key = keyStore.getKey(alias, keyPass);
3385                 } else {
3386                     throw e;
3387                 }
3388             }
3389         } else {
3390             key = keyStore.getKey(alias, keyPass);
3391         }
3392 
3393         return Pair.of(key, keyPass);
3394     }
3395 
3396     /**
3397      * Recovers entry associated with given alias.
3398      *
3399      * @return an array of objects, where the 1st element in the array is the
3400      * recovered entry, and the 2nd element is the password used to
3401      * recover it (null if no password).
3402      */
3403     private Pair<Entry,char[]> recoverEntry(KeyStore ks,
3404                             String alias,
3405                             char[] pstore,
3406                             char[] pkey) throws Exception {
3407 
3408         if (ks.containsAlias(alias) == false) {
3409             MessageFormat form = new MessageFormat
3410                 (rb.getString("Alias.alias.does.not.exist"));
3411             Object[] source = {alias};
3412             throw new Exception(form.format(source));
3413         }
3414 
3415         PasswordProtection pp = null;
3416         Entry entry;
3417 
3418         try {
3419             // First attempt to access entry without key password
3420             // (PKCS11 entry or trusted certificate entry, for example)
3421 
3422             entry = ks.getEntry(alias, pp);
3423             pkey = null;
3424         } catch (UnrecoverableEntryException une) {
3425 
3426             if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
3427                 KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
3428                 // should not happen, but a possibility
3429                 throw une;
3430             }
3431 
3432             // entry is protected
3433 
3434             if (pkey != null) {
3435 
3436                 // try provided key password
3437 
3438                 pp = new PasswordProtection(pkey);
3439                 entry = ks.getEntry(alias, pp);
3440 
3441             } else {
3442 
3443                 // try store pass
3444 
3445                 try {
3446                     pp = new PasswordProtection(pstore);
3447                     entry = ks.getEntry(alias, pp);
3448                     pkey = pstore;
3449                 } catch (UnrecoverableEntryException une2) {
3450                     if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
3451 
3452                         // P12 keystore currently does not support separate
3453                         // store and entry passwords
3454 
3455                         throw une2;
3456                     } else {
3457 
3458                         // prompt for entry password
3459 
3460                         pkey = getKeyPasswd(alias, null, null);
3461                         pp = new PasswordProtection(pkey);
3462                         entry = ks.getEntry(alias, pp);
3463                     }
3464                 }
3465             }
3466         }
3467 
3468         return Pair.of(entry, pkey);
3469     }
3470     /**
3471      * Gets the requested finger print of the certificate.
3472      */
3473     private String getCertFingerPrint(String mdAlg, Certificate cert)
3474         throws Exception
3475     {
3476         byte[] encCertInfo = cert.getEncoded();
3477         MessageDigest md = MessageDigest.getInstance(mdAlg);
3478         byte[] digest = md.digest(encCertInfo);
3479         return toHexString(digest);
3480     }
3481 
3482     /**
3483      * Prints warning about missing integrity check.
3484      */
3485     private void printNoIntegrityWarning() {
3486         System.err.println();
3487         System.err.println(rb.getString
3488             (".WARNING.WARNING.WARNING."));
3489         System.err.println(rb.getString
3490             (".The.integrity.of.the.information.stored.in.your.keystore."));
3491         System.err.println(rb.getString
3492             (".WARNING.WARNING.WARNING."));
3493         System.err.println();
3494     }
3495 
3496     /**
3497      * Validates chain in certification reply, and returns the ordered
3498      * elements of the chain (with user certificate first, and root
3499      * certificate last in the array).
3500      *
3501      * @param alias the alias name
3502      * @param userCert the user certificate of the alias
3503      * @param replyCerts the chain provided in the reply
3504      */
3505     private Certificate[] validateReply(String alias,
3506                                         Certificate userCert,
3507                                         Certificate[] replyCerts)
3508         throws Exception
3509     {
3510 
3511         checkWeak(rb.getString("reply"), replyCerts);
3512 
3513         // order the certs in the reply (bottom-up).
3514         // we know that all certs in the reply are of type X.509, because
3515         // we parsed them using an X.509 certificate factory
3516         int i;
3517         PublicKey userPubKey = userCert.getPublicKey();
3518         for (i=0; i<replyCerts.length; i++) {
3519             if (userPubKey.equals(replyCerts[i].getPublicKey())) {
3520                 break;
3521             }
3522         }
3523         if (i == replyCerts.length) {
3524             MessageFormat form = new MessageFormat(rb.getString
3525                 ("Certificate.reply.does.not.contain.public.key.for.alias."));
3526             Object[] source = {alias};
3527             throw new Exception(form.format(source));
3528         }
3529 
3530         Certificate tmpCert = replyCerts[0];
3531         replyCerts[0] = replyCerts[i];
3532         replyCerts[i] = tmpCert;
3533 
3534         X509Certificate thisCert = (X509Certificate)replyCerts[0];
3535 
3536         for (i=1; i < replyCerts.length-1; i++) {
3537             // find a cert in the reply who signs thisCert
3538             int j;
3539             for (j=i; j<replyCerts.length; j++) {
3540                 if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {
3541                     tmpCert = replyCerts[i];
3542                     replyCerts[i] = replyCerts[j];
3543                     replyCerts[j] = tmpCert;
3544                     thisCert = (X509Certificate)replyCerts[i];
3545                     break;
3546                 }
3547             }
3548             if (j == replyCerts.length) {
3549                 throw new Exception
3550                     (rb.getString("Incomplete.certificate.chain.in.reply"));
3551             }
3552         }
3553 
3554         if (noprompt) {
3555             return replyCerts;
3556         }
3557 
3558         // do we trust the cert at the top?
3559         Certificate topCert = replyCerts[replyCerts.length-1];
3560         boolean fromKeyStore = true;
3561         Pair<String,Certificate> root = getSigner(topCert, keyStore);
3562         if (root == null && trustcacerts && caks != null) {
3563             root = getSigner(topCert, caks);
3564             fromKeyStore = false;
3565         }
3566         if (root == null) {
3567             System.err.println();
3568             System.err.println
3569                     (rb.getString("Top.level.certificate.in.reply."));
3570             printX509Cert((X509Certificate)topCert, System.out);
3571             System.err.println();
3572             System.err.print(rb.getString(".is.not.trusted."));
3573             printWeakWarnings(true);
3574             String reply = getYesNoReply
3575                     (rb.getString("Install.reply.anyway.no."));
3576             if ("NO".equals(reply)) {
3577                 return null;
3578             }
3579         } else {
3580             if (root.snd != topCert) {
3581                 // append the root CA cert to the chain
3582                 Certificate[] tmpCerts =
3583                     new Certificate[replyCerts.length+1];
3584                 System.arraycopy(replyCerts, 0, tmpCerts, 0,
3585                                  replyCerts.length);
3586                 tmpCerts[tmpCerts.length-1] = root.snd;
3587                 replyCerts = tmpCerts;
3588                 checkWeak(String.format(rb.getString(fromKeyStore ?
3589                                             "alias.in.keystore" :
3590                                             "alias.in.cacerts"),
3591                                         root.fst),
3592                           root.snd);
3593             }
3594         }
3595         return replyCerts;
3596     }
3597 
3598     /**
3599      * Establishes a certificate chain (using trusted certificates in the
3600      * keystore and cacerts), starting with the reply (certToVerify)
3601      * and ending at a self-signed certificate found in the keystore.
3602      *
3603      * @param userCert optional existing certificate, mostly likely be the
3604      *                 original self-signed cert created by -genkeypair.
3605      *                 It must have the same public key as certToVerify
3606      *                 but cannot be the same cert.
3607      * @param certToVerify the starting certificate to build the chain
3608      * @returns the established chain, might be null if user decides not
3609      */
3610     private Certificate[] establishCertChain(Certificate userCert,
3611                                              Certificate certToVerify)
3612         throws Exception
3613     {
3614         if (userCert != null) {
3615             // Make sure that the public key of the certificate reply matches
3616             // the original public key in the keystore
3617             PublicKey origPubKey = userCert.getPublicKey();
3618             PublicKey replyPubKey = certToVerify.getPublicKey();
3619             if (!origPubKey.equals(replyPubKey)) {
3620                 throw new Exception(rb.getString
3621                         ("Public.keys.in.reply.and.keystore.don.t.match"));
3622             }
3623 
3624             // If the two certs are identical, we're done: no need to import
3625             // anything
3626             if (certToVerify.equals(userCert)) {
3627                 throw new Exception(rb.getString
3628                         ("Certificate.reply.and.certificate.in.keystore.are.identical"));
3629             }
3630         }
3631 
3632         // Build a hash table of all certificates in the keystore.
3633         // Use the subject distinguished name as the key into the hash table.
3634         // All certificates associated with the same subject distinguished
3635         // name are stored in the same hash table entry as a vector.
3636         Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
3637         if (keyStore.size() > 0) {
3638             certs = new Hashtable<>(11);
3639             keystorecerts2Hashtable(keyStore, certs);
3640         }
3641         if (trustcacerts) {
3642             if (caks!=null && caks.size()>0) {
3643                 if (certs == null) {
3644                     certs = new Hashtable<>(11);
3645                 }
3646                 keystorecerts2Hashtable(caks, certs);
3647             }
3648         }
3649 
3650         // start building chain
3651         Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
3652         if (buildChain(
3653                 new Pair<>(rb.getString("the.input"),
3654                            (X509Certificate) certToVerify),
3655                 chain, certs)) {
3656             for (Pair<String,X509Certificate> p : chain) {
3657                 checkWeak(p.fst, p.snd);
3658             }
3659             Certificate[] newChain =
3660                     new Certificate[chain.size()];
3661             // buildChain() returns chain with self-signed root-cert first and
3662             // user-cert last, so we need to invert the chain before we store
3663             // it
3664             int j=0;
3665             for (int i=chain.size()-1; i>=0; i--) {
3666                 newChain[j] = chain.elementAt(i).snd;
3667                 j++;
3668             }
3669             return newChain;
3670         } else {
3671             throw new Exception
3672                 (rb.getString("Failed.to.establish.chain.from.reply"));
3673         }
3674     }
3675 
3676     /**
3677      * Recursively tries to establish chain from pool of certs starting from
3678      * certToVerify until a self-signed cert is found, and fill the certs found
3679      * into chain. Each cert in the chain signs the next one.
3680      *
3681      * This method is able to recover from an error, say, if certToVerify
3682      * is signed by certA but certA has no issuer in certs and itself is not
3683      * self-signed, the method can try another certB that also signs
3684      * certToVerify and look for signer of certB, etc, etc.
3685      *
3686      * Each cert in chain comes with a label showing its origin. The label is
3687      * used in the warning message when the cert is considered a risk.
3688      *
3689      * @param certToVerify the cert that needs to be verified.
3690      * @param chain the chain that's being built.
3691      * @param certs the pool of trusted certs
3692      *
3693      * @return true if successful, false otherwise.
3694      */
3695     private boolean buildChain(Pair<String,X509Certificate> certToVerify,
3696             Vector<Pair<String,X509Certificate>> chain,
3697             Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
3698         if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
3699             // reached self-signed root cert;
3700             // no verification needed because it's trusted.
3701             chain.addElement(certToVerify);
3702             return true;
3703         }
3704 
3705         Principal issuer = certToVerify.snd.getIssuerDN();
3706 
3707         // Get the issuer's certificate(s)
3708         Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
3709         if (vec == null) {
3710             return false;
3711         }
3712 
3713         // Try out each certificate in the vector, until we find one
3714         // whose public key verifies the signature of the certificate
3715         // in question.
3716         for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
3717              issuerCerts.hasMoreElements(); ) {
3718             Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
3719             PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
3720             try {
3721                 certToVerify.snd.verify(issuerPubKey);
3722             } catch (Exception e) {
3723                 continue;
3724             }
3725             if (buildChain(issuerCert, chain, certs)) {
3726                 chain.addElement(certToVerify);
3727                 return true;
3728             }
3729         }
3730         return false;
3731     }
3732 
3733     /**
3734      * Prompts user for yes/no decision.
3735      *
3736      * @return the user's decision, can only be "YES" or "NO"
3737      */
3738     private String getYesNoReply(String prompt)
3739         throws IOException
3740     {
3741         String reply = null;
3742         int maxRetry = 20;
3743         do {
3744             if (maxRetry-- < 0) {
3745                 throw new RuntimeException(rb.getString(
3746                         "Too.many.retries.program.terminated"));
3747             }
3748             System.err.print(prompt);
3749             System.err.flush();
3750             reply = (new BufferedReader(new InputStreamReader
3751                                         (System.in))).readLine();
3752             if (collator.compare(reply, "") == 0 ||
3753                 collator.compare(reply, rb.getString("n")) == 0 ||
3754                 collator.compare(reply, rb.getString("no")) == 0) {
3755                 reply = "NO";
3756             } else if (collator.compare(reply, rb.getString("y")) == 0 ||
3757                        collator.compare(reply, rb.getString("yes")) == 0) {
3758                 reply = "YES";
3759             } else {
3760                 System.err.println(rb.getString("Wrong.answer.try.again"));
3761                 reply = null;
3762             }
3763         } while (reply == null);
3764         return reply;
3765     }
3766 
3767     /**
3768      * Stores the (leaf) certificates of a keystore in a hashtable.
3769      * All certs belonging to the same CA are stored in a vector that
3770      * in turn is stored in the hashtable, keyed by the CA's subject DN.
3771      * Each cert comes with a string label that shows its origin and alias.
3772      */
3773     private void keystorecerts2Hashtable(KeyStore ks,
3774                 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
3775         throws Exception {
3776 
3777         for (Enumeration<String> aliases = ks.aliases();
3778                                         aliases.hasMoreElements(); ) {
3779             String alias = aliases.nextElement();
3780             Certificate cert = ks.getCertificate(alias);
3781             if (cert != null) {
3782                 Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
3783                 Pair<String,X509Certificate> pair = new Pair<>(
3784                         String.format(
3785                                 rb.getString(ks == caks ?
3786                                         "alias.in.cacerts" :
3787                                         "alias.in.keystore"),
3788                                 alias),
3789                         (X509Certificate)cert);
3790                 Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
3791                 if (vec == null) {
3792                     vec = new Vector<>();
3793                     vec.addElement(pair);
3794                 } else {
3795                     if (!vec.contains(pair)) {
3796                         vec.addElement(pair);
3797                     }
3798                 }
3799                 hash.put(subjectDN, vec);
3800             }
3801         }
3802     }
3803 
3804     /**
3805      * Returns the issue time that's specified the -startdate option
3806      * @param s the value of -startdate option
3807      */
3808     private static Date getStartDate(String s) throws IOException {
3809         Calendar c = new GregorianCalendar();
3810         if (s != null) {
3811             IOException ioe = new IOException(
3812                     rb.getString("Illegal.startdate.value"));
3813             int len = s.length();
3814             if (len == 0) {
3815                 throw ioe;
3816             }
3817             if (s.charAt(0) == '-' || s.charAt(0) == '+') {
3818                 // Form 1: ([+-]nnn[ymdHMS])+
3819                 int start = 0;
3820                 while (start < len) {
3821                     int sign = 0;
3822                     switch (s.charAt(start)) {
3823                         case '+': sign = 1; break;
3824                         case '-': sign = -1; break;
3825                         default: throw ioe;
3826                     }
3827                     int i = start+1;
3828                     for (; i<len; i++) {
3829                         char ch = s.charAt(i);
3830                         if (ch < '0' || ch > '9') break;
3831                     }
3832                     if (i == start+1) throw ioe;
3833                     int number = Integer.parseInt(s.substring(start+1, i));
3834                     if (i >= len) throw ioe;
3835                     int unit = 0;
3836                     switch (s.charAt(i)) {
3837                         case 'y': unit = Calendar.YEAR; break;
3838                         case 'm': unit = Calendar.MONTH; break;
3839                         case 'd': unit = Calendar.DATE; break;
3840                         case 'H': unit = Calendar.HOUR; break;
3841                         case 'M': unit = Calendar.MINUTE; break;
3842                         case 'S': unit = Calendar.SECOND; break;
3843                         default: throw ioe;
3844                     }
3845                     c.add(unit, sign * number);
3846                     start = i + 1;
3847                 }
3848             } else  {
3849                 // Form 2: [yyyy/mm/dd] [HH:MM:SS]
3850                 String date = null, time = null;
3851                 if (len == 19) {
3852                     date = s.substring(0, 10);
3853                     time = s.substring(11);
3854                     if (s.charAt(10) != ' ')
3855                         throw ioe;
3856                 } else if (len == 10) {
3857                     date = s;
3858                 } else if (len == 8) {
3859                     time = s;
3860                 } else {
3861                     throw ioe;
3862                 }
3863                 if (date != null) {
3864                     if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {
3865                         c.set(Integer.valueOf(date.substring(0, 4)),
3866                                 Integer.valueOf(date.substring(5, 7))-1,
3867                                 Integer.valueOf(date.substring(8, 10)));
3868                     } else {
3869                         throw ioe;
3870                     }
3871                 }
3872                 if (time != null) {
3873                     if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {
3874                         c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));
3875                         c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2)));
3876                         c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2)));
3877                         c.set(Calendar.MILLISECOND, 0);
3878                     } else {
3879                         throw ioe;
3880                     }
3881                 }
3882             }
3883         }
3884         return c.getTime();
3885     }
3886 
3887     /**
3888      * Match a command (may be abbreviated) with a command set.
3889      * @param s the command provided
3890      * @param list the legal command set. If there is a null, commands after it
3891      * are regarded experimental, which means they are supported but their
3892      * existence should not be revealed to user.
3893      * @return the position of a single match, or -1 if none matched
3894      * @throws Exception if s is ambiguous
3895      */
3896     private static int oneOf(String s, String... list) throws Exception {
3897         int[] match = new int[list.length];
3898         int nmatch = 0;
3899         int experiment = Integer.MAX_VALUE;
3900         for (int i = 0; i<list.length; i++) {
3901             String one = list[i];
3902             if (one == null) {
3903                 experiment = i;
3904                 continue;
3905             }
3906             if (one.toLowerCase(Locale.ENGLISH)
3907                     .startsWith(s.toLowerCase(Locale.ENGLISH))) {
3908                 match[nmatch++] = i;
3909             } else {
3910                 StringBuffer sb = new StringBuffer();
3911                 boolean first = true;
3912                 for (char c: one.toCharArray()) {
3913                     if (first) {
3914                         sb.append(c);
3915                         first = false;
3916                     } else {
3917                         if (!Character.isLowerCase(c)) {
3918                             sb.append(c);
3919                         }
3920                     }
3921                 }
3922                 if (sb.toString().equalsIgnoreCase(s)) {
3923                     match[nmatch++] = i;
3924                 }
3925             }
3926         }
3927         if (nmatch == 0) {
3928             return -1;
3929         } else if (nmatch == 1) {
3930             return match[0];
3931         } else {
3932             // If multiple matches is in experimental commands, ignore them
3933             if (match[1] > experiment) {
3934                 return match[0];
3935             }
3936             StringBuffer sb = new StringBuffer();
3937             MessageFormat form = new MessageFormat(rb.getString
3938                 ("command.{0}.is.ambiguous."));
3939             Object[] source = {s};
3940             sb.append(form.format(source));
3941             sb.append("\n    ");
3942             for (int i=0; i<nmatch && match[i]<experiment; i++) {
3943                 sb.append(' ');
3944                 sb.append(list[match[i]]);
3945             }
3946             throw new Exception(sb.toString());
3947         }
3948     }
3949 
3950     /**
3951      * Create a GeneralName object from known types
3952      * @param t one of 5 known types
3953      * @param v value
3954      * @return which one
3955      */
3956     private GeneralName createGeneralName(String t, String v)
3957             throws Exception {
3958         GeneralNameInterface gn;
3959         int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
3960         if (p < 0) {
3961             throw new Exception(rb.getString(
3962                     "Unrecognized.GeneralName.type.") + t);
3963         }
3964         switch (p) {
3965             case 0: gn = new RFC822Name(v); break;
3966             case 1: gn = new URIName(v); break;
3967             case 2: gn = new DNSName(v); break;
3968             case 3: gn = new IPAddressName(v); break;
3969             default: gn = new OIDName(v); break; //4
3970         }
3971         return new GeneralName(gn);
3972     }
3973 
3974     private static final String[] extSupported = {
3975                         "BasicConstraints",
3976                         "KeyUsage",
3977                         "ExtendedKeyUsage",
3978                         "SubjectAlternativeName",
3979                         "IssuerAlternativeName",
3980                         "SubjectInfoAccess",
3981                         "AuthorityInfoAccess",
3982                         null,
3983                         "CRLDistributionPoints",
3984     };
3985 
3986     private ObjectIdentifier findOidForExtName(String type)
3987             throws Exception {
3988         switch (oneOf(type, extSupported)) {
3989             case 0: return PKIXExtensions.BasicConstraints_Id;
3990             case 1: return PKIXExtensions.KeyUsage_Id;
3991             case 2: return PKIXExtensions.ExtendedKeyUsage_Id;
3992             case 3: return PKIXExtensions.SubjectAlternativeName_Id;
3993             case 4: return PKIXExtensions.IssuerAlternativeName_Id;
3994             case 5: return PKIXExtensions.SubjectInfoAccess_Id;
3995             case 6: return PKIXExtensions.AuthInfoAccess_Id;
3996             case 8: return PKIXExtensions.CRLDistributionPoints_Id;
3997             default: return new ObjectIdentifier(type);
3998         }
3999     }
4000 
4001     /**
4002      * Create X509v3 extensions from a string representation. Note that the
4003      * SubjectKeyIdentifierExtension will always be created non-critical besides
4004      * the extension requested in the <code>extstr</code> argument.
4005      *
4006      * @param reqex the requested extensions, can be null, used for -gencert
4007      * @param ext the original extensions, can be null, used for -selfcert
4008      * @param extstrs -ext values, Read keytool doc
4009      * @param pkey the public key for the certificate
4010      * @param akey the public key for the authority (issuer)
4011      * @return the created CertificateExtensions
4012      */
4013     private CertificateExtensions createV3Extensions(
4014             CertificateExtensions reqex,
4015             CertificateExtensions ext,
4016             List <String> extstrs,
4017             PublicKey pkey,
4018             PublicKey akey) throws Exception {
4019 
4020         if (ext != null && reqex != null) {
4021             // This should not happen
4022             throw new Exception("One of request and original should be null.");
4023         }
4024         if (ext == null) ext = new CertificateExtensions();
4025         try {
4026             // name{:critical}{=value}
4027             // Honoring requested extensions
4028             if (reqex != null) {
4029                 for(String extstr: extstrs) {
4030                     if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {
4031                         List<String> list = Arrays.asList(
4032                                 extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));
4033                         // First check existence of "all"
4034                         if (list.contains("all")) {
4035                             ext = reqex;    // we know ext was null
4036                         }
4037                         // one by one for others
4038                         for (String item: list) {
4039                             if (item.equals("all")) continue;
4040 
4041                             // add or remove
4042                             boolean add = true;
4043                             // -1, unchanged, 0 crtical, 1 non-critical
4044                             int action = -1;
4045                             String type = null;
4046                             if (item.startsWith("-")) {
4047                                 add = false;
4048                                 type = item.substring(1);
4049                             } else {
4050                                 int colonpos = item.indexOf(':');
4051                                 if (colonpos >= 0) {
4052                                     type = item.substring(0, colonpos);
4053                                     action = oneOf(item.substring(colonpos+1),
4054                                             "critical", "non-critical");
4055                                     if (action == -1) {
4056                                         throw new Exception(rb.getString
4057                                             ("Illegal.value.") + item);
4058                                     }
4059                                 }
4060                             }
4061                             String n = reqex.getNameByOid(findOidForExtName(type));
4062                             if (add) {
4063                                 Extension e = reqex.get(n);
4064                                 if (!e.isCritical() && action == 0
4065                                         || e.isCritical() && action == 1) {
4066                                     e = Extension.newExtension(
4067                                             e.getExtensionId(),
4068                                             !e.isCritical(),
4069                                             e.getExtensionValue());
4070                                     ext.set(n, e);
4071                                 }
4072                             } else {
4073                                 ext.delete(n);
4074                             }
4075                         }
4076                         break;
4077                     }
4078                 }
4079             }
4080             for(String extstr: extstrs) {
4081                 String name, value;
4082                 boolean isCritical = false;
4083 
4084                 int eqpos = extstr.indexOf('=');
4085                 if (eqpos >= 0) {
4086                     name = extstr.substring(0, eqpos);
4087                     value = extstr.substring(eqpos+1);
4088                 } else {
4089                     name = extstr;
4090                     value = null;
4091                 }
4092 
4093                 int colonpos = name.indexOf(':');
4094                 if (colonpos >= 0) {
4095                     if (oneOf(name.substring(colonpos+1), "critical") == 0) {
4096                         isCritical = true;
4097                     }
4098                     name = name.substring(0, colonpos);
4099                 }
4100 
4101                 if (name.equalsIgnoreCase("honored")) {
4102                     continue;
4103                 }
4104                 int exttype = oneOf(name, extSupported);
4105                 switch (exttype) {
4106                     case 0:     // BC
4107                         int pathLen = -1;
4108                         boolean isCA = false;
4109                         if (value == null) {
4110                             isCA = true;
4111                         } else {
4112                             try {   // the abbr format
4113                                 pathLen = Integer.parseInt(value);
4114                                 isCA = true;
4115                             } catch (NumberFormatException ufe) {
4116                                 // ca:true,pathlen:1
4117                                 for (String part: value.split(",")) {
4118                                     String[] nv = part.split(":");
4119                                     if (nv.length != 2) {
4120                                         throw new Exception(rb.getString
4121                                                 ("Illegal.value.") + extstr);
4122                                     } else {
4123                                         if (nv[0].equalsIgnoreCase("ca")) {
4124                                             isCA = Boolean.parseBoolean(nv[1]);
4125                                         } else if (nv[0].equalsIgnoreCase("pathlen")) {
4126                                             pathLen = Integer.parseInt(nv[1]);
4127                                         } else {
4128                                             throw new Exception(rb.getString
4129                                                 ("Illegal.value.") + extstr);
4130                                         }
4131                                     }
4132                                 }
4133                             }
4134                         }
4135                         ext.set(BasicConstraintsExtension.NAME,
4136                                 new BasicConstraintsExtension(isCritical, isCA,
4137                                 pathLen));
4138                         break;
4139                     case 1:     // KU
4140                         if(value != null) {
4141                             boolean[] ok = new boolean[9];
4142                             for (String s: value.split(",")) {
4143                                 int p = oneOf(s,
4144                                        "digitalSignature",  // (0),
4145                                        "nonRepudiation",    // (1)
4146                                        "keyEncipherment",   // (2),
4147                                        "dataEncipherment",  // (3),
4148                                        "keyAgreement",      // (4),
4149                                        "keyCertSign",       // (5),
4150                                        "cRLSign",           // (6),
4151                                        "encipherOnly",      // (7),
4152                                        "decipherOnly",      // (8)
4153                                        "contentCommitment"  // also (1)
4154                                        );
4155                                 if (p < 0) {
4156                                     throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);
4157                                 }
4158                                 if (p == 9) p = 1;
4159                                 ok[p] = true;
4160                             }
4161                             KeyUsageExtension kue = new KeyUsageExtension(ok);
4162                             // The above KeyUsageExtension constructor does not
4163                             // allow isCritical value, so...
4164                             ext.set(KeyUsageExtension.NAME, Extension.newExtension(
4165                                     kue.getExtensionId(),
4166                                     isCritical,
4167                                     kue.getExtensionValue()));
4168                         } else {
4169                             throw new Exception(rb.getString
4170                                     ("Illegal.value.") + extstr);
4171                         }
4172                         break;
4173                     case 2:     // EKU
4174                         if(value != null) {
4175                             Vector<ObjectIdentifier> v = new Vector<>();
4176                             for (String s: value.split(",")) {
4177                                 int p = oneOf(s,
4178                                         "anyExtendedKeyUsage",
4179                                         "serverAuth",       //1
4180                                         "clientAuth",       //2
4181                                         "codeSigning",      //3
4182                                         "emailProtection",  //4
4183                                         "",                 //5
4184                                         "",                 //6
4185                                         "",                 //7
4186                                         "timeStamping",     //8
4187                                         "OCSPSigning"       //9
4188                                        );
4189                                 if (p < 0) {
4190                                     try {
4191                                         v.add(new ObjectIdentifier(s));
4192                                     } catch (Exception e) {
4193                                         throw new Exception(rb.getString(
4194                                                 "Unknown.extendedkeyUsage.type.") + s);
4195                                     }
4196                                 } else if (p == 0) {
4197                                     v.add(new ObjectIdentifier("2.5.29.37.0"));
4198                                 } else {
4199                                     v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));
4200                                 }
4201                             }
4202                             ext.set(ExtendedKeyUsageExtension.NAME,
4203                                     new ExtendedKeyUsageExtension(isCritical, v));
4204                         } else {
4205                             throw new Exception(rb.getString
4206                                     ("Illegal.value.") + extstr);
4207                         }
4208                         break;
4209                     case 3:     // SAN
4210                     case 4:     // IAN
4211                         if(value != null) {
4212                             String[] ps = value.split(",");
4213                             GeneralNames gnames = new GeneralNames();
4214                             for(String item: ps) {
4215                                 colonpos = item.indexOf(':');
4216                                 if (colonpos < 0) {
4217                                     throw new Exception("Illegal item " + item + " in " + extstr);
4218                                 }
4219                                 String t = item.substring(0, colonpos);
4220                                 String v = item.substring(colonpos+1);
4221                                 gnames.add(createGeneralName(t, v));
4222                             }
4223                             if (exttype == 3) {
4224                                 ext.set(SubjectAlternativeNameExtension.NAME,
4225                                         new SubjectAlternativeNameExtension(
4226                                             isCritical, gnames));
4227                             } else {
4228                                 ext.set(IssuerAlternativeNameExtension.NAME,
4229                                         new IssuerAlternativeNameExtension(
4230                                             isCritical, gnames));
4231                             }
4232                         } else {
4233                             throw new Exception(rb.getString
4234                                     ("Illegal.value.") + extstr);
4235                         }
4236                         break;
4237                     case 5:     // SIA, always non-critical
4238                     case 6:     // AIA, always non-critical
4239                         if (isCritical) {
4240                             throw new Exception(rb.getString(
4241                                     "This.extension.cannot.be.marked.as.critical.") + extstr);
4242                         }
4243                         if(value != null) {
4244                             List<AccessDescription> accessDescriptions =
4245                                     new ArrayList<>();
4246                             String[] ps = value.split(",");
4247                             for(String item: ps) {
4248                                 colonpos = item.indexOf(':');
4249                                 int colonpos2 = item.indexOf(':', colonpos+1);
4250                                 if (colonpos < 0 || colonpos2 < 0) {
4251                                     throw new Exception(rb.getString
4252                                             ("Illegal.value.") + extstr);
4253                                 }
4254                                 String m = item.substring(0, colonpos);
4255                                 String t = item.substring(colonpos+1, colonpos2);
4256                                 String v = item.substring(colonpos2+1);
4257                                 int p = oneOf(m,
4258                                         "",
4259                                         "ocsp",         //1
4260                                         "caIssuers",    //2
4261                                         "timeStamping", //3
4262                                         "",
4263                                         "caRepository"  //5
4264                                         );
4265                                 ObjectIdentifier oid;
4266                                 if (p < 0) {
4267                                     try {
4268                                         oid = new ObjectIdentifier(m);
4269                                     } catch (Exception e) {
4270                                         throw new Exception(rb.getString(
4271                                                 "Unknown.AccessDescription.type.") + m);
4272                                     }
4273                                 } else {
4274                                     oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
4275                                 }
4276                                 accessDescriptions.add(new AccessDescription(
4277                                         oid, createGeneralName(t, v)));
4278                             }
4279                             if (exttype == 5) {
4280                                 ext.set(SubjectInfoAccessExtension.NAME,
4281                                         new SubjectInfoAccessExtension(accessDescriptions));
4282                             } else {
4283                                 ext.set(AuthorityInfoAccessExtension.NAME,
4284                                         new AuthorityInfoAccessExtension(accessDescriptions));
4285                             }
4286                         } else {
4287                             throw new Exception(rb.getString
4288                                     ("Illegal.value.") + extstr);
4289                         }
4290                         break;
4291                     case 8: // CRL, experimental, only support 1 distributionpoint
4292                         if(value != null) {
4293                             String[] ps = value.split(",");
4294                             GeneralNames gnames = new GeneralNames();
4295                             for(String item: ps) {
4296                                 colonpos = item.indexOf(':');
4297                                 if (colonpos < 0) {
4298                                     throw new Exception("Illegal item " + item + " in " + extstr);
4299                                 }
4300                                 String t = item.substring(0, colonpos);
4301                                 String v = item.substring(colonpos+1);
4302                                 gnames.add(createGeneralName(t, v));
4303                             }
4304                             ext.set(CRLDistributionPointsExtension.NAME,
4305                                     new CRLDistributionPointsExtension(
4306                                         isCritical, Collections.singletonList(
4307                                         new DistributionPoint(gnames, null, null))));
4308                         } else {
4309                             throw new Exception(rb.getString
4310                                     ("Illegal.value.") + extstr);
4311                         }
4312                         break;
4313                     case -1:
4314                         ObjectIdentifier oid = new ObjectIdentifier(name);
4315                         byte[] data = null;
4316                         if (value != null) {
4317                             data = new byte[value.length() / 2 + 1];
4318                             int pos = 0;
4319                             for (char c: value.toCharArray()) {
4320                                 int hex;
4321                                 if (c >= '0' && c <= '9') {
4322                                     hex = c - '0' ;
4323                                 } else if (c >= 'A' && c <= 'F') {
4324                                     hex = c - 'A' + 10;
4325                                 } else if (c >= 'a' && c <= 'f') {
4326                                     hex = c - 'a' + 10;
4327                                 } else {
4328                                     continue;
4329                                 }
4330                                 if (pos % 2 == 0) {
4331                                     data[pos/2] = (byte)(hex << 4);
4332                                 } else {
4333                                     data[pos/2] += hex;
4334                                 }
4335                                 pos++;
4336                             }
4337                             if (pos % 2 != 0) {
4338                                 throw new Exception(rb.getString(
4339                                         "Odd.number.of.hex.digits.found.") + extstr);
4340                             }
4341                             data = Arrays.copyOf(data, pos/2);
4342                         } else {
4343                             data = new byte[0];
4344                         }
4345                         ext.set(oid.toString(), new Extension(oid, isCritical,
4346                                 new DerValue(DerValue.tag_OctetString, data)
4347                                         .toByteArray()));
4348                         break;
4349                     default:
4350                         throw new Exception(rb.getString(
4351                                 "Unknown.extension.type.") + extstr);
4352                 }
4353             }
4354             // always non-critical
4355             ext.set(SubjectKeyIdentifierExtension.NAME,
4356                     new SubjectKeyIdentifierExtension(
4357                         new KeyIdentifier(pkey).getIdentifier()));
4358             if (akey != null && !pkey.equals(akey)) {
4359                 ext.set(AuthorityKeyIdentifierExtension.NAME,
4360                         new AuthorityKeyIdentifierExtension(
4361                         new KeyIdentifier(akey), null, null));
4362             }
4363         } catch(IOException e) {
4364             throw new RuntimeException(e);
4365         }
4366         return ext;
4367     }
4368 
4369     private Date getLastDate(Date firstDate, long validity)
4370             throws Exception {
4371         Date lastDate = new Date();
4372         lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
4373 
4374         Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
4375         c.setTime(lastDate);
4376         if (c.get(Calendar.YEAR) > 9999) {
4377             throw new Exception("Validity period ends at calendar year " +
4378                     c.get(Calendar.YEAR) + " which is greater than 9999");
4379         }
4380 
4381         return lastDate;
4382     }
4383 
4384     private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
4385         if (caks != null && caks.getCertificateAlias(cert) != null) {
4386             return true;
4387         } else {
4388             String inKS = keyStore.getCertificateAlias(cert);
4389             return inKS != null && keyStore.isCertificateEntry(inKS);
4390         }
4391     }
4392 
4393     private void checkWeak(String label, String sigAlg, Key key) {
4394         if (sigAlg != null) {
4395             if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
4396                 weakWarnings.add(String.format(
4397                     rb.getString("whose.sigalg.disabled"), label, sigAlg));
4398             } else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
4399                 weakWarnings.add(String.format(
4400                     rb.getString("whose.sigalg.weak"), label, sigAlg));
4401             }
4402         }
4403 
4404         if (key != null) {
4405             if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4406                 weakWarnings.add(String.format(
4407                     rb.getString("whose.key.disabled"), label,
4408                     String.format(rb.getString("key.bit"),
4409                     KeyUtil.getKeySize(key), fullDisplayAlgName(key))));
4410             } else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4411                 weakWarnings.add(String.format(
4412                     rb.getString("whose.key.weak"), label,
4413                     String.format(rb.getString("key.bit"),
4414                     KeyUtil.getKeySize(key), fullDisplayAlgName(key))));
4415             }
4416         }
4417     }
4418 
4419     private void checkWeak(String label, Certificate[] certs)
4420             throws KeyStoreException {
4421         for (int i = 0; i < certs.length; i++) {
4422             Certificate cert = certs[i];
4423             if (cert instanceof X509Certificate) {
4424                 X509Certificate xc = (X509Certificate)cert;
4425                 String fullLabel = label;
4426                 if (certs.length > 1) {
4427                     fullLabel = oneInMany(label, i, certs.length);
4428                 }
4429                 checkWeak(fullLabel, xc);
4430             }
4431         }
4432     }
4433 
4434     private void checkWeak(String label, Certificate cert)
4435             throws KeyStoreException {
4436         if (cert instanceof X509Certificate) {
4437             X509Certificate xc = (X509Certificate)cert;
4438             // No need to check the sigalg of a trust anchor
4439             String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
4440             checkWeak(label, sigAlg, xc.getPublicKey());
4441         }
4442     }
4443 
4444     private void checkWeak(String label, PKCS10 p10) {
4445         checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
4446     }
4447 
4448     private void checkWeak(String label, CRL crl, Key key) {
4449         if (crl instanceof X509CRLImpl) {
4450             X509CRLImpl impl = (X509CRLImpl)crl;
4451             checkWeak(label, impl.getSigAlgName(), key);
4452         }
4453     }
4454 
4455     private void printWeakWarnings(boolean newLine) {
4456         if (!weakWarnings.isEmpty() && !nowarn) {
4457             System.err.println("\nWarning:");
4458             for (String warning : weakWarnings) {
4459                 System.err.println(warning);
4460             }
4461             if (newLine) {
4462                 // When calling before a yes/no prompt, add a new line
4463                 System.err.println();
4464             }
4465         }
4466         weakWarnings.clear();
4467     }
4468 
4469     /**
4470      * Prints the usage of this tool.
4471      */
4472     private void usage() {
4473         if (command != null) {
4474             System.err.println("keytool " + command +
4475                     rb.getString(".OPTION."));
4476             System.err.println();
4477             System.err.println(rb.getString(command.description));
4478             System.err.println();
4479             System.err.println(rb.getString("Options."));
4480             System.err.println();
4481 
4482             // Left and right sides of the options list
4483             String[] left = new String[command.options.length];
4484             String[] right = new String[command.options.length];
4485 
4486             // Check if there's an unknown option
4487             boolean found = false;
4488 
4489             // Length of left side of options list
4490             int lenLeft = 0;
4491             for (int j=0; j<left.length; j++) {
4492                 Option opt = command.options[j];
4493                 left[j] = opt.toString();
4494                 if (opt.arg != null) left[j] += " " + opt.arg;
4495                 if (left[j].length() > lenLeft) {
4496                     lenLeft = left[j].length();
4497                 }
4498                 right[j] = rb.getString(opt.description);
4499             }
4500             for (int j=0; j<left.length; j++) {
4501                 System.err.printf(" %-" + lenLeft + "s  %s\n",
4502                         left[j], right[j]);
4503             }
4504             System.err.println();
4505             System.err.println(rb.getString(
4506                     "Use.keytool.help.for.all.available.commands"));
4507         } else {
4508             System.err.println(rb.getString(
4509                     "Key.and.Certificate.Management.Tool"));
4510             System.err.println();
4511             System.err.println(rb.getString("Commands."));
4512             System.err.println();
4513             for (Command c: Command.values()) {
4514                 if (c == KEYCLONE) break;
4515                 System.err.printf(" %-20s%s\n", c, rb.getString(c.description));
4516             }
4517             System.err.println();
4518             System.err.println(rb.getString(
4519                     "Use.keytool.command.name.help.for.usage.of.command.name"));
4520         }
4521     }
4522 
4523     private void tinyHelp() {
4524         usage();
4525         if (debug) {
4526             throw new RuntimeException("NO BIG ERROR, SORRY");
4527         } else {
4528             System.exit(1);
4529         }
4530     }
4531 
4532     private void errorNeedArgument(String flag) {
4533         Object[] source = {flag};
4534         System.err.println(new MessageFormat(
4535                 rb.getString("Command.option.flag.needs.an.argument.")).format(source));
4536         tinyHelp();
4537     }
4538 
4539     private char[] getPass(String modifier, String arg) {
4540         char[] output =
4541             KeyStoreUtil.getPassWithModifier(modifier, arg, rb, collator);
4542         if (output != null) return output;
4543         tinyHelp();
4544         return null;    // Useless, tinyHelp() already exits.
4545     }
4546 }
4547 
4548 // This class is exactly the same as com.sun.tools.javac.util.Pair,
4549 // it's copied here since the original one is not included in JRE.
4550 class Pair<A, B> {
4551 
4552     public final A fst;
4553     public final B snd;
4554 
4555     public Pair(A fst, B snd) {
4556         this.fst = fst;
4557         this.snd = snd;
4558     }
4559 
4560     public String toString() {
4561         return "Pair[" + fst + "," + snd + "]";
4562     }
4563 
4564     public boolean equals(Object other) {
4565         return
4566             other instanceof Pair &&
4567             Objects.equals(fst, ((Pair)other).fst) &&
4568             Objects.equals(snd, ((Pair)other).snd);
4569     }
4570 
4571     public int hashCode() {
4572         if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
4573         else if (snd == null) return fst.hashCode() + 2;
4574         else return fst.hashCode() * 17 + snd.hashCode();
4575     }
4576 
4577     public static <A,B> Pair<A,B> of(A a, B b) {
4578         return new Pair<>(a,b);
4579     }
4580 }
4581 
4582