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