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