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