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