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