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