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