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