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