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