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