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