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