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