Save This Page
Home » openjdk-7 » sun.security » tools » [javadoc | source]
    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       private static boolean equals(Object x, Object y) {
 4197           return (x == null && y == null) || (x != null && x.equals(y));
 4198       }
 4199   
 4200       public boolean equals(Object other) {
 4201           return
 4202               other instanceof Pair &&
 4203               equals(fst, ((Pair)other).fst) &&
 4204               equals(snd, ((Pair)other).snd);
 4205       }
 4206   
 4207       public int hashCode() {
 4208           if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
 4209           else if (snd == null) return fst.hashCode() + 2;
 4210           else return fst.hashCode() * 17 + snd.hashCode();
 4211       }
 4212   
 4213       public static <A,B> Pair<A,B> of(A a, B b) {
 4214           return new Pair<>(a,b);
 4215       }
 4216   }
 4217   

Save This Page
Home » openjdk-7 » sun.security » tools » [javadoc | source]