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