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