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                 !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                 store = KeyStore.getInstance(srcksfile, srcstorePass);
2117                 srcstoretype = store.getType();
2118             } else {
2119                 if (srcstoretype == null) {
2120                     srcstoretype = KeyStore.getDefaultType();
2121                 }
2122                 if (srcProviderName == null) {
2123                     store = KeyStore.getInstance(srcstoretype);
2124                 } else {
2125                     store = KeyStore.getInstance(srcstoretype, srcProviderName);
2126                 }
2127             }
2128 
2129             if (srcstorePass == null
2130                     && !srcprotectedPath
2131                     && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2132                 System.err.print(rb.getString("Enter.source.keystore.password."));
2133                 System.err.flush();
2134                 srcstorePass = Password.readPassword(System.in);
2135                 passwords.add(srcstorePass);
2136             }
2137 
2138             // always let keypass be storepass when using pkcs12
2139             if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {
2140                 if (srckeyPass != null && srcstorePass != null &&
2141                         !Arrays.equals(srcstorePass, srckeyPass)) {
2142                     MessageFormat form = new MessageFormat(rb.getString(
2143                         "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));
2144                     Object[] source = {"-srckeypass"};
2145                     System.err.println(form.format(source));
2146                     srckeyPass = srcstorePass;
2147                 }
2148             }
2149 
2150             store.load(is, srcstorePass);   // "is" already null in PKCS11
2151         } finally {
2152             if (is != null) {
2153                 is.close();
2154             }
2155         }
2156 
2157         if (srcstorePass == null
2158                 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2159             // anti refactoring, copied from printNoIntegrityWarning(),
2160             // but change 2 lines
2161             System.err.println();
2162             System.err.println(rb.getString
2163                 (".WARNING.WARNING.WARNING."));
2164             System.err.println(rb.getString
2165                 (".The.integrity.of.the.information.stored.in.the.srckeystore."));
2166             System.err.println(rb.getString
2167                 (".WARNING.WARNING.WARNING."));
2168             System.err.println();
2169         }
2170 
2171         return store;
2172     }
2173 
2174     /**
2175      * import all keys and certs from importkeystore.
2176      * keep alias unchanged if no name conflict, otherwise, prompt.
2177      * keep keypass unchanged for keys
2178      */
2179     private void doImportKeyStore(KeyStore srcKS) throws Exception {
2180 
2181         if (alias != null) {
2182             doImportKeyStoreSingle(srcKS, alias);
2183         } else {
2184             if (dest != null || srckeyPass != null) {
2185                 throw new Exception(rb.getString(
2186                         "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));
2187             }
2188             doImportKeyStoreAll(srcKS);
2189         }
2190 
2191         if (inplaceImport) {
2192             // Backup to file.old or file.old2...
2193             // The keystore is not rewritten yet now.
2194             for (int n = 1; /* forever */; n++) {
2195                 inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);
2196                 File bkFile = new File(inplaceBackupName);
2197                 if (!bkFile.exists()) {
2198                     Files.copy(Paths.get(srcksfname), bkFile.toPath());
2199                     break;
2200                 }
2201             }
2202 
2203         }
2204 
2205         /*
2206          * Information display rule of -importkeystore
2207          * 1. inside single, shows failure
2208          * 2. inside all, shows sucess
2209          * 3. inside all where there is a failure, prompt for continue
2210          * 4. at the final of all, shows summary
2211          */
2212     }
2213 
2214     /**
2215      * Import a single entry named alias from srckeystore
2216      * @return  1 if the import action succeed
2217      *          0 if user choose to ignore an alias-dumplicated entry
2218      *          2 if setEntry throws Exception
2219      */
2220     private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)
2221             throws Exception {
2222 
2223         String newAlias = (dest==null) ? alias : dest;
2224 
2225         if (keyStore.containsAlias(newAlias)) {
2226             Object[] source = {alias};
2227             if (noprompt) {
2228                 System.err.println(new MessageFormat(rb.getString(
2229                         "Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source));
2230             } else {
2231                 String reply = getYesNoReply(new MessageFormat(rb.getString(
2232                         "Existing.entry.alias.alias.exists.overwrite.no.")).format(source));
2233                 if ("NO".equals(reply)) {
2234                     newAlias = inputStringFromStdin(rb.getString
2235                             ("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry."));
2236                     if ("".equals(newAlias)) {
2237                         System.err.println(new MessageFormat(rb.getString(
2238                                 "Entry.for.alias.alias.not.imported.")).format(
2239                                 source));
2240                         return 0;
2241                     }
2242                 }
2243             }
2244         }
2245 
2246         Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
2247         Entry entry = objs.fst;
2248 
2249         PasswordProtection pp = null;
2250 
2251         // According to keytool.html, "The destination entry will be protected
2252         // using destkeypass. If destkeypass is not provided, the destination
2253         // entry will be protected with the source entry password."
2254         // so always try to protect with destKeyPass.
2255         char[] newPass = null;
2256         if (destKeyPass != null) {
2257             newPass = destKeyPass;
2258             pp = new PasswordProtection(destKeyPass);
2259         } else if (objs.snd != null) {
2260             newPass = objs.snd;
2261             pp = new PasswordProtection(objs.snd);
2262         }
2263 
2264         try {
2265             Certificate c = srckeystore.getCertificate(alias);
2266             if (c != null) {
2267                 checkWeak("<" + newAlias + ">", c);
2268             }
2269             keyStore.setEntry(newAlias, entry, pp);
2270             // Place the check so that only successful imports are blocked.
2271             // For example, we don't block a failed SecretEntry import.
2272             if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
2273                 if (newPass != null && !Arrays.equals(newPass, storePass)) {
2274                     throw new Exception(rb.getString(
2275                             "The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));
2276                 }
2277             }
2278             return 1;
2279         } catch (KeyStoreException kse) {
2280             Object[] source2 = {alias, kse.toString()};
2281             MessageFormat form = new MessageFormat(rb.getString(
2282                     "Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported."));
2283             System.err.println(form.format(source2));
2284             return 2;
2285         }
2286     }
2287 
2288     private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {
2289 
2290         int ok = 0;
2291         int count = srckeystore.size();
2292         for (Enumeration<String> e = srckeystore.aliases();
2293                                         e.hasMoreElements(); ) {
2294             String alias = e.nextElement();
2295             int result = doImportKeyStoreSingle(srckeystore, alias);
2296             if (result == 1) {
2297                 ok++;
2298                 Object[] source = {alias};
2299                 MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));
2300                 System.err.println(form.format(source));
2301             } else if (result == 2) {
2302                 if (!noprompt) {
2303                     String reply = getYesNoReply("Do you want to quit the import process? [no]:  ");
2304                     if ("YES".equals(reply)) {
2305                         break;
2306                     }
2307                 }
2308             }
2309         }
2310         Object[] source = {ok, count-ok};
2311         MessageFormat form = new MessageFormat(rb.getString(
2312                 "Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled"));
2313         System.err.println(form.format(source));
2314     }
2315 
2316     /**
2317      * Prints all keystore entries.
2318      */
2319     private void doPrintEntries(PrintStream out)
2320         throws Exception
2321     {
2322         out.println(rb.getString("Keystore.type.") + keyStore.getType());
2323         out.println(rb.getString("Keystore.provider.") +
2324                 keyStore.getProvider().getName());
2325         out.println();
2326 
2327         MessageFormat form;
2328         form = (keyStore.size() == 1) ?
2329                 new MessageFormat(rb.getString
2330                         ("Your.keystore.contains.keyStore.size.entry")) :
2331                 new MessageFormat(rb.getString
2332                         ("Your.keystore.contains.keyStore.size.entries"));
2333         Object[] source = {keyStore.size()};
2334         out.println(form.format(source));
2335         out.println();
2336 
2337         for (Enumeration<String> e = keyStore.aliases();
2338                                         e.hasMoreElements(); ) {
2339             String alias = e.nextElement();
2340             doPrintEntry("<" + alias + ">", alias, out);
2341             if (verbose || rfc) {
2342                 out.println(rb.getString("NEWLINE"));
2343                 out.println(rb.getString
2344                         ("STAR"));
2345                 out.println(rb.getString
2346                         ("STARNN"));
2347             }
2348         }
2349     }
2350 
2351     private static <T> Iterable<T> e2i(final Enumeration<T> e) {
2352         return new Iterable<T>() {
2353             @Override
2354             public Iterator<T> iterator() {
2355                 return new Iterator<T>() {
2356                     @Override
2357                     public boolean hasNext() {
2358                         return e.hasMoreElements();
2359                     }
2360                     @Override
2361                     public T next() {
2362                         return e.nextElement();
2363                     }
2364                     public void remove() {
2365                         throw new UnsupportedOperationException("Not supported yet.");
2366                     }
2367                 };
2368             }
2369         };
2370     }
2371 
2372     /**
2373      * Loads CRLs from a source. This method is also called in JarSigner.
2374      * @param src the source, which means System.in if null, or a URI,
2375      *        or a bare file path name
2376      */
2377     public static Collection<? extends CRL> loadCRLs(String src) throws Exception {
2378         InputStream in = null;
2379         URI uri = null;
2380         if (src == null) {
2381             in = System.in;
2382         } else {
2383             try {
2384                 uri = new URI(src);
2385                 if (uri.getScheme().equals("ldap")) {
2386                     // No input stream for LDAP
2387                 } else {
2388                     in = uri.toURL().openStream();
2389                 }
2390             } catch (Exception e) {
2391                 try {
2392                     in = new FileInputStream(src);
2393                 } catch (Exception e2) {
2394                     if (uri == null || uri.getScheme() == null) {
2395                         throw e2;   // More likely a bare file path
2396                     } else {
2397                         throw e;    // More likely a protocol or network problem
2398                     }
2399                 }
2400             }
2401         }
2402         if (in != null) {
2403             try {
2404                 // Read the full stream before feeding to X509Factory,
2405                 // otherwise, keytool -gencrl | keytool -printcrl
2406                 // might not work properly, since -gencrl is slow
2407                 // and there's no data in the pipe at the beginning.
2408                 ByteArrayOutputStream bout = new ByteArrayOutputStream();
2409                 byte[] b = new byte[4096];
2410                 while (true) {
2411                     int len = in.read(b);
2412                     if (len < 0) break;
2413                     bout.write(b, 0, len);
2414                 }
2415                 return CertificateFactory.getInstance("X509").generateCRLs(
2416                         new ByteArrayInputStream(bout.toByteArray()));
2417             } finally {
2418                 if (in != System.in) {
2419                     in.close();
2420                 }
2421             }
2422         } else {    // must be LDAP, and uri is not null
2423             URICertStoreParameters params =
2424                 new URICertStoreParameters(uri);
2425             CertStore s = CertStore.getInstance("LDAP", params);
2426             return s.getCRLs(new X509CRLSelector());
2427         }
2428     }
2429 
2430     /**
2431      * Returns CRLs described in a X509Certificate's CRLDistributionPoints
2432      * Extension. Only those containing a general name of type URI are read.
2433      */
2434     public static List<CRL> readCRLsFromCert(X509Certificate cert)
2435             throws Exception {
2436         List<CRL> crls = new ArrayList<>();
2437         CRLDistributionPointsExtension ext =
2438                 X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension();
2439         if (ext == null) return crls;
2440         List<DistributionPoint> distPoints =
2441                 ext.get(CRLDistributionPointsExtension.POINTS);
2442         for (DistributionPoint o: distPoints) {
2443             GeneralNames names = o.getFullName();
2444             if (names != null) {
2445                 for (GeneralName name: names.names()) {
2446                     if (name.getType() == GeneralNameInterface.NAME_URI) {
2447                         URIName uriName = (URIName)name.getName();
2448                         for (CRL crl: loadCRLs(uriName.getName())) {
2449                             if (crl instanceof X509CRL) {
2450                                 crls.add((X509CRL)crl);
2451                             }
2452                         }
2453                         break;  // Different name should point to same CRL
2454                     }
2455                 }
2456             }
2457         }
2458         return crls;
2459     }
2460 
2461     private static String verifyCRL(KeyStore ks, CRL crl)
2462             throws Exception {
2463         X509CRLImpl xcrl = (X509CRLImpl)crl;
2464         X500Principal issuer = xcrl.getIssuerX500Principal();
2465         for (String s: e2i(ks.aliases())) {
2466             Certificate cert = ks.getCertificate(s);
2467             if (cert instanceof X509Certificate) {
2468                 X509Certificate xcert = (X509Certificate)cert;
2469                 if (xcert.getSubjectX500Principal().equals(issuer)) {
2470                     try {
2471                         ((X509CRLImpl)crl).verify(cert.getPublicKey());
2472                         return s;
2473                     } catch (Exception e) {
2474                     }
2475                 }
2476             }
2477         }
2478         return null;
2479     }
2480 
2481     private void doPrintCRL(String src, PrintStream out)
2482             throws Exception {
2483         for (CRL crl: loadCRLs(src)) {
2484             printCRL(crl, out);
2485             String issuer = null;
2486             Certificate signer = null;
2487             if (caks != null) {
2488                 issuer = verifyCRL(caks, crl);
2489                 if (issuer != null) {
2490                     signer = caks.getCertificate(issuer);
2491                     out.printf(rb.getString(
2492                             "verified.by.s.in.s.weak"),
2493                             issuer,
2494                             "cacerts",
2495                             withWeak(signer.getPublicKey()));
2496                     out.println();
2497                 }
2498             }
2499             if (issuer == null && keyStore != null) {
2500                 issuer = verifyCRL(keyStore, crl);
2501                 if (issuer != null) {
2502                     signer = keyStore.getCertificate(issuer);
2503                     out.printf(rb.getString(
2504                             "verified.by.s.in.s.weak"),
2505                             issuer,
2506                             "keystore",
2507                             withWeak(signer.getPublicKey()));
2508                     out.println();
2509                 }
2510             }
2511             if (issuer == null) {
2512                 out.println(rb.getString
2513                         ("STAR"));
2514                 out.println(rb.getString
2515                         ("warning.not.verified.make.sure.keystore.is.correct"));
2516                 out.println(rb.getString
2517                         ("STARNN"));
2518             }
2519             checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());
2520         }
2521     }
2522 
2523     private void printCRL(CRL crl, PrintStream out)
2524             throws Exception {
2525         X509CRL xcrl = (X509CRL)crl;
2526         if (rfc) {
2527             out.println("-----BEGIN X509 CRL-----");
2528             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
2529             out.println("-----END X509 CRL-----");
2530         } else {
2531             String s;
2532             if (crl instanceof X509CRLImpl) {
2533                 X509CRLImpl x509crl = (X509CRLImpl) crl;
2534                 s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));
2535             } else {
2536                 s = crl.toString();
2537             }
2538             out.println(s);
2539         }
2540     }
2541 
2542     private void doPrintCertReq(InputStream in, PrintStream out)
2543             throws Exception {
2544 
2545         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
2546         StringBuffer sb = new StringBuffer();
2547         boolean started = false;
2548         while (true) {
2549             String s = reader.readLine();
2550             if (s == null) break;
2551             if (!started) {
2552                 if (s.startsWith("-----")) {
2553                     started = true;
2554                 }
2555             } else {
2556                 if (s.startsWith("-----")) {
2557                     break;
2558                 }
2559                 sb.append(s);
2560             }
2561         }
2562         PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
2563 
2564         PublicKey pkey = req.getSubjectPublicKeyInfo();
2565         out.printf(rb.getString("PKCS.10.with.weak"),
2566                 req.getSubjectName(),
2567                 pkey.getFormat(),
2568                 withWeak(pkey),
2569                 withWeak(req.getSigAlg()));
2570         for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
2571             ObjectIdentifier oid = attr.getAttributeId();
2572             if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
2573                 CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();
2574                 if (exts != null) {
2575                     printExtensions(rb.getString("Extension.Request."), exts, out);
2576                 }
2577             } else {
2578                 out.println("Attribute: " + attr.getAttributeId());
2579                 PKCS9Attribute pkcs9Attr =
2580                         new PKCS9Attribute(attr.getAttributeId(),
2581                                            attr.getAttributeValue());
2582                 out.print(pkcs9Attr.getName() + ": ");
2583                 Object attrVal = attr.getAttributeValue();
2584                 out.println(attrVal instanceof String[] ?
2585                             Arrays.toString((String[]) attrVal) :
2586                             attrVal);
2587             }
2588         }
2589         if (debug) {
2590             out.println(req);   // Just to see more, say, public key length...
2591         }
2592         checkWeak(rb.getString("the.certificate.request"), req);
2593     }
2594 
2595     /**
2596      * Reads a certificate (or certificate chain) and prints its contents in
2597      * a human readable format.
2598      */
2599     private void printCertFromStream(InputStream in, PrintStream out)
2600         throws Exception
2601     {
2602         Collection<? extends Certificate> c = null;
2603         try {
2604             c = cf.generateCertificates(in);
2605         } catch (CertificateException ce) {
2606             throw new Exception(rb.getString("Failed.to.parse.input"), ce);
2607         }
2608         if (c.isEmpty()) {
2609             throw new Exception(rb.getString("Empty.input"));
2610         }
2611         Certificate[] certs = c.toArray(new Certificate[c.size()]);
2612         for (int i=0; i<certs.length; i++) {
2613             X509Certificate x509Cert = null;
2614             try {
2615                 x509Cert = (X509Certificate)certs[i];
2616             } catch (ClassCastException cce) {
2617                 throw new Exception(rb.getString("Not.X.509.certificate"));
2618             }
2619             if (certs.length > 1) {
2620                 MessageFormat form = new MessageFormat
2621                         (rb.getString("Certificate.i.1."));
2622                 Object[] source = {i + 1};
2623                 out.println(form.format(source));
2624             }
2625             if (rfc)
2626                 dumpCert(x509Cert, out);
2627             else
2628                 printX509Cert(x509Cert, out);
2629             if (i < (certs.length-1)) {
2630                 out.println();
2631             }
2632             checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);
2633         }
2634     }
2635 
2636     private static String oneInMany(String label, int i, int num) {
2637         if (num == 1) {
2638             return label;
2639         } else {
2640             return String.format(rb.getString("one.in.many"), label, i+1, num);
2641         }
2642     }
2643 
2644     private void doPrintCert(final PrintStream out) throws Exception {
2645         if (jarfile != null) {
2646             // reset "jdk.certpath.disabledAlgorithms" security property
2647             // to be able to read jars which were signed with weak algorithms
2648             Security.setProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS, "");
2649 
2650             JarFile jf = new JarFile(jarfile, true);
2651             Enumeration<JarEntry> entries = jf.entries();
2652             Set<CodeSigner> ss = new HashSet<>();
2653             byte[] buffer = new byte[8192];
2654             int pos = 0;
2655             while (entries.hasMoreElements()) {
2656                 JarEntry je = entries.nextElement();
2657                 try (InputStream is = jf.getInputStream(je)) {
2658                     while (is.read(buffer) != -1) {
2659                         // we just read. this will throw a SecurityException
2660                         // if a signature/digest check fails. This also
2661                         // populate the signers
2662                     }
2663                 }
2664                 CodeSigner[] signers = je.getCodeSigners();
2665                 if (signers != null) {
2666                     for (CodeSigner signer: signers) {
2667                         if (!ss.contains(signer)) {
2668                             ss.add(signer);
2669                             out.printf(rb.getString("Signer.d."), ++pos);
2670                             out.println();
2671                             out.println();
2672                             out.println(rb.getString("Signature."));
2673                             out.println();
2674 
2675                             List<? extends Certificate> certs
2676                                     = signer.getSignerCertPath().getCertificates();
2677                             int cc = 0;
2678                             for (Certificate cert: certs) {
2679                                 X509Certificate x = (X509Certificate)cert;
2680                                 if (rfc) {
2681                                     out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2682                                     dumpCert(x, out);
2683                                 } else {
2684                                     printX509Cert(x, out);
2685                                 }
2686                                 out.println();
2687                                 checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);
2688                             }
2689                             Timestamp ts = signer.getTimestamp();
2690                             if (ts != null) {
2691                                 out.println(rb.getString("Timestamp."));
2692                                 out.println();
2693                                 certs = ts.getSignerCertPath().getCertificates();
2694                                 cc = 0;
2695                                 for (Certificate cert: certs) {
2696                                     X509Certificate x = (X509Certificate)cert;
2697                                     if (rfc) {
2698                                         out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2699                                         dumpCert(x, out);
2700                                     } else {
2701                                         printX509Cert(x, out);
2702                                     }
2703                                     out.println();
2704                                     checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);
2705                                 }
2706                             }
2707                         }
2708                     }
2709                 }
2710             }
2711             jf.close();
2712             if (ss.isEmpty()) {
2713                 out.println(rb.getString("Not.a.signed.jar.file"));
2714             }
2715         } else if (sslserver != null) {
2716             CertStore cs = SSLServerCertStore.getInstance(new URI("https://" + sslserver));
2717             Collection<? extends Certificate> chain;
2718             try {
2719                 chain = cs.getCertificates(null);
2720                 if (chain.isEmpty()) {
2721                     // If the certs are not retrieved, we consider it an error
2722                     // even if the URL connection is successful.
2723                     throw new Exception(rb.getString(
2724                                         "No.certificate.from.the.SSL.server"));
2725                 }
2726             } catch (CertStoreException cse) {
2727                 if (cse.getCause() instanceof IOException) {
2728                     throw new Exception(rb.getString(
2729                                         "No.certificate.from.the.SSL.server"),
2730                                         cse.getCause());
2731                 } else {
2732                     throw cse;
2733                 }
2734             }
2735 
2736             int i = 0;
2737             for (Certificate cert : chain) {
2738                 try {
2739                     if (rfc) {
2740                         dumpCert(cert, out);
2741                     } else {
2742                         out.println("Certificate #" + i);
2743                         out.println("====================================");
2744                         printX509Cert((X509Certificate)cert, out);
2745                         out.println();
2746                     }
2747                     checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert);
2748                 } catch (Exception e) {
2749                     if (debug) {
2750                         e.printStackTrace();
2751                     }
2752                 }
2753             }
2754         } else {
2755             if (filename != null) {
2756                 try (FileInputStream inStream = new FileInputStream(filename)) {
2757                     printCertFromStream(inStream, out);
2758                 }
2759             } else {
2760                 printCertFromStream(System.in, out);
2761             }
2762         }
2763     }
2764     /**
2765      * Creates a self-signed certificate, and stores it as a single-element
2766      * certificate chain.
2767      */
2768     private void doSelfCert(String alias, String dname, String sigAlgName)
2769         throws Exception
2770     {
2771         if (alias == null) {
2772             alias = keyAlias;
2773         }
2774 
2775         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2776         PrivateKey privKey = (PrivateKey)objs.fst;
2777         if (keyPass == null)
2778             keyPass = objs.snd;
2779 
2780         // Determine the signature algorithm
2781         if (sigAlgName == null) {
2782             sigAlgName = getCompatibleSigAlgName(privKey);
2783         }
2784 
2785         // Get the old certificate
2786         Certificate oldCert = keyStore.getCertificate(alias);
2787         if (oldCert == null) {
2788             MessageFormat form = new MessageFormat
2789                 (rb.getString("alias.has.no.public.key"));
2790             Object[] source = {alias};
2791             throw new Exception(form.format(source));
2792         }
2793         if (!(oldCert instanceof X509Certificate)) {
2794             MessageFormat form = new MessageFormat
2795                 (rb.getString("alias.has.no.X.509.certificate"));
2796             Object[] source = {alias};
2797             throw new Exception(form.format(source));
2798         }
2799 
2800         // convert to X509CertImpl, so that we can modify selected fields
2801         // (no public APIs available yet)
2802         byte[] encoded = oldCert.getEncoded();
2803         X509CertImpl certImpl = new X509CertImpl(encoded);
2804         X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME
2805                                                            + "." +
2806                                                            X509CertImpl.INFO);
2807 
2808         // Extend its validity
2809         Date firstDate = getStartDate(startDate);
2810         Date lastDate = new Date();
2811         lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
2812         CertificateValidity interval = new CertificateValidity(firstDate,
2813                                                                lastDate);
2814         certInfo.set(X509CertInfo.VALIDITY, interval);
2815 
2816         // Make new serial number
2817         certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
2818                     new java.util.Random().nextInt() & 0x7fffffff));
2819 
2820         // Set owner and issuer fields
2821         X500Name owner;
2822         if (dname == null) {
2823             // Get the owner name from the certificate
2824             owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +
2825                                            X509CertInfo.DN_NAME);
2826         } else {
2827             // Use the owner name specified at the command line
2828             owner = new X500Name(dname);
2829             certInfo.set(X509CertInfo.SUBJECT + "." +
2830                          X509CertInfo.DN_NAME, owner);
2831         }
2832         // Make issuer same as owner (self-signed!)
2833         certInfo.set(X509CertInfo.ISSUER + "." +
2834                      X509CertInfo.DN_NAME, owner);
2835 
2836         // The inner and outer signature algorithms have to match.
2837         // The way we achieve that is really ugly, but there seems to be no
2838         // other solution: We first sign the cert, then retrieve the
2839         // outer sigalg and use it to set the inner sigalg
2840         X509CertImpl newCert = new X509CertImpl(certInfo);
2841         newCert.sign(privKey, sigAlgName);
2842         AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
2843         certInfo.set(CertificateAlgorithmId.NAME + "." +
2844                      CertificateAlgorithmId.ALGORITHM, sigAlgid);
2845 
2846         certInfo.set(X509CertInfo.VERSION,
2847                         new CertificateVersion(CertificateVersion.V3));
2848 
2849         CertificateExtensions ext = createV3Extensions(
2850                 null,
2851                 (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),
2852                 v3ext,
2853                 oldCert.getPublicKey(),
2854                 null);
2855         certInfo.set(X509CertInfo.EXTENSIONS, ext);
2856         // Sign the new certificate
2857         newCert = new X509CertImpl(certInfo);
2858         newCert.sign(privKey, sigAlgName);
2859 
2860         // Store the new certificate as a single-element certificate chain
2861         keyStore.setKeyEntry(alias, privKey,
2862                              (keyPass != null) ? keyPass : storePass,
2863                              new Certificate[] { newCert } );
2864 
2865         if (verbose) {
2866             System.err.println(rb.getString("New.certificate.self.signed."));
2867             System.err.print(newCert.toString());
2868             System.err.println();
2869         }
2870     }
2871 
2872     /**
2873      * Processes a certificate reply from a certificate authority.
2874      *
2875      * <p>Builds a certificate chain on top of the certificate reply,
2876      * using trusted certificates from the keystore. The chain is complete
2877      * after a self-signed certificate has been encountered. The self-signed
2878      * certificate is considered a root certificate authority, and is stored
2879      * at the end of the chain.
2880      *
2881      * <p>The newly generated chain replaces the old chain associated with the
2882      * key entry.
2883      *
2884      * @return true if the certificate reply was installed, otherwise false.
2885      */
2886     private boolean installReply(String alias, InputStream in)
2887         throws Exception
2888     {
2889         if (alias == null) {
2890             alias = keyAlias;
2891         }
2892 
2893         Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2894         PrivateKey privKey = (PrivateKey)objs.fst;
2895         if (keyPass == null) {
2896             keyPass = objs.snd;
2897         }
2898 
2899         Certificate userCert = keyStore.getCertificate(alias);
2900         if (userCert == null) {
2901             MessageFormat form = new MessageFormat
2902                 (rb.getString("alias.has.no.public.key.certificate."));
2903             Object[] source = {alias};
2904             throw new Exception(form.format(source));
2905         }
2906 
2907         // Read the certificates in the reply
2908         Collection<? extends Certificate> c = cf.generateCertificates(in);
2909         if (c.isEmpty()) {
2910             throw new Exception(rb.getString("Reply.has.no.certificates"));
2911         }
2912         Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);
2913         Certificate[] newChain;
2914         if (replyCerts.length == 1) {
2915             // single-cert reply
2916             newChain = establishCertChain(userCert, replyCerts[0]);
2917         } else {
2918             // cert-chain reply (e.g., PKCS#7)
2919             newChain = validateReply(alias, userCert, replyCerts);
2920         }
2921 
2922         // Now store the newly established chain in the keystore. The new
2923         // chain replaces the old one. The chain can be null if user chooses no.
2924         if (newChain != null) {
2925             keyStore.setKeyEntry(alias, privKey,
2926                                  (keyPass != null) ? keyPass : storePass,
2927                                  newChain);
2928             return true;
2929         } else {
2930             return false;
2931         }
2932     }
2933 
2934     /**
2935      * Imports a certificate and adds it to the list of trusted certificates.
2936      *
2937      * @return true if the certificate was added, otherwise false.
2938      */
2939     private boolean addTrustedCert(String alias, InputStream in)
2940         throws Exception
2941     {
2942         if (alias == null) {
2943             throw new Exception(rb.getString("Must.specify.alias"));
2944         }
2945         if (keyStore.containsAlias(alias)) {
2946             MessageFormat form = new MessageFormat(rb.getString
2947                 ("Certificate.not.imported.alias.alias.already.exists"));
2948             Object[] source = {alias};
2949             throw new Exception(form.format(source));
2950         }
2951 
2952         // Read the certificate
2953         X509Certificate cert = null;
2954         try {
2955             cert = (X509Certificate)cf.generateCertificate(in);
2956         } catch (ClassCastException | CertificateException ce) {
2957             throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
2958         }
2959 
2960         if (noprompt) {
2961             checkWeak(rb.getString("the.input"), cert);
2962             keyStore.setCertificateEntry(alias, cert);
2963             return true;
2964         }
2965 
2966         // if certificate is self-signed, make sure it verifies
2967         boolean selfSigned = false;
2968         if (KeyStoreUtil.isSelfSigned(cert)) {
2969             cert.verify(cert.getPublicKey());
2970             selfSigned = true;
2971         }
2972 
2973         // check if cert already exists in keystore
2974         String reply = null;
2975         String trustalias = keyStore.getCertificateAlias(cert);
2976         if (trustalias != null) {
2977             MessageFormat form = new MessageFormat(rb.getString
2978                 ("Certificate.already.exists.in.keystore.under.alias.trustalias."));
2979             Object[] source = {trustalias};
2980             System.err.println(form.format(source));
2981             checkWeak(rb.getString("the.input"), cert);
2982             printWeakWarnings(true);
2983             reply = getYesNoReply
2984                 (rb.getString("Do.you.still.want.to.add.it.no."));
2985         } else if (selfSigned) {
2986             if (trustcacerts && (caks != null) &&
2987                     ((trustalias=caks.getCertificateAlias(cert)) != null)) {
2988                 MessageFormat form = new MessageFormat(rb.getString
2989                         ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
2990                 Object[] source = {trustalias};
2991                 System.err.println(form.format(source));
2992                 checkWeak(rb.getString("the.input"), cert);
2993                 printWeakWarnings(true);
2994                 reply = getYesNoReply
2995                         (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
2996             }
2997             if (trustalias == null) {
2998                 // Print the cert and ask user if they really want to add
2999                 // it to their keystore
3000                 printX509Cert(cert, System.out);
3001                 checkWeak(rb.getString("the.input"), cert);
3002                 printWeakWarnings(true);
3003                 reply = getYesNoReply
3004                         (rb.getString("Trust.this.certificate.no."));
3005             }
3006         }
3007         if (reply != null) {
3008             if ("YES".equals(reply)) {
3009                 keyStore.setCertificateEntry(alias, cert);
3010                 return true;
3011             } else {
3012                 return false;
3013             }
3014         }
3015 
3016         // Not found in this keystore and not self-signed
3017         // Try to establish trust chain
3018         try {
3019             Certificate[] chain = establishCertChain(null, cert);
3020             if (chain != null) {
3021                 keyStore.setCertificateEntry(alias, cert);
3022                 return true;
3023             }
3024         } catch (Exception e) {
3025             // Print the cert and ask user if they really want to add it to
3026             // their keystore
3027             printX509Cert(cert, System.out);
3028             checkWeak(rb.getString("the.input"), cert);
3029             printWeakWarnings(true);
3030             reply = getYesNoReply
3031                 (rb.getString("Trust.this.certificate.no."));
3032             if ("YES".equals(reply)) {
3033                 keyStore.setCertificateEntry(alias, cert);
3034                 return true;
3035             } else {
3036                 return false;
3037             }
3038         }
3039 
3040         return false;
3041     }
3042 
3043     /**
3044      * Prompts user for new password. New password must be different from
3045      * old one.
3046      *
3047      * @param prompt the message that gets prompted on the screen
3048      * @param oldPasswd the current (i.e., old) password
3049      */
3050     private char[] getNewPasswd(String prompt, char[] oldPasswd)
3051         throws Exception
3052     {
3053         char[] entered = null;
3054         char[] reentered = null;
3055 
3056         for (int count = 0; count < 3; count++) {
3057             MessageFormat form = new MessageFormat
3058                 (rb.getString("New.prompt."));
3059             Object[] source = {prompt};
3060             System.err.print(form.format(source));
3061             entered = Password.readPassword(System.in);
3062             passwords.add(entered);
3063             if (entered == null || entered.length < 6) {
3064                 System.err.println(rb.getString
3065                     ("Password.is.too.short.must.be.at.least.6.characters"));
3066             } else if (Arrays.equals(entered, oldPasswd)) {
3067                 System.err.println(rb.getString("Passwords.must.differ"));
3068             } else {
3069                 form = new MessageFormat
3070                         (rb.getString("Re.enter.new.prompt."));
3071                 Object[] src = {prompt};
3072                 System.err.print(form.format(src));
3073                 reentered = Password.readPassword(System.in);
3074                 passwords.add(reentered);
3075                 if (!Arrays.equals(entered, reentered)) {
3076                     System.err.println
3077                         (rb.getString("They.don.t.match.Try.again"));
3078                 } else {
3079                     Arrays.fill(reentered, ' ');
3080                     return entered;
3081                 }
3082             }
3083             if (entered != null) {
3084                 Arrays.fill(entered, ' ');
3085                 entered = null;
3086             }
3087             if (reentered != null) {
3088                 Arrays.fill(reentered, ' ');
3089                 reentered = null;
3090             }
3091         }
3092         throw new Exception(rb.getString("Too.many.failures.try.later"));
3093     }
3094 
3095     /**
3096      * Prompts user for alias name.
3097      * @param prompt the {0} of "Enter {0} alias name:  " in prompt line
3098      * @return the string entered by the user, without the \n at the end
3099      */
3100     private String getAlias(String prompt) throws Exception {
3101         if (prompt != null) {
3102             MessageFormat form = new MessageFormat
3103                 (rb.getString("Enter.prompt.alias.name."));
3104             Object[] source = {prompt};
3105             System.err.print(form.format(source));
3106         } else {
3107             System.err.print(rb.getString("Enter.alias.name."));
3108         }
3109         return (new BufferedReader(new InputStreamReader(
3110                                         System.in))).readLine();
3111     }
3112 
3113     /**
3114      * Prompts user for an input string from the command line (System.in)
3115      * @prompt the prompt string printed
3116      * @return the string entered by the user, without the \n at the end
3117      */
3118     private String inputStringFromStdin(String prompt) throws Exception {
3119         System.err.print(prompt);
3120         return (new BufferedReader(new InputStreamReader(
3121                                         System.in))).readLine();
3122     }
3123 
3124     /**
3125      * Prompts user for key password. User may select to choose the same
3126      * password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.
3127      */
3128     private char[] getKeyPasswd(String alias, String otherAlias,
3129                                 char[] otherKeyPass)
3130         throws Exception
3131     {
3132         int count = 0;
3133         char[] keyPass = null;
3134 
3135         do {
3136             if (otherKeyPass != null) {
3137                 MessageFormat form = new MessageFormat(rb.getString
3138                         ("Enter.key.password.for.alias."));
3139                 Object[] source = {alias};
3140                 System.err.println(form.format(source));
3141 
3142                 form = new MessageFormat(rb.getString
3143                         (".RETURN.if.same.as.for.otherAlias."));
3144                 Object[] src = {otherAlias};
3145                 System.err.print(form.format(src));
3146             } else {
3147                 MessageFormat form = new MessageFormat(rb.getString
3148                         ("Enter.key.password.for.alias."));
3149                 Object[] source = {alias};
3150                 System.err.print(form.format(source));
3151             }
3152             System.err.flush();
3153             keyPass = Password.readPassword(System.in);
3154             passwords.add(keyPass);
3155             if (keyPass == null) {
3156                 keyPass = otherKeyPass;
3157             }
3158             count++;
3159         } while ((keyPass == null) && count < 3);
3160 
3161         if (keyPass == null) {
3162             throw new Exception(rb.getString("Too.many.failures.try.later"));
3163         }
3164 
3165         return keyPass;
3166     }
3167 
3168     private String withWeak(String alg) {
3169         if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
3170             return alg;
3171         } else {
3172             return String.format(rb.getString("with.weak"), alg);
3173         }
3174     }
3175 
3176     private String withWeak(PublicKey key) {
3177         if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
3178             int kLen = KeyUtil.getKeySize(key);
3179             if (kLen >= 0) {
3180                 return String.format(rb.getString("key.bit"),
3181                         kLen, key.getAlgorithm());
3182             } else {
3183                 return String.format(
3184                         rb.getString("unknown.size.1"), key.getAlgorithm());
3185             }
3186         } else {
3187             return String.format(rb.getString("key.bit.weak"),
3188                     KeyUtil.getKeySize(key), key.getAlgorithm());
3189         }
3190     }
3191 
3192     /**
3193      * Prints a certificate in a human readable format.
3194      */
3195     private void printX509Cert(X509Certificate cert, PrintStream out)
3196         throws Exception
3197     {
3198 
3199         MessageFormat form = new MessageFormat
3200                 (rb.getString(".PATTERN.printX509Cert.with.weak"));
3201         PublicKey pkey = cert.getPublicKey();
3202         String sigName = cert.getSigAlgName();
3203         // No need to warn about sigalg of a trust anchor
3204         if (!isTrustedCert(cert)) {
3205             sigName = withWeak(sigName);
3206         }
3207         Object[] source = {cert.getSubjectDN().toString(),
3208                         cert.getIssuerDN().toString(),
3209                         cert.getSerialNumber().toString(16),
3210                         cert.getNotBefore().toString(),
3211                         cert.getNotAfter().toString(),
3212                         getCertFingerPrint("SHA-1", cert),
3213                         getCertFingerPrint("SHA-256", cert),
3214                         sigName,
3215                         withWeak(pkey),
3216                         cert.getVersion()
3217                         };
3218         out.println(form.format(source));
3219 
3220         if (cert instanceof X509CertImpl) {
3221             X509CertImpl impl = (X509CertImpl)cert;
3222             X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME
3223                                                            + "." +
3224                                                            X509CertImpl.INFO);
3225             CertificateExtensions exts = (CertificateExtensions)
3226                     certInfo.get(X509CertInfo.EXTENSIONS);
3227             if (exts != null) {
3228                 printExtensions(rb.getString("Extensions."), exts, out);
3229             }
3230         }
3231     }
3232 
3233     private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)
3234             throws Exception {
3235         int extnum = 0;
3236         Iterator<Extension> i1 = exts.getAllExtensions().iterator();
3237         Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();
3238         while (i1.hasNext() || i2.hasNext()) {
3239             Extension ext = i1.hasNext()?i1.next():i2.next();
3240             if (extnum == 0) {
3241                 out.println();
3242                 out.println(title);
3243                 out.println();
3244             }
3245             out.print("#"+(++extnum)+": "+ ext);
3246             if (ext.getClass() == Extension.class) {
3247                 byte[] v = ext.getExtensionValue();
3248                 if (v.length == 0) {
3249                     out.println(rb.getString(".Empty.value."));
3250                 } else {
3251                     new sun.security.util.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);
3252                     out.println();
3253                 }
3254             }
3255             out.println();
3256         }
3257     }
3258 
3259     /**
3260      * Locates a signer for a given certificate from a given keystore and
3261      * returns the signer's certificate.
3262      * @param cert the certificate whose signer is searched, not null
3263      * @param ks the keystore to search with, not null
3264      * @return <code>cert</code> itself if it's already inside <code>ks</code>,
3265      * or a certificate inside <code>ks</code> who signs <code>cert</code>,
3266      * or null otherwise. A label is added.
3267      */
3268     private static Pair<String,Certificate>
3269             getSigner(Certificate cert, KeyStore ks) throws Exception {
3270         if (ks.getCertificateAlias(cert) != null) {
3271             return new Pair<>("", cert);
3272         }
3273         for (Enumeration<String> aliases = ks.aliases();
3274                 aliases.hasMoreElements(); ) {
3275             String name = aliases.nextElement();
3276             Certificate trustedCert = ks.getCertificate(name);
3277             if (trustedCert != null) {
3278                 try {
3279                     cert.verify(trustedCert.getPublicKey());
3280                     return new Pair<>(name, trustedCert);
3281                 } catch (Exception e) {
3282                     // Not verified, skip to the next one
3283                 }
3284             }
3285         }
3286         return null;
3287     }
3288 
3289     /**
3290      * Gets an X.500 name suitable for inclusion in a certification request.
3291      */
3292     private X500Name getX500Name() throws IOException {
3293         BufferedReader in;
3294         in = new BufferedReader(new InputStreamReader(System.in));
3295         String commonName = "Unknown";
3296         String organizationalUnit = "Unknown";
3297         String organization = "Unknown";
3298         String city = "Unknown";
3299         String state = "Unknown";
3300         String country = "Unknown";
3301         X500Name name;
3302         String userInput = null;
3303 
3304         int maxRetry = 20;
3305         do {
3306             if (maxRetry-- < 0) {
3307                 throw new RuntimeException(rb.getString(
3308                         "Too.many.retries.program.terminated"));
3309             }
3310             commonName = inputString(in,
3311                     rb.getString("What.is.your.first.and.last.name."),
3312                     commonName);
3313             organizationalUnit = inputString(in,
3314                     rb.getString
3315                         ("What.is.the.name.of.your.organizational.unit."),
3316                     organizationalUnit);
3317             organization = inputString(in,
3318                     rb.getString("What.is.the.name.of.your.organization."),
3319                     organization);
3320             city = inputString(in,
3321                     rb.getString("What.is.the.name.of.your.City.or.Locality."),
3322                     city);
3323             state = inputString(in,
3324                     rb.getString("What.is.the.name.of.your.State.or.Province."),
3325                     state);
3326             country = inputString(in,
3327                     rb.getString
3328                         ("What.is.the.two.letter.country.code.for.this.unit."),
3329                     country);
3330             name = new X500Name(commonName, organizationalUnit, organization,
3331                                 city, state, country);
3332             MessageFormat form = new MessageFormat
3333                 (rb.getString("Is.name.correct."));
3334             Object[] source = {name};
3335             userInput = inputString
3336                 (in, form.format(source), rb.getString("no"));
3337         } while (collator.compare(userInput, rb.getString("yes")) != 0 &&
3338                  collator.compare(userInput, rb.getString("y")) != 0);
3339 
3340         System.err.println();
3341         return name;
3342     }
3343 
3344     private String inputString(BufferedReader in, String prompt,
3345                                String defaultValue)
3346         throws IOException
3347     {
3348         System.err.println(prompt);
3349         MessageFormat form = new MessageFormat
3350                 (rb.getString(".defaultValue."));
3351         Object[] source = {defaultValue};
3352         System.err.print(form.format(source));
3353         System.err.flush();
3354 
3355         String value = in.readLine();
3356         if (value == null || collator.compare(value, "") == 0) {
3357             value = defaultValue;
3358         }
3359         return value;
3360     }
3361 
3362     /**
3363      * Writes an X.509 certificate in base64 or binary encoding to an output
3364      * stream.
3365      */
3366     private void dumpCert(Certificate cert, PrintStream out)
3367         throws IOException, CertificateException
3368     {
3369         if (rfc) {
3370             out.println(X509Factory.BEGIN_CERT);
3371             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));
3372             out.println(X509Factory.END_CERT);
3373         } else {
3374             out.write(cert.getEncoded()); // binary
3375         }
3376     }
3377 
3378     /**
3379      * Converts a byte to hex digit and writes to the supplied buffer
3380      */
3381     private void byte2hex(byte b, StringBuffer buf) {
3382         char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
3383                             '9', 'A', 'B', 'C', 'D', 'E', 'F' };
3384         int high = ((b & 0xf0) >> 4);
3385         int low = (b & 0x0f);
3386         buf.append(hexChars[high]);
3387         buf.append(hexChars[low]);
3388     }
3389 
3390     /**
3391      * Converts a byte array to hex string
3392      */
3393     private String toHexString(byte[] block) {
3394         StringBuffer buf = new StringBuffer();
3395         int len = block.length;
3396         for (int i = 0; i < len; i++) {
3397              byte2hex(block[i], buf);
3398              if (i < len-1) {
3399                  buf.append(":");
3400              }
3401         }
3402         return buf.toString();
3403     }
3404 
3405     /**
3406      * Recovers (private) key associated with given alias.
3407      *
3408      * @return an array of objects, where the 1st element in the array is the
3409      * recovered private key, and the 2nd element is the password used to
3410      * recover it.
3411      */
3412     private Pair<Key,char[]> recoverKey(String alias, char[] storePass,
3413                                        char[] keyPass)
3414         throws Exception
3415     {
3416         Key key = null;
3417 
3418         if (keyStore.containsAlias(alias) == false) {
3419             MessageFormat form = new MessageFormat
3420                 (rb.getString("Alias.alias.does.not.exist"));
3421             Object[] source = {alias};
3422             throw new Exception(form.format(source));
3423         }
3424         if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&
3425                 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
3426             MessageFormat form = new MessageFormat
3427                 (rb.getString("Alias.alias.has.no.key"));
3428             Object[] source = {alias};
3429             throw new Exception(form.format(source));
3430         }
3431 
3432         if (keyPass == null) {
3433             // Try to recover the key using the keystore password
3434             try {
3435                 key = keyStore.getKey(alias, storePass);
3436 
3437                 keyPass = storePass;
3438                 passwords.add(keyPass);
3439             } catch (UnrecoverableKeyException e) {
3440                 // Did not work out, so prompt user for key password
3441                 if (!token) {
3442                     keyPass = getKeyPasswd(alias, null, null);
3443                     key = keyStore.getKey(alias, keyPass);
3444                 } else {
3445                     throw e;
3446                 }
3447             }
3448         } else {
3449             key = keyStore.getKey(alias, keyPass);
3450         }
3451 
3452         return Pair.of(key, keyPass);
3453     }
3454 
3455     /**
3456      * Recovers entry associated with given alias.
3457      *
3458      * @return an array of objects, where the 1st element in the array is the
3459      * recovered entry, and the 2nd element is the password used to
3460      * recover it (null if no password).
3461      */
3462     private Pair<Entry,char[]> recoverEntry(KeyStore ks,
3463                             String alias,
3464                             char[] pstore,
3465                             char[] pkey) throws Exception {
3466 
3467         if (ks.containsAlias(alias) == false) {
3468             MessageFormat form = new MessageFormat
3469                 (rb.getString("Alias.alias.does.not.exist"));
3470             Object[] source = {alias};
3471             throw new Exception(form.format(source));
3472         }
3473 
3474         PasswordProtection pp = null;
3475         Entry entry;
3476 
3477         try {
3478             // First attempt to access entry without key password
3479             // (PKCS11 entry or trusted certificate entry, for example)
3480 
3481             entry = ks.getEntry(alias, pp);
3482             pkey = null;
3483         } catch (UnrecoverableEntryException une) {
3484 
3485             if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
3486                 KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
3487                 // should not happen, but a possibility
3488                 throw une;
3489             }
3490 
3491             // entry is protected
3492 
3493             if (pkey != null) {
3494 
3495                 // try provided key password
3496 
3497                 pp = new PasswordProtection(pkey);
3498                 entry = ks.getEntry(alias, pp);
3499 
3500             } else {
3501 
3502                 // try store pass
3503 
3504                 try {
3505                     pp = new PasswordProtection(pstore);
3506                     entry = ks.getEntry(alias, pp);
3507                     pkey = pstore;
3508                 } catch (UnrecoverableEntryException une2) {
3509                     if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
3510 
3511                         // P12 keystore currently does not support separate
3512                         // store and entry passwords
3513 
3514                         throw une2;
3515                     } else {
3516 
3517                         // prompt for entry password
3518 
3519                         pkey = getKeyPasswd(alias, null, null);
3520                         pp = new PasswordProtection(pkey);
3521                         entry = ks.getEntry(alias, pp);
3522                     }
3523                 }
3524             }
3525         }
3526 
3527         return Pair.of(entry, pkey);
3528     }
3529     /**
3530      * Gets the requested finger print of the certificate.
3531      */
3532     private String getCertFingerPrint(String mdAlg, Certificate cert)
3533         throws Exception
3534     {
3535         byte[] encCertInfo = cert.getEncoded();
3536         MessageDigest md = MessageDigest.getInstance(mdAlg);
3537         byte[] digest = md.digest(encCertInfo);
3538         return toHexString(digest);
3539     }
3540 
3541     /**
3542      * Prints warning about missing integrity check.
3543      */
3544     private void printNoIntegrityWarning() {
3545         System.err.println();
3546         System.err.println(rb.getString
3547             (".WARNING.WARNING.WARNING."));
3548         System.err.println(rb.getString
3549             (".The.integrity.of.the.information.stored.in.your.keystore."));
3550         System.err.println(rb.getString
3551             (".WARNING.WARNING.WARNING."));
3552         System.err.println();
3553     }
3554 
3555     /**
3556      * Validates chain in certification reply, and returns the ordered
3557      * elements of the chain (with user certificate first, and root
3558      * certificate last in the array).
3559      *
3560      * @param alias the alias name
3561      * @param userCert the user certificate of the alias
3562      * @param replyCerts the chain provided in the reply
3563      */
3564     private Certificate[] validateReply(String alias,
3565                                         Certificate userCert,
3566                                         Certificate[] replyCerts)
3567         throws Exception
3568     {
3569 
3570         checkWeak(rb.getString("reply"), replyCerts);
3571 
3572         // order the certs in the reply (bottom-up).
3573         // we know that all certs in the reply are of type X.509, because
3574         // we parsed them using an X.509 certificate factory
3575         int i;
3576         PublicKey userPubKey = userCert.getPublicKey();
3577 
3578         // Remove duplicated certificates.
3579         HashSet<Certificate> nodup = new HashSet<>(Arrays.asList(replyCerts));
3580         replyCerts = nodup.toArray(new Certificate[nodup.size()]);
3581 
3582         for (i=0; i<replyCerts.length; i++) {
3583             if (userPubKey.equals(replyCerts[i].getPublicKey())) {
3584                 break;
3585             }
3586         }
3587         if (i == replyCerts.length) {
3588             MessageFormat form = new MessageFormat(rb.getString
3589                 ("Certificate.reply.does.not.contain.public.key.for.alias."));
3590             Object[] source = {alias};
3591             throw new Exception(form.format(source));
3592         }
3593 
3594         Certificate tmpCert = replyCerts[0];
3595         replyCerts[0] = replyCerts[i];
3596         replyCerts[i] = tmpCert;
3597 
3598         X509Certificate thisCert = (X509Certificate)replyCerts[0];
3599 
3600         for (i=1; i < replyCerts.length-1; i++) {
3601             // find a cert in the reply who signs thisCert
3602             int j;
3603             for (j=i; j<replyCerts.length; j++) {
3604                 if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {
3605                     tmpCert = replyCerts[i];
3606                     replyCerts[i] = replyCerts[j];
3607                     replyCerts[j] = tmpCert;
3608                     thisCert = (X509Certificate)replyCerts[i];
3609                     break;
3610                 }
3611             }
3612             if (j == replyCerts.length) {
3613                 throw new Exception
3614                     (rb.getString("Incomplete.certificate.chain.in.reply"));
3615             }
3616         }
3617 
3618         if (noprompt) {
3619             return replyCerts;
3620         }
3621 
3622         // do we trust the cert at the top?
3623         Certificate topCert = replyCerts[replyCerts.length-1];
3624         boolean fromKeyStore = true;
3625         Pair<String,Certificate> root = getSigner(topCert, keyStore);
3626         if (root == null && trustcacerts && caks != null) {
3627             root = getSigner(topCert, caks);
3628             fromKeyStore = false;
3629         }
3630         if (root == null) {
3631             System.err.println();
3632             System.err.println
3633                     (rb.getString("Top.level.certificate.in.reply."));
3634             printX509Cert((X509Certificate)topCert, System.out);
3635             System.err.println();
3636             System.err.print(rb.getString(".is.not.trusted."));
3637             printWeakWarnings(true);
3638             String reply = getYesNoReply
3639                     (rb.getString("Install.reply.anyway.no."));
3640             if ("NO".equals(reply)) {
3641                 return null;
3642             }
3643         } else {
3644             if (root.snd != topCert) {
3645                 // append the root CA cert to the chain
3646                 Certificate[] tmpCerts =
3647                     new Certificate[replyCerts.length+1];
3648                 System.arraycopy(replyCerts, 0, tmpCerts, 0,
3649                                  replyCerts.length);
3650                 tmpCerts[tmpCerts.length-1] = root.snd;
3651                 replyCerts = tmpCerts;
3652                 checkWeak(String.format(rb.getString(fromKeyStore ?
3653                                             "alias.in.keystore" :
3654                                             "alias.in.cacerts"),
3655                                         root.fst),
3656                           root.snd);
3657             }
3658         }
3659         return replyCerts;
3660     }
3661 
3662     /**
3663      * Establishes a certificate chain (using trusted certificates in the
3664      * keystore and cacerts), starting with the reply (certToVerify)
3665      * and ending at a self-signed certificate found in the keystore.
3666      *
3667      * @param userCert optional existing certificate, mostly likely be the
3668      *                 original self-signed cert created by -genkeypair.
3669      *                 It must have the same public key as certToVerify
3670      *                 but cannot be the same cert.
3671      * @param certToVerify the starting certificate to build the chain
3672      * @returns the established chain, might be null if user decides not
3673      */
3674     private Certificate[] establishCertChain(Certificate userCert,
3675                                              Certificate certToVerify)
3676         throws Exception
3677     {
3678         if (userCert != null) {
3679             // Make sure that the public key of the certificate reply matches
3680             // the original public key in the keystore
3681             PublicKey origPubKey = userCert.getPublicKey();
3682             PublicKey replyPubKey = certToVerify.getPublicKey();
3683             if (!origPubKey.equals(replyPubKey)) {
3684                 throw new Exception(rb.getString
3685                         ("Public.keys.in.reply.and.keystore.don.t.match"));
3686             }
3687 
3688             // If the two certs are identical, we're done: no need to import
3689             // anything
3690             if (certToVerify.equals(userCert)) {
3691                 throw new Exception(rb.getString
3692                         ("Certificate.reply.and.certificate.in.keystore.are.identical"));
3693             }
3694         }
3695 
3696         // Build a hash table of all certificates in the keystore.
3697         // Use the subject distinguished name as the key into the hash table.
3698         // All certificates associated with the same subject distinguished
3699         // name are stored in the same hash table entry as a vector.
3700         Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
3701         if (keyStore.size() > 0) {
3702             certs = new Hashtable<>(11);
3703             keystorecerts2Hashtable(keyStore, certs);
3704         }
3705         if (trustcacerts) {
3706             if (caks!=null && caks.size()>0) {
3707                 if (certs == null) {
3708                     certs = new Hashtable<>(11);
3709                 }
3710                 keystorecerts2Hashtable(caks, certs);
3711             }
3712         }
3713 
3714         // start building chain
3715         Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
3716         if (buildChain(
3717                 new Pair<>(rb.getString("the.input"),
3718                            (X509Certificate) certToVerify),
3719                 chain, certs)) {
3720             for (Pair<String,X509Certificate> p : chain) {
3721                 checkWeak(p.fst, p.snd);
3722             }
3723             Certificate[] newChain =
3724                     new Certificate[chain.size()];
3725             // buildChain() returns chain with self-signed root-cert first and
3726             // user-cert last, so we need to invert the chain before we store
3727             // it
3728             int j=0;
3729             for (int i=chain.size()-1; i>=0; i--) {
3730                 newChain[j] = chain.elementAt(i).snd;
3731                 j++;
3732             }
3733             return newChain;
3734         } else {
3735             throw new Exception
3736                 (rb.getString("Failed.to.establish.chain.from.reply"));
3737         }
3738     }
3739 
3740     /**
3741      * Recursively tries to establish chain from pool of certs starting from
3742      * certToVerify until a self-signed cert is found, and fill the certs found
3743      * into chain. Each cert in the chain signs the next one.
3744      *
3745      * This method is able to recover from an error, say, if certToVerify
3746      * is signed by certA but certA has no issuer in certs and itself is not
3747      * self-signed, the method can try another certB that also signs
3748      * certToVerify and look for signer of certB, etc, etc.
3749      *
3750      * Each cert in chain comes with a label showing its origin. The label is
3751      * used in the warning message when the cert is considered a risk.
3752      *
3753      * @param certToVerify the cert that needs to be verified.
3754      * @param chain the chain that's being built.
3755      * @param certs the pool of trusted certs
3756      *
3757      * @return true if successful, false otherwise.
3758      */
3759     private boolean buildChain(Pair<String,X509Certificate> certToVerify,
3760             Vector<Pair<String,X509Certificate>> chain,
3761             Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
3762         if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
3763             // reached self-signed root cert;
3764             // no verification needed because it's trusted.
3765             chain.addElement(certToVerify);
3766             return true;
3767         }
3768 
3769         Principal issuer = certToVerify.snd.getIssuerDN();
3770 
3771         // Get the issuer's certificate(s)
3772         Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
3773         if (vec == null) {
3774             return false;
3775         }
3776 
3777         // Try out each certificate in the vector, until we find one
3778         // whose public key verifies the signature of the certificate
3779         // in question.
3780         for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
3781                 issuerCerts.hasMoreElements(); ) {
3782             Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
3783             PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
3784             try {
3785                 certToVerify.snd.verify(issuerPubKey);
3786             } catch (Exception e) {
3787                 continue;
3788             }
3789             if (buildChain(issuerCert, chain, certs)) {
3790                 chain.addElement(certToVerify);
3791                 return true;
3792             }
3793         }
3794         return false;
3795     }
3796 
3797     /**
3798      * Prompts user for yes/no decision.
3799      *
3800      * @return the user's decision, can only be "YES" or "NO"
3801      */
3802     private String getYesNoReply(String prompt)
3803         throws IOException
3804     {
3805         String reply = null;
3806         int maxRetry = 20;
3807         do {
3808             if (maxRetry-- < 0) {
3809                 throw new RuntimeException(rb.getString(
3810                         "Too.many.retries.program.terminated"));
3811             }
3812             System.err.print(prompt);
3813             System.err.flush();
3814             reply = (new BufferedReader(new InputStreamReader
3815                                         (System.in))).readLine();
3816             if (reply == null ||
3817                 collator.compare(reply, "") == 0 ||
3818                 collator.compare(reply, rb.getString("n")) == 0 ||
3819                 collator.compare(reply, rb.getString("no")) == 0) {
3820                 reply = "NO";
3821             } else if (collator.compare(reply, rb.getString("y")) == 0 ||
3822                        collator.compare(reply, rb.getString("yes")) == 0) {
3823                 reply = "YES";
3824             } else {
3825                 System.err.println(rb.getString("Wrong.answer.try.again"));
3826                 reply = null;
3827             }
3828         } while (reply == null);
3829         return reply;
3830     }
3831 
3832     /**
3833      * Stores the (leaf) certificates of a keystore in a hashtable.
3834      * All certs belonging to the same CA are stored in a vector that
3835      * in turn is stored in the hashtable, keyed by the CA's subject DN.
3836      * Each cert comes with a string label that shows its origin and alias.
3837      */
3838     private void keystorecerts2Hashtable(KeyStore ks,
3839                 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
3840         throws Exception {
3841 
3842         for (Enumeration<String> aliases = ks.aliases();
3843                                         aliases.hasMoreElements(); ) {
3844             String alias = aliases.nextElement();
3845             Certificate cert = ks.getCertificate(alias);
3846             if (cert != null) {
3847                 Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
3848                 Pair<String,X509Certificate> pair = new Pair<>(
3849                         String.format(
3850                                 rb.getString(ks == caks ?
3851                                         "alias.in.cacerts" :
3852                                         "alias.in.keystore"),
3853                                 alias),
3854                         (X509Certificate)cert);
3855                 Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
3856                 if (vec == null) {
3857                     vec = new Vector<>();
3858                     vec.addElement(pair);
3859                 } else {
3860                     if (!vec.contains(pair)) {
3861                         vec.addElement(pair);
3862                     }
3863                 }
3864                 hash.put(subjectDN, vec);
3865             }
3866         }
3867     }
3868 
3869     /**
3870      * Returns the issue time that's specified the -startdate option
3871      * @param s the value of -startdate option
3872      */
3873     private static Date getStartDate(String s) throws IOException {
3874         Calendar c = new GregorianCalendar();
3875         if (s != null) {
3876             IOException ioe = new IOException(
3877                     rb.getString("Illegal.startdate.value"));
3878             int len = s.length();
3879             if (len == 0) {
3880                 throw ioe;
3881             }
3882             if (s.charAt(0) == '-' || s.charAt(0) == '+') {
3883                 // Form 1: ([+-]nnn[ymdHMS])+
3884                 int start = 0;
3885                 while (start < len) {
3886                     int sign = 0;
3887                     switch (s.charAt(start)) {
3888                         case '+': sign = 1; break;
3889                         case '-': sign = -1; break;
3890                         default: throw ioe;
3891                     }
3892                     int i = start+1;
3893                     for (; i<len; i++) {
3894                         char ch = s.charAt(i);
3895                         if (ch < '0' || ch > '9') break;
3896                     }
3897                     if (i == start+1) throw ioe;
3898                     int number = Integer.parseInt(s.substring(start+1, i));
3899                     if (i >= len) throw ioe;
3900                     int unit = 0;
3901                     switch (s.charAt(i)) {
3902                         case 'y': unit = Calendar.YEAR; break;
3903                         case 'm': unit = Calendar.MONTH; break;
3904                         case 'd': unit = Calendar.DATE; break;
3905                         case 'H': unit = Calendar.HOUR; break;
3906                         case 'M': unit = Calendar.MINUTE; break;
3907                         case 'S': unit = Calendar.SECOND; break;
3908                         default: throw ioe;
3909                     }
3910                     c.add(unit, sign * number);
3911                     start = i + 1;
3912                 }
3913             } else  {
3914                 // Form 2: [yyyy/mm/dd] [HH:MM:SS]
3915                 String date = null, time = null;
3916                 if (len == 19) {
3917                     date = s.substring(0, 10);
3918                     time = s.substring(11);
3919                     if (s.charAt(10) != ' ')
3920                         throw ioe;
3921                 } else if (len == 10) {
3922                     date = s;
3923                 } else if (len == 8) {
3924                     time = s;
3925                 } else {
3926                     throw ioe;
3927                 }
3928                 if (date != null) {
3929                     if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {
3930                         c.set(Integer.valueOf(date.substring(0, 4)),
3931                                 Integer.valueOf(date.substring(5, 7))-1,
3932                                 Integer.valueOf(date.substring(8, 10)));
3933                     } else {
3934                         throw ioe;
3935                     }
3936                 }
3937                 if (time != null) {
3938                     if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {
3939                         c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));
3940                         c.set(Calendar.MINUTE, Integer.valueOf(time.substring(3, 5)));
3941                         c.set(Calendar.SECOND, Integer.valueOf(time.substring(6, 8)));
3942                         c.set(Calendar.MILLISECOND, 0);
3943                     } else {
3944                         throw ioe;
3945                     }
3946                 }
3947             }
3948         }
3949         return c.getTime();
3950     }
3951 
3952     /**
3953      * Match a command (may be abbreviated) with a command set.
3954      * @param s the command provided
3955      * @param list the legal command set. If there is a null, commands after it
3956      * are regarded experimental, which means they are supported but their
3957      * existence should not be revealed to user.
3958      * @return the position of a single match, or -1 if none matched
3959      * @throws Exception if s is ambiguous
3960      */
3961     private static int oneOf(String s, String... list) throws Exception {
3962         int[] match = new int[list.length];
3963         int nmatch = 0;
3964         int experiment = Integer.MAX_VALUE;
3965         for (int i = 0; i<list.length; i++) {
3966             String one = list[i];
3967             if (one == null) {
3968                 experiment = i;
3969                 continue;
3970             }
3971             if (one.toLowerCase(Locale.ENGLISH)
3972                     .startsWith(s.toLowerCase(Locale.ENGLISH))) {
3973                 match[nmatch++] = i;
3974             } else {
3975                 StringBuilder sb = new StringBuilder();
3976                 boolean first = true;
3977                 for (char c: one.toCharArray()) {
3978                     if (first) {
3979                         sb.append(c);
3980                         first = false;
3981                     } else {
3982                         if (!Character.isLowerCase(c)) {
3983                             sb.append(c);
3984                         }
3985                     }
3986                 }
3987                 if (sb.toString().equalsIgnoreCase(s)) {
3988                     match[nmatch++] = i;
3989                 }
3990             }
3991         }
3992         if (nmatch == 0) {
3993             return -1;
3994         } else if (nmatch == 1) {
3995             return match[0];
3996         } else {
3997             // If multiple matches is in experimental commands, ignore them
3998             if (match[1] > experiment) {
3999                 return match[0];
4000             }
4001             StringBuilder sb = new StringBuilder();
4002             MessageFormat form = new MessageFormat(rb.getString
4003                 ("command.{0}.is.ambiguous."));
4004             Object[] source = {s};
4005             sb.append(form.format(source));
4006             sb.append("\n    ");
4007             for (int i=0; i<nmatch && match[i]<experiment; i++) {
4008                 sb.append(' ');
4009                 sb.append(list[match[i]]);
4010             }
4011             throw new Exception(sb.toString());
4012         }
4013     }
4014 
4015     /**
4016      * Create a GeneralName object from known types
4017      * @param t one of 5 known types
4018      * @param v value
4019      * @return which one
4020      */
4021     private GeneralName createGeneralName(String t, String v)
4022             throws Exception {
4023         GeneralNameInterface gn;
4024         int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
4025         if (p < 0) {
4026             throw new Exception(rb.getString(
4027                     "Unrecognized.GeneralName.type.") + t);
4028         }
4029         switch (p) {
4030             case 0: gn = new RFC822Name(v); break;
4031             case 1: gn = new URIName(v); break;
4032             case 2: gn = new DNSName(v); break;
4033             case 3: gn = new IPAddressName(v); break;
4034             default: gn = new OIDName(v); break; //4
4035         }
4036         return new GeneralName(gn);
4037     }
4038 
4039     private static final String[] extSupported = {
4040                         "BasicConstraints",
4041                         "KeyUsage",
4042                         "ExtendedKeyUsage",
4043                         "SubjectAlternativeName",
4044                         "IssuerAlternativeName",
4045                         "SubjectInfoAccess",
4046                         "AuthorityInfoAccess",
4047                         null,
4048                         "CRLDistributionPoints",
4049     };
4050 
4051     private ObjectIdentifier findOidForExtName(String type)
4052             throws Exception {
4053         switch (oneOf(type, extSupported)) {
4054             case 0: return PKIXExtensions.BasicConstraints_Id;
4055             case 1: return PKIXExtensions.KeyUsage_Id;
4056             case 2: return PKIXExtensions.ExtendedKeyUsage_Id;
4057             case 3: return PKIXExtensions.SubjectAlternativeName_Id;
4058             case 4: return PKIXExtensions.IssuerAlternativeName_Id;
4059             case 5: return PKIXExtensions.SubjectInfoAccess_Id;
4060             case 6: return PKIXExtensions.AuthInfoAccess_Id;
4061             case 8: return PKIXExtensions.CRLDistributionPoints_Id;
4062             default: return new ObjectIdentifier(type);
4063         }
4064     }
4065 
4066     // Add an extension into a CertificateExtensions, always using OID as key
4067     private static void setExt(CertificateExtensions result, Extension ex)
4068             throws IOException {
4069         result.set(ex.getId(), ex);
4070     }
4071 
4072     /**
4073      * Create X509v3 extensions from a string representation. Note that the
4074      * SubjectKeyIdentifierExtension will always be created non-critical besides
4075      * the extension requested in the <code>extstr</code> argument.
4076      *
4077      * @param requestedEx the requested extensions, can be null, used for -gencert
4078      * @param existingEx the original extensions, can be null, used for -selfcert
4079      * @param extstrs -ext values, Read keytool doc
4080      * @param pkey the public key for the certificate
4081      * @param akey the public key for the authority (issuer)
4082      * @return the created CertificateExtensions
4083      */
4084     private CertificateExtensions createV3Extensions(
4085             CertificateExtensions requestedEx,
4086             CertificateExtensions existingEx,
4087             List <String> extstrs,
4088             PublicKey pkey,
4089             PublicKey akey) throws Exception {
4090 
4091         // By design, inside a CertificateExtensions object, all known
4092         // extensions uses name (say, "BasicConstraints") as key and
4093         // a child Extension type (say, "BasicConstraintsExtension")
4094         // as value, unknown extensions uses OID as key and bare
4095         // Extension object as value. This works fine inside JDK.
4096         //
4097         // However, in keytool, there is no way to prevent people
4098         // using OID in -ext, either as a new extension, or in a
4099         // honored value. Thus here we (ab)use CertificateExtensions
4100         // by always using OID as key and value can be of any type.
4101 
4102         if (existingEx != null && requestedEx != null) {
4103             // This should not happen
4104             throw new Exception("One of request and original should be null.");
4105         }
4106         // A new extensions always using OID as key
4107         CertificateExtensions result = new CertificateExtensions();
4108         if (existingEx != null) {
4109             for (Extension ex: existingEx.getAllExtensions()) {
4110                 setExt(result, ex);
4111             }
4112         }
4113         try {
4114             // name{:critical}{=value}
4115             // Honoring requested extensions
4116             if (requestedEx != null) {
4117                 // The existing requestedEx might use names as keys,
4118                 // translate to all-OID first.
4119                 CertificateExtensions request2 = new CertificateExtensions();
4120                 for (sun.security.x509.Extension ex: requestedEx.getAllExtensions()) {
4121                     request2.set(ex.getId(), ex);
4122                 }
4123                 for(String extstr: extstrs) {
4124                     if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {
4125                         List<String> list = Arrays.asList(
4126                                 extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));
4127                         // First check existence of "all"
4128                         if (list.contains("all")) {
4129                             for (Extension ex: request2.getAllExtensions()) {
4130                                 setExt(result, ex);
4131                             }
4132                         }
4133                         // one by one for others
4134                         for (String item: list) {
4135                             if (item.equals("all")) continue;
4136 
4137                             // add or remove
4138                             boolean add;
4139                             // -1, unchanged, 0 critical, 1 non-critical
4140                             int action = -1;
4141                             String type = null;
4142                             if (item.startsWith("-")) {
4143                                 add = false;
4144                                 type = item.substring(1);
4145                             } else {
4146                                 add = true;
4147                                 int colonpos = item.indexOf(':');
4148                                 if (colonpos >= 0) {
4149                                     type = item.substring(0, colonpos);
4150                                     action = oneOf(item.substring(colonpos+1),
4151                                             "critical", "non-critical");
4152                                     if (action == -1) {
4153                                         throw new Exception(rb.getString
4154                                             ("Illegal.value.") + item);
4155                                     }
4156                                 } else {
4157                                     type = item;
4158                                 }
4159                             }
4160                             String n = findOidForExtName(type).toString();
4161                             if (add) {
4162                                 Extension e = request2.get(n);
4163                                 if (!e.isCritical() && action == 0
4164                                         || e.isCritical() && action == 1) {
4165                                     e = Extension.newExtension(
4166                                             e.getExtensionId(),
4167                                             !e.isCritical(),
4168                                             e.getExtensionValue());
4169                                 }
4170                                 setExt(result, e);
4171                             } else {
4172                                 result.delete(n);
4173                             }
4174                         }
4175                         break;
4176                     }
4177                 }
4178             }
4179             for(String extstr: extstrs) {
4180                 String name, value;
4181                 boolean isCritical = false;
4182 
4183                 int eqpos = extstr.indexOf('=');
4184                 if (eqpos >= 0) {
4185                     name = extstr.substring(0, eqpos);
4186                     value = extstr.substring(eqpos+1);
4187                 } else {
4188                     name = extstr;
4189                     value = null;
4190                 }
4191 
4192                 int colonpos = name.indexOf(':');
4193                 if (colonpos >= 0) {
4194                     if (oneOf(name.substring(colonpos+1), "critical") == 0) {
4195                         isCritical = true;
4196                     }
4197                     name = name.substring(0, colonpos);
4198                 }
4199 
4200                 if (name.equalsIgnoreCase("honored")) {
4201                     continue;
4202                 }
4203                 int exttype = oneOf(name, extSupported);
4204                 switch (exttype) {
4205                     case 0:     // BC
4206                         int pathLen = -1;
4207                         boolean isCA = false;
4208                         if (value == null) {
4209                             isCA = true;
4210                         } else {
4211                             try {   // the abbr format
4212                                 pathLen = Integer.parseInt(value);
4213                                 isCA = true;
4214                             } catch (NumberFormatException ufe) {
4215                                 // ca:true,pathlen:1
4216                                 for (String part: value.split(",")) {
4217                                     String[] nv = part.split(":");
4218                                     if (nv.length != 2) {
4219                                         throw new Exception(rb.getString
4220                                                 ("Illegal.value.") + extstr);
4221                                     } else {
4222                                         if (nv[0].equalsIgnoreCase("ca")) {
4223                                             isCA = Boolean.parseBoolean(nv[1]);
4224                                         } else if (nv[0].equalsIgnoreCase("pathlen")) {
4225                                             pathLen = Integer.parseInt(nv[1]);
4226                                         } else {
4227                                             throw new Exception(rb.getString
4228                                                 ("Illegal.value.") + extstr);
4229                                         }
4230                                     }
4231                                 }
4232                             }
4233                         }
4234                         setExt(result, new BasicConstraintsExtension(isCritical, isCA,
4235                                 pathLen));
4236                         break;
4237                     case 1:     // KU
4238                         if(value != null) {
4239                             boolean[] ok = new boolean[9];
4240                             for (String s: value.split(",")) {
4241                                 int p = oneOf(s,
4242                                        "digitalSignature",  // (0),
4243                                        "nonRepudiation",    // (1)
4244                                        "keyEncipherment",   // (2),
4245                                        "dataEncipherment",  // (3),
4246                                        "keyAgreement",      // (4),
4247                                        "keyCertSign",       // (5),
4248                                        "cRLSign",           // (6),
4249                                        "encipherOnly",      // (7),
4250                                        "decipherOnly",      // (8)
4251                                        "contentCommitment"  // also (1)
4252                                        );
4253                                 if (p < 0) {
4254                                     throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);
4255                                 }
4256                                 if (p == 9) p = 1;
4257                                 ok[p] = true;
4258                             }
4259                             KeyUsageExtension kue = new KeyUsageExtension(ok);
4260                             // The above KeyUsageExtension constructor does not
4261                             // allow isCritical value, so...
4262                             setExt(result, Extension.newExtension(
4263                                     kue.getExtensionId(),
4264                                     isCritical,
4265                                     kue.getExtensionValue()));
4266                         } else {
4267                             throw new Exception(rb.getString
4268                                     ("Illegal.value.") + extstr);
4269                         }
4270                         break;
4271                     case 2:     // EKU
4272                         if(value != null) {
4273                             Vector<ObjectIdentifier> v = new Vector<>();
4274                             for (String s: value.split(",")) {
4275                                 int p = oneOf(s,
4276                                         "anyExtendedKeyUsage",
4277                                         "serverAuth",       //1
4278                                         "clientAuth",       //2
4279                                         "codeSigning",      //3
4280                                         "emailProtection",  //4
4281                                         "",                 //5
4282                                         "",                 //6
4283                                         "",                 //7
4284                                         "timeStamping",     //8
4285                                         "OCSPSigning"       //9
4286                                        );
4287                                 if (p < 0) {
4288                                     try {
4289                                         v.add(new ObjectIdentifier(s));
4290                                     } catch (Exception e) {
4291                                         throw new Exception(rb.getString(
4292                                                 "Unknown.extendedkeyUsage.type.") + s);
4293                                     }
4294                                 } else if (p == 0) {
4295                                     v.add(new ObjectIdentifier("2.5.29.37.0"));
4296                                 } else {
4297                                     v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));
4298                                 }
4299                             }
4300                             setExt(result, new ExtendedKeyUsageExtension(isCritical, v));
4301                         } else {
4302                             throw new Exception(rb.getString
4303                                     ("Illegal.value.") + extstr);
4304                         }
4305                         break;
4306                     case 3:     // SAN
4307                     case 4:     // IAN
4308                         if(value != null) {
4309                             String[] ps = value.split(",");
4310                             GeneralNames gnames = new GeneralNames();
4311                             for(String item: ps) {
4312                                 colonpos = item.indexOf(':');
4313                                 if (colonpos < 0) {
4314                                     throw new Exception("Illegal item " + item + " in " + extstr);
4315                                 }
4316                                 String t = item.substring(0, colonpos);
4317                                 String v = item.substring(colonpos+1);
4318                                 gnames.add(createGeneralName(t, v));
4319                             }
4320                             if (exttype == 3) {
4321                                 setExt(result, new SubjectAlternativeNameExtension(
4322                                         isCritical, gnames));
4323                             } else {
4324                                 setExt(result, new IssuerAlternativeNameExtension(
4325                                         isCritical, gnames));
4326                             }
4327                         } else {
4328                             throw new Exception(rb.getString
4329                                     ("Illegal.value.") + extstr);
4330                         }
4331                         break;
4332                     case 5:     // SIA, always non-critical
4333                     case 6:     // AIA, always non-critical
4334                         if (isCritical) {
4335                             throw new Exception(rb.getString(
4336                                     "This.extension.cannot.be.marked.as.critical.") + extstr);
4337                         }
4338                         if(value != null) {
4339                             List<AccessDescription> accessDescriptions =
4340                                     new ArrayList<>();
4341                             String[] ps = value.split(",");
4342                             for(String item: ps) {
4343                                 colonpos = item.indexOf(':');
4344                                 int colonpos2 = item.indexOf(':', colonpos+1);
4345                                 if (colonpos < 0 || colonpos2 < 0) {
4346                                     throw new Exception(rb.getString
4347                                             ("Illegal.value.") + extstr);
4348                                 }
4349                                 String m = item.substring(0, colonpos);
4350                                 String t = item.substring(colonpos+1, colonpos2);
4351                                 String v = item.substring(colonpos2+1);
4352                                 int p = oneOf(m,
4353                                         "",
4354                                         "ocsp",         //1
4355                                         "caIssuers",    //2
4356                                         "timeStamping", //3
4357                                         "",
4358                                         "caRepository"  //5
4359                                         );
4360                                 ObjectIdentifier oid;
4361                                 if (p < 0) {
4362                                     try {
4363                                         oid = new ObjectIdentifier(m);
4364                                     } catch (Exception e) {
4365                                         throw new Exception(rb.getString(
4366                                                 "Unknown.AccessDescription.type.") + m);
4367                                     }
4368                                 } else {
4369                                     oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
4370                                 }
4371                                 accessDescriptions.add(new AccessDescription(
4372                                         oid, createGeneralName(t, v)));
4373                             }
4374                             if (exttype == 5) {
4375                                 setExt(result, new SubjectInfoAccessExtension(accessDescriptions));
4376                             } else {
4377                                 setExt(result, new AuthorityInfoAccessExtension(accessDescriptions));
4378                             }
4379                         } else {
4380                             throw new Exception(rb.getString
4381                                     ("Illegal.value.") + extstr);
4382                         }
4383                         break;
4384                     case 8: // CRL, experimental, only support 1 distributionpoint
4385                         if(value != null) {
4386                             String[] ps = value.split(",");
4387                             GeneralNames gnames = new GeneralNames();
4388                             for(String item: ps) {
4389                                 colonpos = item.indexOf(':');
4390                                 if (colonpos < 0) {
4391                                     throw new Exception("Illegal item " + item + " in " + extstr);
4392                                 }
4393                                 String t = item.substring(0, colonpos);
4394                                 String v = item.substring(colonpos+1);
4395                                 gnames.add(createGeneralName(t, v));
4396                             }
4397                             setExt(result, new CRLDistributionPointsExtension(
4398                                     isCritical, Collections.singletonList(
4399                                     new DistributionPoint(gnames, null, null))));
4400                         } else {
4401                             throw new Exception(rb.getString
4402                                     ("Illegal.value.") + extstr);
4403                         }
4404                         break;
4405                     case -1:
4406                         ObjectIdentifier oid = new ObjectIdentifier(name);
4407                         byte[] data = null;
4408                         if (value != null) {
4409                             data = new byte[value.length() / 2 + 1];
4410                             int pos = 0;
4411                             for (char c: value.toCharArray()) {
4412                                 int hex;
4413                                 if (c >= '0' && c <= '9') {
4414                                     hex = c - '0' ;
4415                                 } else if (c >= 'A' && c <= 'F') {
4416                                     hex = c - 'A' + 10;
4417                                 } else if (c >= 'a' && c <= 'f') {
4418                                     hex = c - 'a' + 10;
4419                                 } else {
4420                                     continue;
4421                                 }
4422                                 if (pos % 2 == 0) {
4423                                     data[pos/2] = (byte)(hex << 4);
4424                                 } else {
4425                                     data[pos/2] += hex;
4426                                 }
4427                                 pos++;
4428                             }
4429                             if (pos % 2 != 0) {
4430                                 throw new Exception(rb.getString(
4431                                         "Odd.number.of.hex.digits.found.") + extstr);
4432                             }
4433                             data = Arrays.copyOf(data, pos/2);
4434                         } else {
4435                             data = new byte[0];
4436                         }
4437                         setExt(result, new Extension(oid, isCritical,
4438                                 new DerValue(DerValue.tag_OctetString, data)
4439                                         .toByteArray()));
4440                         break;
4441                     default:
4442                         throw new Exception(rb.getString(
4443                                 "Unknown.extension.type.") + extstr);
4444                 }
4445             }
4446             // always non-critical
4447             setExt(result, new SubjectKeyIdentifierExtension(
4448                     new KeyIdentifier(pkey).getIdentifier()));
4449             if (akey != null && !pkey.equals(akey)) {
4450                 setExt(result, new AuthorityKeyIdentifierExtension(
4451                                 new KeyIdentifier(akey), null, null));
4452             }
4453         } catch(IOException e) {
4454             throw new RuntimeException(e);
4455         }
4456         return result;
4457     }
4458 
4459     private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
4460         if (caks != null && caks.getCertificateAlias(cert) != null) {
4461             return true;
4462         } else {
4463             String inKS = keyStore.getCertificateAlias(cert);
4464             return inKS != null && keyStore.isCertificateEntry(inKS);
4465         }
4466     }
4467 
4468     private void checkWeak(String label, String sigAlg, Key key) {
4469 
4470         if (sigAlg != null && !DISABLED_CHECK.permits(
4471                 SIG_PRIMITIVE_SET, sigAlg, null)) {
4472             weakWarnings.add(String.format(
4473                     rb.getString("whose.sigalg.risk"), label, sigAlg));
4474         }
4475         if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4476             weakWarnings.add(String.format(
4477                     rb.getString("whose.key.risk"),
4478                     label,
4479                     String.format(rb.getString("key.bit"),
4480                             KeyUtil.getKeySize(key), key.getAlgorithm())));
4481         }
4482     }
4483 
4484     private void checkWeak(String label, Certificate[] certs)
4485             throws KeyStoreException {
4486         for (int i = 0; i < certs.length; i++) {
4487             Certificate cert = certs[i];
4488             if (cert instanceof X509Certificate) {
4489                 X509Certificate xc = (X509Certificate)cert;
4490                 String fullLabel = label;
4491                 if (certs.length > 1) {
4492                     fullLabel = oneInMany(label, i, certs.length);
4493                 }
4494                 checkWeak(fullLabel, xc);
4495             }
4496         }
4497     }
4498 
4499     private void checkWeak(String label, Certificate cert)
4500             throws KeyStoreException {
4501         if (cert instanceof X509Certificate) {
4502             X509Certificate xc = (X509Certificate)cert;
4503             // No need to check the sigalg of a trust anchor
4504             String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
4505             checkWeak(label, sigAlg, xc.getPublicKey());
4506         }
4507     }
4508 
4509     private void checkWeak(String label, PKCS10 p10) {
4510         checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
4511     }
4512 
4513     private void checkWeak(String label, CRL crl, Key key) {
4514         if (crl instanceof X509CRLImpl) {
4515             X509CRLImpl impl = (X509CRLImpl)crl;
4516             checkWeak(label, impl.getSigAlgName(), key);
4517         }
4518     }
4519 
4520     private void printWeakWarnings(boolean newLine) {
4521         if (!weakWarnings.isEmpty() && !nowarn) {
4522             System.err.println("\nWarning:");
4523             for (String warning : weakWarnings) {
4524                 System.err.println(warning);
4525             }
4526             if (newLine) {
4527                 // When calling before a yes/no prompt, add a new line
4528                 System.err.println();
4529             }
4530         }
4531         weakWarnings.clear();
4532     }
4533 
4534     /**
4535      * Prints the usage of this tool.
4536      */
4537     private void usage() {
4538         if (command != null) {
4539             System.err.println("keytool " + command +
4540                     rb.getString(".OPTION."));
4541             System.err.println();
4542             System.err.println(rb.getString(command.description));
4543             System.err.println();
4544             System.err.println(rb.getString("Options."));
4545             System.err.println();
4546 
4547             // Left and right sides of the options list. Both might
4548             // contain "\n" and span multiple lines
4549             String[] left = new String[command.options.length];
4550             String[] right = new String[command.options.length];
4551 
4552             // Length of left side of options list
4553             int lenLeft = 0;
4554 
4555             for (int j = 0; j < command.options.length; j++) {
4556                 Option opt = command.options[j];
4557                 left[j] = opt.toString();
4558                 if (opt.arg != null) {
4559                     left[j] += " " + opt.arg;
4560                 }
4561                 String[] lefts = left[j].split("\n");
4562                 for (String s : lefts) {
4563                     if (s.length() > lenLeft) {
4564                         lenLeft = s.length();
4565                     }
4566                 }
4567                 right[j] = rb.getString(opt.description);
4568             }
4569             for (int j = 0; j < left.length; j++) {
4570                 String[] lefts = left[j].split("\n");
4571                 String[] rights = right[j].split("\n");
4572                 for (int i = 0; i < lefts.length && i < rights.length; i++) {
4573                     String s1 = i < lefts.length ? lefts[i] : "";
4574                     String s2 = i < rights.length ? rights[i] : "";
4575                     if (i == 0) {
4576                         System.err.printf(" %-" + lenLeft + "s  %s\n", s1, s2);
4577                     } else {
4578                         System.err.printf("   %-" + lenLeft + "s  %s\n", s1, s2);
4579                     }
4580                 }
4581             }
4582             System.err.println();
4583             System.err.println(rb.getString(
4584                     "Use.keytool.help.for.all.available.commands"));
4585         } else {
4586             System.err.println(rb.getString(
4587                     "Key.and.Certificate.Management.Tool"));
4588             System.err.println();
4589             System.err.println(rb.getString("Commands."));
4590             System.err.println();
4591             for (Command c: Command.values()) {
4592                 if (c == KEYCLONE) break;
4593                 System.err.printf(" %-20s%s\n", c, rb.getString(c.description));
4594             }
4595             System.err.println();
4596             System.err.println(rb.getString(
4597                     "Use.keytool.command.name.help.for.usage.of.command.name"));
4598         }
4599     }
4600 
4601     private void tinyHelp() {
4602         usage();
4603         if (debug) {
4604             throw new RuntimeException("NO BIG ERROR, SORRY");
4605         } else {
4606             System.exit(1);
4607         }
4608     }
4609 
4610     private void errorNeedArgument(String flag) {
4611         Object[] source = {flag};
4612         System.err.println(new MessageFormat(
4613                 rb.getString("Command.option.flag.needs.an.argument.")).format(source));
4614         tinyHelp();
4615     }
4616 
4617     private char[] getPass(String modifier, String arg) {
4618         char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
4619         if (output != null) return output;
4620         tinyHelp();
4621         return null;    // Useless, tinyHelp() already exits.
4622     }
4623 }
4624 
4625 // This class is exactly the same as com.sun.tools.javac.util.Pair,
4626 // it's copied here since the original one is not included in JRE.
4627 class Pair<A, B> {
4628 
4629     public final A fst;
4630     public final B snd;
4631 
4632     public Pair(A fst, B snd) {
4633         this.fst = fst;
4634         this.snd = snd;
4635     }
4636 
4637     public String toString() {
4638         return "Pair[" + fst + "," + snd + "]";
4639     }
4640 
4641     public boolean equals(Object other) {
4642         return
4643             other instanceof Pair &&
4644             Objects.equals(fst, ((Pair)other).fst) &&
4645             Objects.equals(snd, ((Pair)other).snd);
4646     }
4647 
4648     public int hashCode() {
4649         if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
4650         else if (snd == null) return fst.hashCode() + 2;
4651         else return fst.hashCode() * 17 + snd.hashCode();
4652     }
4653 
4654     public static <A,B> Pair<A,B> of(A a, B b) {
4655         return new Pair<>(a,b);
4656     }
4657 }
4658