jdk/src/share/classes/sun/security/tools/KeyTool.java
changeset 2 90ce3da70b43
child 904 eadc9fa4b700
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1997-2006 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.security.tools;
       
    27 
       
    28 import java.io.*;
       
    29 import java.math.BigInteger;
       
    30 import java.security.GeneralSecurityException;
       
    31 import java.security.InvalidParameterException;
       
    32 import java.security.KeyStore;
       
    33 import java.security.KeyStoreException;
       
    34 import java.security.MessageDigest;
       
    35 import java.security.NoSuchAlgorithmException;
       
    36 import java.security.Key;
       
    37 import java.security.PublicKey;
       
    38 import java.security.PrivateKey;
       
    39 import java.security.Security;
       
    40 import java.security.Signature;
       
    41 import java.security.SignatureException;
       
    42 import java.security.UnrecoverableEntryException;
       
    43 import java.security.UnrecoverableKeyException;
       
    44 import java.security.Principal;
       
    45 import java.security.Provider;
       
    46 import java.security.Identity;
       
    47 import java.security.Signer;
       
    48 import java.security.cert.Certificate;
       
    49 import java.security.cert.CertificateFactory;
       
    50 import java.security.cert.X509Certificate;
       
    51 import java.security.cert.CertificateException;
       
    52 import java.security.interfaces.DSAParams;
       
    53 import java.security.interfaces.DSAPrivateKey;
       
    54 import java.security.interfaces.DSAPublicKey;
       
    55 import java.security.interfaces.RSAPrivateCrtKey;
       
    56 import java.security.interfaces.RSAPrivateKey;
       
    57 import java.security.interfaces.RSAPublicKey;
       
    58 import java.text.Collator;
       
    59 import java.text.MessageFormat;
       
    60 import java.util.*;
       
    61 import java.lang.reflect.Constructor;
       
    62 import java.net.URL;
       
    63 import java.net.URLClassLoader;
       
    64 
       
    65 import sun.misc.BASE64Decoder;
       
    66 import sun.misc.BASE64Encoder;
       
    67 import sun.security.util.ObjectIdentifier;
       
    68 import sun.security.pkcs.PKCS10;
       
    69 import sun.security.provider.IdentityDatabase;
       
    70 import sun.security.provider.SystemSigner;
       
    71 import sun.security.provider.SystemIdentity;
       
    72 import sun.security.provider.X509Factory;
       
    73 import sun.security.util.DerOutputStream;
       
    74 import sun.security.util.Password;
       
    75 import sun.security.util.Resources;
       
    76 import sun.security.util.PathList;
       
    77 import javax.crypto.KeyGenerator;
       
    78 import javax.crypto.SecretKey;
       
    79 
       
    80 import sun.security.x509.*;
       
    81 
       
    82 import static java.security.KeyStore.*;
       
    83 
       
    84 /**
       
    85  * This tool manages keystores.
       
    86  *
       
    87  * @author Jan Luehe
       
    88  *
       
    89  *
       
    90  * @see java.security.KeyStore
       
    91  * @see sun.security.provider.KeyProtector
       
    92  * @see sun.security.provider.JavaKeyStore
       
    93  *
       
    94  * @since 1.2
       
    95  */
       
    96 
       
    97 public final class KeyTool {
       
    98 
       
    99     private boolean debug = false;
       
   100     private int command = -1;
       
   101     private String sigAlgName = null;
       
   102     private String keyAlgName = null;
       
   103     private boolean verbose = false;
       
   104     private int keysize = -1;
       
   105     private boolean rfc = false;
       
   106     private long validity = (long)90;
       
   107     private String alias = null;
       
   108     private String dname = null;
       
   109     private String dest = null;
       
   110     private String filename = null;
       
   111     private String srcksfname = null;
       
   112 
       
   113     // User-specified providers are added before any command is called.
       
   114     // However, they are not removed before the end of the main() method.
       
   115     // If you're calling KeyTool.main() directly in your own Java program,
       
   116     // please programtically add any providers you need and do not specify
       
   117     // them through the command line.
       
   118 
       
   119     private Set<Pair <String, String>> providers = null;
       
   120     private String storetype = null;
       
   121     private String srcProviderName = null;
       
   122     private String providerName = null;
       
   123     private String pathlist = null;
       
   124     private char[] storePass = null;
       
   125     private char[] storePassNew = null;
       
   126     private char[] keyPass = null;
       
   127     private char[] keyPassNew = null;
       
   128     private char[] oldPass = null;
       
   129     private char[] newPass = null;
       
   130     private char[] destKeyPass = null;
       
   131     private char[] srckeyPass = null;
       
   132     private String ksfname = null;
       
   133     private File ksfile = null;
       
   134     private InputStream ksStream = null; // keystore stream
       
   135     private KeyStore keyStore = null;
       
   136     private boolean token = false;
       
   137     private boolean nullStream = false;
       
   138     private boolean kssave = false;
       
   139     private boolean noprompt = false;
       
   140     private boolean trustcacerts = false;
       
   141     private boolean protectedPath = false;
       
   142     private boolean srcprotectedPath = false;
       
   143     private CertificateFactory cf = null;
       
   144     private KeyStore caks = null; // "cacerts" keystore
       
   145     private char[] srcstorePass = null;
       
   146     private String srcstoretype = null;
       
   147     private Set<char[]> passwords = new HashSet<char[]> ();
       
   148     private String startDate = null;
       
   149 
       
   150     private static final int CERTREQ = 1;
       
   151     private static final int CHANGEALIAS = 2;
       
   152     private static final int DELETE = 3;
       
   153     private static final int EXPORTCERT = 4;
       
   154     private static final int GENKEYPAIR = 5;
       
   155     private static final int GENSECKEY = 6;
       
   156     // there is no HELP
       
   157     private static final int IDENTITYDB = 7;
       
   158     private static final int IMPORTCERT = 8;
       
   159     private static final int IMPORTKEYSTORE = 9;
       
   160     private static final int KEYCLONE = 10;
       
   161     private static final int KEYPASSWD = 11;
       
   162     private static final int LIST = 12;
       
   163     private static final int PRINTCERT = 13;
       
   164     private static final int SELFCERT = 14;
       
   165     private static final int STOREPASSWD = 15;
       
   166 
       
   167     private static final Class[] PARAM_STRING = { String.class };
       
   168 
       
   169     private static final String JKS = "jks";
       
   170     private static final String NONE = "NONE";
       
   171     private static final String P11KEYSTORE = "PKCS11";
       
   172     private static final String P12KEYSTORE = "PKCS12";
       
   173     private final String keyAlias = "mykey";
       
   174 
       
   175     // for i18n
       
   176     private static final java.util.ResourceBundle rb =
       
   177         java.util.ResourceBundle.getBundle("sun.security.util.Resources");
       
   178     private static final Collator collator = Collator.getInstance();
       
   179     static {
       
   180         // this is for case insensitive string comparisons
       
   181         collator.setStrength(Collator.PRIMARY);
       
   182     };
       
   183 
       
   184     private KeyTool() { }
       
   185 
       
   186     public static void main(String[] args) throws Exception {
       
   187         KeyTool kt = new KeyTool();
       
   188         kt.run(args, System.out);
       
   189     }
       
   190 
       
   191     private void run(String[] args, PrintStream out) throws Exception {
       
   192         try {
       
   193             parseArgs(args);
       
   194             doCommands(out);
       
   195         } catch (Exception e) {
       
   196             System.out.println(rb.getString("keytool error: ") + e);
       
   197             if (verbose) {
       
   198                 e.printStackTrace(System.out);
       
   199             }
       
   200             if (!debug) {
       
   201                 System.exit(1);
       
   202             } else {
       
   203                 throw e;
       
   204             }
       
   205         } finally {
       
   206             for (char[] pass : passwords) {
       
   207                 if (pass != null) {
       
   208                     Arrays.fill(pass, ' ');
       
   209                     pass = null;
       
   210                 }
       
   211             }
       
   212 
       
   213             if (ksStream != null) {
       
   214                 ksStream.close();
       
   215             }
       
   216         }
       
   217     }
       
   218 
       
   219     /**
       
   220      * Parse command line arguments.
       
   221      */
       
   222     void parseArgs(String[] args) {
       
   223 
       
   224         if (args.length == 0) usage();
       
   225 
       
   226         int i=0;
       
   227 
       
   228         for (i=0; (i < args.length) && args[i].startsWith("-"); i++) {
       
   229 
       
   230             String flags = args[i];
       
   231             /*
       
   232              * command modes
       
   233              */
       
   234             if (collator.compare(flags, "-certreq") == 0) {
       
   235                 command = CERTREQ;
       
   236             } else if (collator.compare(flags, "-delete") == 0) {
       
   237                 command = DELETE;
       
   238             } else if (collator.compare(flags, "-export") == 0 ||
       
   239                     collator.compare(flags, "-exportcert") == 0) {
       
   240                 command = EXPORTCERT;
       
   241             } else if (collator.compare(flags, "-genkey") == 0 ||
       
   242                     collator.compare(flags, "-genkeypair") == 0) {
       
   243                 command = GENKEYPAIR;
       
   244             } else if (collator.compare(flags, "-help") == 0) {
       
   245                 usage();
       
   246                 return;
       
   247             } else if (collator.compare(flags, "-identitydb") == 0) { // obsolete
       
   248                 command = IDENTITYDB;
       
   249             } else if (collator.compare(flags, "-import") == 0 ||
       
   250                     collator.compare(flags, "-importcert") == 0) {
       
   251                 command = IMPORTCERT;
       
   252             } else if (collator.compare(flags, "-keyclone") == 0) { // obsolete
       
   253                 command = KEYCLONE;
       
   254             } else if (collator.compare(flags, "-changealias") == 0) {
       
   255                 command = CHANGEALIAS;
       
   256             } else if (collator.compare(flags, "-keypasswd") == 0) {
       
   257                 command = KEYPASSWD;
       
   258             } else if (collator.compare(flags, "-list") == 0) {
       
   259                 command = LIST;
       
   260             } else if (collator.compare(flags, "-printcert") == 0) {
       
   261                 command = PRINTCERT;
       
   262             } else if (collator.compare(flags, "-selfcert") == 0) {     // obsolete
       
   263                 command = SELFCERT;
       
   264             } else if (collator.compare(flags, "-storepasswd") == 0) {
       
   265                 command = STOREPASSWD;
       
   266             } else if (collator.compare(flags, "-importkeystore") == 0) {
       
   267                 command = IMPORTKEYSTORE;
       
   268             } else if (collator.compare(flags, "-genseckey") == 0) {
       
   269                 command = GENSECKEY;
       
   270             }
       
   271 
       
   272             /*
       
   273              * specifiers
       
   274              */
       
   275             else if (collator.compare(flags, "-keystore") == 0 ||
       
   276                     collator.compare(flags, "-destkeystore") == 0) {
       
   277                 if (++i == args.length) errorNeedArgument(flags);
       
   278                 ksfname = args[i];
       
   279             } else if (collator.compare(flags, "-storepass") == 0 ||
       
   280                     collator.compare(flags, "-deststorepass") == 0) {
       
   281                 if (++i == args.length) errorNeedArgument(flags);
       
   282                 storePass = args[i].toCharArray();
       
   283                 passwords.add(storePass);
       
   284             } else if (collator.compare(flags, "-storetype") == 0 ||
       
   285                     collator.compare(flags, "-deststoretype") == 0) {
       
   286                 if (++i == args.length) errorNeedArgument(flags);
       
   287                 storetype = args[i];
       
   288             } else if (collator.compare(flags, "-srcstorepass") == 0) {
       
   289                 if (++i == args.length) errorNeedArgument(flags);
       
   290                 srcstorePass = args[i].toCharArray();
       
   291                 passwords.add(srcstorePass);
       
   292             } else if (collator.compare(flags, "-srcstoretype") == 0) {
       
   293                 if (++i == args.length) errorNeedArgument(flags);
       
   294                 srcstoretype = args[i];
       
   295             } else if (collator.compare(flags, "-srckeypass") == 0) {
       
   296                 if (++i == args.length) errorNeedArgument(flags);
       
   297                 srckeyPass = args[i].toCharArray();
       
   298                 passwords.add(srckeyPass);
       
   299             } else if (collator.compare(flags, "-srcprovidername") == 0) {
       
   300                 if (++i == args.length) errorNeedArgument(flags);
       
   301                 srcProviderName = args[i];
       
   302             } else if (collator.compare(flags, "-providername") == 0 ||
       
   303                     collator.compare(flags, "-destprovidername") == 0) {
       
   304                 if (++i == args.length) errorNeedArgument(flags);
       
   305                 providerName = args[i];
       
   306             } else if (collator.compare(flags, "-providerpath") == 0) {
       
   307                 if (++i == args.length) errorNeedArgument(flags);
       
   308                 pathlist = args[i];
       
   309             } else if (collator.compare(flags, "-keypass") == 0) {
       
   310                 if (++i == args.length) errorNeedArgument(flags);
       
   311                 keyPass = args[i].toCharArray();
       
   312                 passwords.add(keyPass);
       
   313             } else if (collator.compare(flags, "-new") == 0) {
       
   314                 if (++i == args.length) errorNeedArgument(flags);
       
   315                 newPass = args[i].toCharArray();
       
   316                 passwords.add(newPass);
       
   317             } else if (collator.compare(flags, "-destkeypass") == 0) {
       
   318                 if (++i == args.length) errorNeedArgument(flags);
       
   319                 destKeyPass = args[i].toCharArray();
       
   320                 passwords.add(destKeyPass);
       
   321             } else if (collator.compare(flags, "-alias") == 0 ||
       
   322                     collator.compare(flags, "-srcalias") == 0) {
       
   323                 if (++i == args.length) errorNeedArgument(flags);
       
   324                 alias = args[i];
       
   325             } else if (collator.compare(flags, "-dest") == 0 ||
       
   326                     collator.compare(flags, "-destalias") == 0) {
       
   327                 if (++i == args.length) errorNeedArgument(flags);
       
   328                 dest = args[i];
       
   329             } else if (collator.compare(flags, "-dname") == 0) {
       
   330                 if (++i == args.length) errorNeedArgument(flags);
       
   331                 dname = args[i];
       
   332             } else if (collator.compare(flags, "-keysize") == 0) {
       
   333                 if (++i == args.length) errorNeedArgument(flags);
       
   334                 keysize = Integer.parseInt(args[i]);
       
   335             } else if (collator.compare(flags, "-keyalg") == 0) {
       
   336                 if (++i == args.length) errorNeedArgument(flags);
       
   337                 keyAlgName = args[i];
       
   338             } else if (collator.compare(flags, "-sigalg") == 0) {
       
   339                 if (++i == args.length) errorNeedArgument(flags);
       
   340                 sigAlgName = args[i];
       
   341             } else if (collator.compare(flags, "-startdate") == 0) {
       
   342                 if (++i == args.length) errorNeedArgument(flags);
       
   343                 startDate = args[i];
       
   344             } else if (collator.compare(flags, "-validity") == 0) {
       
   345                 if (++i == args.length) errorNeedArgument(flags);
       
   346                 validity = Long.parseLong(args[i]);
       
   347             } else if (collator.compare(flags, "-file") == 0) {
       
   348                 if (++i == args.length) errorNeedArgument(flags);
       
   349                 filename = args[i];
       
   350             } else if (collator.compare(flags, "-srckeystore") == 0) {
       
   351                 if (++i == args.length) errorNeedArgument(flags);
       
   352                 srcksfname = args[i];
       
   353             } else if ((collator.compare(flags, "-provider") == 0) ||
       
   354                         (collator.compare(flags, "-providerclass") == 0)) {
       
   355                 if (++i == args.length) errorNeedArgument(flags);
       
   356                 if (providers == null) {
       
   357                     providers = new HashSet<Pair <String, String>> (3);
       
   358                 }
       
   359                 String providerClass = args[i];
       
   360                 String providerArg = null;
       
   361 
       
   362                 if (args.length > (i+1)) {
       
   363                     flags = args[i+1];
       
   364                     if (collator.compare(flags, "-providerarg") == 0) {
       
   365                         if (args.length == (i+2)) errorNeedArgument(flags);
       
   366                         providerArg = args[i+2];
       
   367                         i += 2;
       
   368                     }
       
   369                 }
       
   370                 providers.add(
       
   371                         new Pair<String, String>(providerClass, providerArg));
       
   372             }
       
   373 
       
   374             /*
       
   375              * options
       
   376              */
       
   377             else if (collator.compare(flags, "-v") == 0) {
       
   378                 verbose = true;
       
   379             } else if (collator.compare(flags, "-debug") == 0) {
       
   380                 debug = true;
       
   381             } else if (collator.compare(flags, "-rfc") == 0) {
       
   382                 rfc = true;
       
   383             } else if (collator.compare(flags, "-noprompt") == 0) {
       
   384                 noprompt = true;
       
   385             } else if (collator.compare(flags, "-trustcacerts") == 0) {
       
   386                 trustcacerts = true;
       
   387             } else if (collator.compare(flags, "-protected") == 0 ||
       
   388                     collator.compare(flags, "-destprotected") == 0) {
       
   389                 protectedPath = true;
       
   390             } else if (collator.compare(flags, "-srcprotected") == 0) {
       
   391                 srcprotectedPath = true;
       
   392             } else  {
       
   393                 System.err.println(rb.getString("Illegal option:  ") + flags);
       
   394                 tinyHelp();
       
   395             }
       
   396         }
       
   397 
       
   398         if (i<args.length) {
       
   399             MessageFormat form = new MessageFormat
       
   400                 (rb.getString("Usage error, <arg> is not a legal command"));
       
   401             Object[] source = {args[i]};
       
   402             throw new RuntimeException(form.format(source));
       
   403         }
       
   404 
       
   405         if (command == -1) {
       
   406             System.err.println(rb.getString("Usage error: no command provided"));
       
   407             tinyHelp();
       
   408         }
       
   409     }
       
   410 
       
   411     /**
       
   412      * Execute the commands.
       
   413      */
       
   414     void doCommands(PrintStream out) throws Exception {
       
   415 
       
   416         if (storetype == null) {
       
   417             storetype = KeyStore.getDefaultType();
       
   418         }
       
   419         storetype = KeyStoreUtil.niceStoreTypeName(storetype);
       
   420 
       
   421         if (srcstoretype == null) {
       
   422             srcstoretype = KeyStore.getDefaultType();
       
   423         }
       
   424         srcstoretype = KeyStoreUtil.niceStoreTypeName(srcstoretype);
       
   425 
       
   426         if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
       
   427                 KeyStoreUtil.isWindowsKeyStore(storetype)) {
       
   428             token = true;
       
   429             if (ksfname == null) {
       
   430                 ksfname = NONE;
       
   431             }
       
   432         }
       
   433         if (NONE.equals(ksfname)) {
       
   434             nullStream = true;
       
   435         }
       
   436 
       
   437         if (token && !nullStream) {
       
   438             System.err.println(MessageFormat.format(rb.getString
       
   439                 ("-keystore must be NONE if -storetype is {0}"), storetype));
       
   440             System.err.println();
       
   441             tinyHelp();
       
   442         }
       
   443 
       
   444         if (token &&
       
   445             (command == KEYPASSWD || command == STOREPASSWD)) {
       
   446             throw new UnsupportedOperationException(MessageFormat.format(rb.getString
       
   447                         ("-storepasswd and -keypasswd commands not supported " +
       
   448                         "if -storetype is {0}"), storetype));
       
   449         }
       
   450 
       
   451         if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {
       
   452             throw new UnsupportedOperationException(rb.getString
       
   453                         ("-keypasswd commands not supported " +
       
   454                         "if -storetype is PKCS12"));
       
   455         }
       
   456 
       
   457         if (token && (keyPass != null || newPass != null || destKeyPass != null)) {
       
   458             throw new IllegalArgumentException(MessageFormat.format(rb.getString
       
   459                 ("-keypass and -new " +
       
   460                 "can not be specified if -storetype is {0}"), storetype));
       
   461         }
       
   462 
       
   463         if (protectedPath) {
       
   464             if (storePass != null || keyPass != null ||
       
   465                     newPass != null || destKeyPass != null) {
       
   466                 throw new IllegalArgumentException(rb.getString
       
   467                         ("if -protected is specified, " +
       
   468                         "then -storepass, -keypass, and -new " +
       
   469                         "must not be specified"));
       
   470             }
       
   471         }
       
   472 
       
   473         if (srcprotectedPath) {
       
   474             if (srcstorePass != null || srckeyPass != null) {
       
   475                 throw new IllegalArgumentException(rb.getString
       
   476                         ("if -srcprotected is specified, " +
       
   477                         "then -srcstorepass and -srckeypass " +
       
   478                         "must not be specified"));
       
   479             }
       
   480         }
       
   481 
       
   482         if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
       
   483             if (storePass != null || keyPass != null ||
       
   484                     newPass != null || destKeyPass != null) {
       
   485                 throw new IllegalArgumentException(rb.getString
       
   486                         ("if keystore is not password protected, " +
       
   487                         "then -storepass, -keypass, and -new " +
       
   488                         "must not be specified"));
       
   489             }
       
   490         }
       
   491 
       
   492         if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
       
   493             if (srcstorePass != null || srckeyPass != null) {
       
   494                 throw new IllegalArgumentException(rb.getString
       
   495                         ("if source keystore is not password protected, " +
       
   496                         "then -srcstorepass and -srckeypass " +
       
   497                         "must not be specified"));
       
   498             }
       
   499         }
       
   500 
       
   501         if (validity <= (long)0) {
       
   502             throw new Exception
       
   503                 (rb.getString("Validity must be greater than zero"));
       
   504         }
       
   505 
       
   506         // Try to load and install specified provider
       
   507         if (providers != null) {
       
   508             ClassLoader cl = null;
       
   509             if (pathlist != null) {
       
   510                 String path = null;
       
   511                 path = PathList.appendPath(
       
   512                         path, System.getProperty("java.class.path"));
       
   513                 path = PathList.appendPath(
       
   514                         path, System.getProperty("env.class.path"));
       
   515                 path = PathList.appendPath(path, pathlist);
       
   516 
       
   517                 URL[] urls = PathList.pathToURLs(path);
       
   518                 cl = new URLClassLoader(urls);
       
   519             } else {
       
   520                 cl = ClassLoader.getSystemClassLoader();
       
   521             }
       
   522 
       
   523             for (Pair <String, String> provider: providers) {
       
   524                 String provName = provider.fst;
       
   525                 Class<?> provClass;
       
   526                 if (cl != null) {
       
   527                     provClass = cl.loadClass(provName);
       
   528                 } else {
       
   529                     provClass = Class.forName(provName);
       
   530                 }
       
   531 
       
   532                 String provArg = provider.snd;
       
   533                 Object obj;
       
   534                 if (provArg == null) {
       
   535                     obj = provClass.newInstance();
       
   536                 } else {
       
   537                     Constructor<?> c = provClass.getConstructor(PARAM_STRING);
       
   538                     obj = c.newInstance(provArg);
       
   539                 }
       
   540                 if (!(obj instanceof Provider)) {
       
   541                     MessageFormat form = new MessageFormat
       
   542                         (rb.getString("provName not a provider"));
       
   543                     Object[] source = {provName};
       
   544                     throw new Exception(form.format(source));
       
   545                 }
       
   546                 Security.addProvider((Provider)obj);
       
   547             }
       
   548         }
       
   549 
       
   550         if (command == LIST && verbose && rfc) {
       
   551             System.err.println(rb.getString
       
   552                 ("Must not specify both -v and -rfc with 'list' command"));
       
   553             tinyHelp();
       
   554         }
       
   555 
       
   556         // Make sure provided passwords are at least 6 characters long
       
   557         if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) {
       
   558             throw new Exception(rb.getString
       
   559                 ("Key password must be at least 6 characters"));
       
   560         }
       
   561         if (newPass != null && newPass.length < 6) {
       
   562             throw new Exception(rb.getString
       
   563                 ("New password must be at least 6 characters"));
       
   564         }
       
   565         if (destKeyPass != null && destKeyPass.length < 6) {
       
   566             throw new Exception(rb.getString
       
   567                 ("New password must be at least 6 characters"));
       
   568         }
       
   569 
       
   570         // Check if keystore exists.
       
   571         // If no keystore has been specified at the command line, try to use
       
   572         // the default, which is located in $HOME/.keystore.
       
   573         // If the command is "genkey", "identitydb", "import", or "printcert",
       
   574         // it is OK not to have a keystore.
       
   575         if (command != PRINTCERT) {
       
   576             if (ksfname == null) {
       
   577                 ksfname = System.getProperty("user.home") + File.separator
       
   578                     + ".keystore";
       
   579             }
       
   580 
       
   581             if (!nullStream) {
       
   582                 try {
       
   583                     ksfile = new File(ksfname);
       
   584                     // Check if keystore file is empty
       
   585                     if (ksfile.exists() && ksfile.length() == 0) {
       
   586                         throw new Exception(rb.getString
       
   587                         ("Keystore file exists, but is empty: ") + ksfname);
       
   588                     }
       
   589                     ksStream = new FileInputStream(ksfile);
       
   590                 } catch (FileNotFoundException e) {
       
   591                     if (command != GENKEYPAIR &&
       
   592                         command != GENSECKEY &&
       
   593                         command != IDENTITYDB &&
       
   594                         command != IMPORTCERT &&
       
   595                         command != IMPORTKEYSTORE) {
       
   596                         throw new Exception(rb.getString
       
   597                                 ("Keystore file does not exist: ") + ksfname);
       
   598                     }
       
   599                 }
       
   600             }
       
   601         }
       
   602 
       
   603         if ((command == KEYCLONE || command == CHANGEALIAS)
       
   604                 && dest == null) {
       
   605             dest = getAlias("destination");
       
   606             if ("".equals(dest)) {
       
   607                 throw new Exception(rb.getString
       
   608                         ("Must specify destination alias"));
       
   609             }
       
   610         }
       
   611 
       
   612         if (command == DELETE && alias == null) {
       
   613             alias = getAlias(null);
       
   614             if ("".equals(alias)) {
       
   615                 throw new Exception(rb.getString("Must specify alias"));
       
   616             }
       
   617         }
       
   618 
       
   619         // Create new keystore
       
   620         if (providerName == null) {
       
   621             keyStore = KeyStore.getInstance(storetype);
       
   622         } else {
       
   623             keyStore = KeyStore.getInstance(storetype, providerName);
       
   624         }
       
   625 
       
   626         /*
       
   627          * Load the keystore data.
       
   628          *
       
   629          * At this point, it's OK if no keystore password has been provided.
       
   630          * We want to make sure that we can load the keystore data, i.e.,
       
   631          * the keystore data has the right format. If we cannot load the
       
   632          * keystore, why bother asking the user for his or her password?
       
   633          * Only if we were able to load the keystore, and no keystore
       
   634          * password has been provided, will we prompt the user for the
       
   635          * keystore password to verify the keystore integrity.
       
   636          * This means that the keystore is loaded twice: first load operation
       
   637          * checks the keystore format, second load operation verifies the
       
   638          * keystore integrity.
       
   639          *
       
   640          * If the keystore password has already been provided (at the
       
   641          * command line), however, the keystore is loaded only once, and the
       
   642          * keystore format and integrity are checked "at the same time".
       
   643          *
       
   644          * Null stream keystores are loaded later.
       
   645          */
       
   646         if (!nullStream) {
       
   647             keyStore.load(ksStream, storePass);
       
   648             if (ksStream != null) {
       
   649                 ksStream.close();
       
   650             }
       
   651         }
       
   652 
       
   653         // All commands that create or modify the keystore require a keystore
       
   654         // password.
       
   655 
       
   656         if (nullStream && storePass != null) {
       
   657             keyStore.load(null, storePass);
       
   658         } else if (!nullStream && storePass != null) {
       
   659             // If we are creating a new non nullStream-based keystore,
       
   660             // insist that the password be at least 6 characters
       
   661             if (ksStream == null && storePass.length < 6) {
       
   662                 throw new Exception(rb.getString
       
   663                         ("Keystore password must be at least 6 characters"));
       
   664             }
       
   665         } else if (storePass == null) {
       
   666 
       
   667             // only prompt if (protectedPath == false)
       
   668 
       
   669             if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) &&
       
   670                 (command == CERTREQ ||
       
   671                         command == DELETE ||
       
   672                         command == GENKEYPAIR ||
       
   673                         command == GENSECKEY ||
       
   674                         command == IMPORTCERT ||
       
   675                         command == IMPORTKEYSTORE ||
       
   676                         command == KEYCLONE ||
       
   677                         command == CHANGEALIAS ||
       
   678                         command == SELFCERT ||
       
   679                         command == STOREPASSWD ||
       
   680                         command == KEYPASSWD ||
       
   681                         command == IDENTITYDB)) {
       
   682                 int count = 0;
       
   683                 do {
       
   684                     if (command == IMPORTKEYSTORE) {
       
   685                         System.err.print
       
   686                                 (rb.getString("Enter destination keystore password:  "));
       
   687                     } else {
       
   688                         System.err.print
       
   689                                 (rb.getString("Enter keystore password:  "));
       
   690                     }
       
   691                     System.err.flush();
       
   692                     storePass = Password.readPassword(System.in);
       
   693                     passwords.add(storePass);
       
   694 
       
   695                     // If we are creating a new non nullStream-based keystore,
       
   696                     // insist that the password be at least 6 characters
       
   697                     if (!nullStream && (storePass == null || storePass.length < 6)) {
       
   698                         System.err.println(rb.getString
       
   699                                 ("Keystore password is too short - " +
       
   700                                 "must be at least 6 characters"));
       
   701                         storePass = null;
       
   702                     }
       
   703 
       
   704                     // If the keystore file does not exist and needs to be
       
   705                     // created, the storepass should be prompted twice.
       
   706                     if (storePass != null && !nullStream && ksStream == null) {
       
   707                         System.err.print(rb.getString("Re-enter new password: "));
       
   708                         char[] storePassAgain = Password.readPassword(System.in);
       
   709                         passwords.add(storePassAgain);
       
   710                         if (!Arrays.equals(storePass, storePassAgain)) {
       
   711                             System.err.println
       
   712                                 (rb.getString("They don't match. Try again"));
       
   713                             storePass = null;
       
   714                         }
       
   715                     }
       
   716 
       
   717                     count++;
       
   718                 } while ((storePass == null) && count < 3);
       
   719 
       
   720 
       
   721                 if (storePass == null) {
       
   722                     System.err.println
       
   723                         (rb.getString("Too many failures - try later"));
       
   724                     return;
       
   725                 }
       
   726             } else if (!protectedPath
       
   727                     && !KeyStoreUtil.isWindowsKeyStore(storetype)
       
   728                     && !(command == PRINTCERT)) {
       
   729                 // here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
       
   730                 System.err.print(rb.getString("Enter keystore password:  "));
       
   731                 System.err.flush();
       
   732                 storePass = Password.readPassword(System.in);
       
   733                 passwords.add(storePass);
       
   734             }
       
   735 
       
   736             // Now load a nullStream-based keystore,
       
   737             // or verify the integrity of an input stream-based keystore
       
   738             if (nullStream) {
       
   739                 keyStore.load(null, storePass);
       
   740             } else if (ksStream != null) {
       
   741                 ksStream = new FileInputStream(ksfile);
       
   742                 keyStore.load(ksStream, storePass);
       
   743                 ksStream.close();
       
   744             }
       
   745         }
       
   746 
       
   747         if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {
       
   748             MessageFormat form = new MessageFormat(rb.getString(
       
   749                 "Warning:  Different store and key passwords not supported " +
       
   750                 "for PKCS12 KeyStores. Ignoring user-specified <command> value."));
       
   751             if (keyPass != null && !Arrays.equals(storePass, keyPass)) {
       
   752                 Object[] source = {"-keypass"};
       
   753                 System.err.println(form.format(source));
       
   754                 keyPass = storePass;
       
   755             }
       
   756             if (newPass != null && !Arrays.equals(storePass, newPass)) {
       
   757                 Object[] source = {"-new"};
       
   758                 System.err.println(form.format(source));
       
   759                 newPass = storePass;
       
   760             }
       
   761             if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) {
       
   762                 Object[] source = {"-destkeypass"};
       
   763                 System.err.println(form.format(source));
       
   764                 destKeyPass = storePass;
       
   765             }
       
   766         }
       
   767 
       
   768         // Create a certificate factory
       
   769         if (command == PRINTCERT || command == IMPORTCERT
       
   770                || command == IDENTITYDB) {
       
   771             cf = CertificateFactory.getInstance("X509");
       
   772         }
       
   773 
       
   774         if (trustcacerts) {
       
   775             caks = getCacertsKeyStore();
       
   776         }
       
   777 
       
   778         // Perform the specified command
       
   779         if (command == CERTREQ) {
       
   780             PrintStream ps = null;
       
   781             if (filename != null) {
       
   782                 ps = new PrintStream(new FileOutputStream
       
   783                                                  (filename));
       
   784                 out = ps;
       
   785             }
       
   786             try {
       
   787                 doCertReq(alias, sigAlgName, out);
       
   788             } finally {
       
   789                 if (ps != null) {
       
   790                     ps.close();
       
   791                 }
       
   792             }
       
   793             if (verbose && filename != null) {
       
   794                 MessageFormat form = new MessageFormat(rb.getString
       
   795                         ("Certification request stored in file <filename>"));
       
   796                 Object[] source = {filename};
       
   797                 System.err.println(form.format(source));
       
   798                 System.err.println(rb.getString("Submit this to your CA"));
       
   799             }
       
   800         } else if (command == DELETE) {
       
   801             doDeleteEntry(alias);
       
   802             kssave = true;
       
   803         } else if (command == EXPORTCERT) {
       
   804             PrintStream ps = null;
       
   805             if (filename != null) {
       
   806                 ps = new PrintStream(new FileOutputStream
       
   807                                                  (filename));
       
   808                 out = ps;
       
   809             }
       
   810             try {
       
   811                 doExportCert(alias, out);
       
   812             } finally {
       
   813                 if (ps != null) {
       
   814                     ps.close();
       
   815                 }
       
   816             }
       
   817             if (filename != null) {
       
   818                 MessageFormat form = new MessageFormat(rb.getString
       
   819                         ("Certificate stored in file <filename>"));
       
   820                 Object[] source = {filename};
       
   821                 System.err.println(form.format(source));
       
   822             }
       
   823         } else if (command == GENKEYPAIR) {
       
   824             if (keyAlgName == null) {
       
   825                 keyAlgName = "DSA";
       
   826             }
       
   827             doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName);
       
   828             kssave = true;
       
   829         } else if (command == GENSECKEY) {
       
   830             if (keyAlgName == null) {
       
   831                 keyAlgName = "DES";
       
   832             }
       
   833             doGenSecretKey(alias, keyAlgName, keysize);
       
   834             kssave = true;
       
   835         } else if (command == IDENTITYDB) {
       
   836             InputStream inStream = System.in;
       
   837             if (filename != null) {
       
   838                 inStream = new FileInputStream(filename);
       
   839             }
       
   840             try {
       
   841                 doImportIdentityDatabase(inStream);
       
   842             } finally {
       
   843                 if (inStream != System.in) {
       
   844                     inStream.close();
       
   845                 }
       
   846             }
       
   847         } else if (command == IMPORTCERT) {
       
   848             InputStream inStream = System.in;
       
   849             if (filename != null) {
       
   850                 inStream = new FileInputStream(filename);
       
   851             }
       
   852             try {
       
   853                 String importAlias = (alias!=null)?alias:keyAlias;
       
   854                 if (keyStore.entryInstanceOf(importAlias, KeyStore.PrivateKeyEntry.class)) {
       
   855                     kssave = installReply(importAlias, inStream);
       
   856                     if (kssave) {
       
   857                         System.err.println(rb.getString
       
   858                             ("Certificate reply was installed in keystore"));
       
   859                     } else {
       
   860                         System.err.println(rb.getString
       
   861                             ("Certificate reply was not installed in keystore"));
       
   862                     }
       
   863                 } else if (!keyStore.containsAlias(importAlias) ||
       
   864                         keyStore.entryInstanceOf(importAlias,
       
   865                             KeyStore.TrustedCertificateEntry.class)) {
       
   866                     kssave = addTrustedCert(importAlias, inStream);
       
   867                     if (kssave) {
       
   868                         System.err.println(rb.getString
       
   869                             ("Certificate was added to keystore"));
       
   870                     } else {
       
   871                         System.err.println(rb.getString
       
   872                             ("Certificate was not added to keystore"));
       
   873                     }
       
   874                 }
       
   875             } finally {
       
   876                 if (inStream != System.in) {
       
   877                     inStream.close();
       
   878                 }
       
   879             }
       
   880         } else if (command == IMPORTKEYSTORE) {
       
   881             doImportKeyStore();
       
   882             kssave = true;
       
   883         } else if (command == KEYCLONE) {
       
   884             keyPassNew = newPass;
       
   885 
       
   886             // added to make sure only key can go thru
       
   887             if (alias == null) {
       
   888                 alias = keyAlias;
       
   889             }
       
   890             if (keyStore.containsAlias(alias) == false) {
       
   891                 MessageFormat form = new MessageFormat
       
   892                     (rb.getString("Alias <alias> does not exist"));
       
   893                 Object[] source = {alias};
       
   894                 throw new Exception(form.format(source));
       
   895             }
       
   896             if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
       
   897                 MessageFormat form = new MessageFormat(rb.getString(
       
   898                         "Alias <alias> references an entry type that is not a private key entry.  " +
       
   899                         "The -keyclone command only supports cloning of private key entries"));
       
   900                 Object[] source = {alias};
       
   901                 throw new Exception(form.format(source));
       
   902             }
       
   903 
       
   904             doCloneEntry(alias, dest, true);  // Now everything can be cloned
       
   905             kssave = true;
       
   906         } else if (command == CHANGEALIAS) {
       
   907             if (alias == null) {
       
   908                 alias = keyAlias;
       
   909             }
       
   910             doCloneEntry(alias, dest, false);
       
   911             // in PKCS11, clone a PrivateKeyEntry will delete the old one
       
   912             if (keyStore.containsAlias(alias)) {
       
   913                 doDeleteEntry(alias);
       
   914             }
       
   915             kssave = true;
       
   916         } else if (command == KEYPASSWD) {
       
   917             keyPassNew = newPass;
       
   918             doChangeKeyPasswd(alias);
       
   919             kssave = true;
       
   920         } else if (command == LIST) {
       
   921             if (alias != null) {
       
   922                 doPrintEntry(alias, out, true);
       
   923             } else {
       
   924                 doPrintEntries(out);
       
   925             }
       
   926         } else if (command == PRINTCERT) {
       
   927             InputStream inStream = System.in;
       
   928             if (filename != null) {
       
   929                 inStream = new FileInputStream(filename);
       
   930             }
       
   931             try {
       
   932                 doPrintCert(inStream, out);
       
   933             } finally {
       
   934                 if (inStream != System.in) {
       
   935                     inStream.close();
       
   936                 }
       
   937             }
       
   938         } else if (command == SELFCERT) {
       
   939             doSelfCert(alias, dname, sigAlgName);
       
   940             kssave = true;
       
   941         } else if (command == STOREPASSWD) {
       
   942             storePassNew = newPass;
       
   943             if (storePassNew == null) {
       
   944                 storePassNew = getNewPasswd("keystore password", storePass);
       
   945             }
       
   946             kssave = true;
       
   947         }
       
   948 
       
   949         // If we need to save the keystore, do so.
       
   950         if (kssave) {
       
   951             if (verbose) {
       
   952                 MessageFormat form = new MessageFormat
       
   953                         (rb.getString("[Storing ksfname]"));
       
   954                 Object[] source = {nullStream ? "keystore" : ksfname};
       
   955                 System.err.println(form.format(source));
       
   956             }
       
   957 
       
   958             if (token) {
       
   959                 keyStore.store(null, null);
       
   960             } else {
       
   961                 FileOutputStream fout = null;
       
   962                 try {
       
   963                     fout = (nullStream ?
       
   964                                         (FileOutputStream)null :
       
   965                                         new FileOutputStream(ksfname));
       
   966                     keyStore.store
       
   967                         (fout,
       
   968                         (storePassNew!=null) ? storePassNew : storePass);
       
   969                 } finally {
       
   970                     if (fout != null) {
       
   971                         fout.close();
       
   972                     }
       
   973                 }
       
   974             }
       
   975         }
       
   976     }
       
   977 
       
   978     /**
       
   979      * Creates a PKCS#10 cert signing request, corresponding to the
       
   980      * keys (and name) associated with a given alias.
       
   981      */
       
   982     private void doCertReq(String alias, String sigAlgName, PrintStream out)
       
   983         throws Exception
       
   984     {
       
   985         if (alias == null) {
       
   986             alias = keyAlias;
       
   987         }
       
   988 
       
   989         Object[] objs = recoverKey(alias, storePass, keyPass);
       
   990         PrivateKey privKey = (PrivateKey)objs[0];
       
   991         if (keyPass == null) {
       
   992             keyPass = (char[])objs[1];
       
   993         }
       
   994 
       
   995         Certificate cert = keyStore.getCertificate(alias);
       
   996         if (cert == null) {
       
   997             MessageFormat form = new MessageFormat
       
   998                 (rb.getString("alias has no public key (certificate)"));
       
   999             Object[] source = {alias};
       
  1000             throw new Exception(form.format(source));
       
  1001         }
       
  1002         PKCS10 request = new PKCS10(cert.getPublicKey());
       
  1003 
       
  1004         // Construct an X500Signer object, so that we can sign the request
       
  1005         if (sigAlgName == null) {
       
  1006             // If no signature algorithm was specified at the command line,
       
  1007             // we choose one that is compatible with the selected private key
       
  1008             String keyAlgName = privKey.getAlgorithm();
       
  1009             if ("DSA".equalsIgnoreCase(keyAlgName)
       
  1010                    || "DSS".equalsIgnoreCase(keyAlgName)) {
       
  1011                 sigAlgName = "SHA1WithDSA";
       
  1012             } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
       
  1013                 sigAlgName = "SHA1WithRSA";
       
  1014             } else {
       
  1015                 throw new Exception(rb.getString
       
  1016                         ("Cannot derive signature algorithm"));
       
  1017             }
       
  1018         }
       
  1019 
       
  1020         Signature signature = Signature.getInstance(sigAlgName);
       
  1021         signature.initSign(privKey);
       
  1022         X500Name subject =
       
  1023             new X500Name(((X509Certificate)cert).getSubjectDN().toString());
       
  1024         X500Signer signer = new X500Signer(signature, subject);
       
  1025 
       
  1026         // Sign the request and base-64 encode it
       
  1027         request.encodeAndSign(signer);
       
  1028         request.print(out);
       
  1029     }
       
  1030 
       
  1031     /**
       
  1032      * Deletes an entry from the keystore.
       
  1033      */
       
  1034     private void doDeleteEntry(String alias) throws Exception {
       
  1035         if (keyStore.containsAlias(alias) == false) {
       
  1036             MessageFormat form = new MessageFormat
       
  1037                 (rb.getString("Alias <alias> does not exist"));
       
  1038             Object[] source = {alias};
       
  1039             throw new Exception(form.format(source));
       
  1040         }
       
  1041         keyStore.deleteEntry(alias);
       
  1042     }
       
  1043 
       
  1044     /**
       
  1045      * Exports a certificate from the keystore.
       
  1046      */
       
  1047     private void doExportCert(String alias, PrintStream out)
       
  1048         throws Exception
       
  1049     {
       
  1050         if (storePass == null
       
  1051                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
       
  1052             printWarning();
       
  1053         }
       
  1054         if (alias == null) {
       
  1055             alias = keyAlias;
       
  1056         }
       
  1057         if (keyStore.containsAlias(alias) == false) {
       
  1058             MessageFormat form = new MessageFormat
       
  1059                 (rb.getString("Alias <alias> does not exist"));
       
  1060             Object[] source = {alias};
       
  1061             throw new Exception(form.format(source));
       
  1062         }
       
  1063 
       
  1064         X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
       
  1065         if (cert == null) {
       
  1066             MessageFormat form = new MessageFormat
       
  1067                 (rb.getString("Alias <alias> has no certificate"));
       
  1068             Object[] source = {alias};
       
  1069             throw new Exception(form.format(source));
       
  1070         }
       
  1071         dumpCert(cert, out);
       
  1072     }
       
  1073 
       
  1074     /**
       
  1075      * Prompt the user for a keypass when generating a key entry.
       
  1076      * @param alias the entry we will set password for
       
  1077      * @param orig the original entry of doing a dup, null if generate new
       
  1078      * @param origPass the password to copy from if user press ENTER
       
  1079      */
       
  1080     private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{
       
  1081         if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
       
  1082             return origPass;
       
  1083         } else if (!token) {
       
  1084             // Prompt for key password
       
  1085             int count;
       
  1086             for (count = 0; count < 3; count++) {
       
  1087                 MessageFormat form = new MessageFormat(rb.getString
       
  1088                         ("Enter key password for <alias>"));
       
  1089                 Object[] source = {alias};
       
  1090                 System.err.println(form.format(source));
       
  1091                 if (orig == null) {
       
  1092                     System.err.print(rb.getString
       
  1093                             ("\t(RETURN if same as keystore password):  "));
       
  1094                 } else {
       
  1095                     form = new MessageFormat(rb.getString
       
  1096                             ("\t(RETURN if same as for <otherAlias>)"));
       
  1097                     Object[] src = {orig};
       
  1098                     System.err.print(form.format(src));
       
  1099                 }
       
  1100                 System.err.flush();
       
  1101                 char[] entered = Password.readPassword(System.in);
       
  1102                 passwords.add(entered);
       
  1103                 if (entered == null) {
       
  1104                     return origPass;
       
  1105                 } else if (entered.length >= 6) {
       
  1106                     System.err.print(rb.getString("Re-enter new password: "));
       
  1107                     char[] passAgain = Password.readPassword(System.in);
       
  1108                     passwords.add(passAgain);
       
  1109                     if (!Arrays.equals(entered, passAgain)) {
       
  1110                         System.err.println
       
  1111                             (rb.getString("They don't match. Try again"));
       
  1112                         continue;
       
  1113                     }
       
  1114                     return entered;
       
  1115                 } else {
       
  1116                     System.err.println(rb.getString
       
  1117                         ("Key password is too short - must be at least 6 characters"));
       
  1118                 }
       
  1119             }
       
  1120             if (count == 3) {
       
  1121                 if (command == KEYCLONE) {
       
  1122                     throw new Exception(rb.getString
       
  1123                         ("Too many failures. Key entry not cloned"));
       
  1124                 } else {
       
  1125                     throw new Exception(rb.getString
       
  1126                             ("Too many failures - key not added to keystore"));
       
  1127                 }
       
  1128             }
       
  1129         }
       
  1130         return null;    // PKCS11
       
  1131     }
       
  1132     /**
       
  1133      * Creates a new secret key.
       
  1134      */
       
  1135     private void doGenSecretKey(String alias, String keyAlgName,
       
  1136                               int keysize)
       
  1137         throws Exception
       
  1138     {
       
  1139         if (alias == null) {
       
  1140             alias = keyAlias;
       
  1141         }
       
  1142         if (keyStore.containsAlias(alias)) {
       
  1143             MessageFormat form = new MessageFormat(rb.getString
       
  1144                 ("Secret key not generated, alias <alias> already exists"));
       
  1145             Object[] source = {alias};
       
  1146             throw new Exception(form.format(source));
       
  1147         }
       
  1148 
       
  1149         SecretKey secKey = null;
       
  1150         KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);
       
  1151         if (keysize != -1) {
       
  1152             keygen.init(keysize);
       
  1153         } else if ("DES".equalsIgnoreCase(keyAlgName)) {
       
  1154             keygen.init(56);
       
  1155         } else if ("DESede".equalsIgnoreCase(keyAlgName)) {
       
  1156             keygen.init(168);
       
  1157         } else {
       
  1158             throw new Exception(rb.getString
       
  1159                 ("Please provide -keysize for secret key generation"));
       
  1160         }
       
  1161 
       
  1162         secKey = keygen.generateKey();
       
  1163         if (keyPass == null) {
       
  1164             keyPass = promptForKeyPass(alias, null, storePass);
       
  1165         }
       
  1166         keyStore.setKeyEntry(alias, secKey, keyPass, null);
       
  1167     }
       
  1168 
       
  1169     /**
       
  1170      * Creates a new key pair and self-signed certificate.
       
  1171      */
       
  1172     private void doGenKeyPair(String alias, String dname, String keyAlgName,
       
  1173                               int keysize, String sigAlgName)
       
  1174         throws Exception
       
  1175     {
       
  1176         if (keysize == -1) {
       
  1177             if ("EC".equalsIgnoreCase(keyAlgName)) {
       
  1178                 keysize = 256;
       
  1179             } else {
       
  1180                 keysize = 1024;
       
  1181             }
       
  1182         }
       
  1183 
       
  1184         if (alias == null) {
       
  1185             alias = keyAlias;
       
  1186         }
       
  1187 
       
  1188         if (keyStore.containsAlias(alias)) {
       
  1189             MessageFormat form = new MessageFormat(rb.getString
       
  1190                 ("Key pair not generated, alias <alias> already exists"));
       
  1191             Object[] source = {alias};
       
  1192             throw new Exception(form.format(source));
       
  1193         }
       
  1194 
       
  1195         if (sigAlgName == null) {
       
  1196             if ("DSA".equalsIgnoreCase(keyAlgName)) {
       
  1197                 sigAlgName = "SHA1WithDSA";
       
  1198             } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
       
  1199                 sigAlgName = "SHA1WithRSA";
       
  1200             } else if ("EC".equalsIgnoreCase(keyAlgName)) {
       
  1201                 sigAlgName = "SHA1withECDSA";
       
  1202             } else {
       
  1203                 throw new Exception(rb.getString
       
  1204                         ("Cannot derive signature algorithm"));
       
  1205             }
       
  1206         }
       
  1207         CertAndKeyGen keypair =
       
  1208                 new CertAndKeyGen(keyAlgName, sigAlgName, providerName);
       
  1209 
       
  1210 
       
  1211         // If DN is provided, parse it. Otherwise, prompt the user for it.
       
  1212         X500Name x500Name;
       
  1213         if (dname == null) {
       
  1214             x500Name = getX500Name();
       
  1215         } else {
       
  1216             x500Name = new X500Name(dname);
       
  1217         }
       
  1218 
       
  1219         keypair.generate(keysize);
       
  1220         PrivateKey privKey = keypair.getPrivateKey();
       
  1221 
       
  1222         X509Certificate[] chain = new X509Certificate[1];
       
  1223         chain[0] = keypair.getSelfCertificate(
       
  1224                 x500Name, getStartDate(startDate), validity*24L*60L*60L);
       
  1225 
       
  1226         if (verbose) {
       
  1227             MessageFormat form = new MessageFormat(rb.getString
       
  1228                 ("Generating keysize bit keyAlgName key pair and self-signed certificate " +
       
  1229                     "(sigAlgName) with a validity of validality days\n\tfor: x500Name"));
       
  1230             Object[] source = {new Integer(keysize),
       
  1231                                 privKey.getAlgorithm(),
       
  1232                                 chain[0].getSigAlgName(),
       
  1233                                 new Long(validity),
       
  1234                                 x500Name};
       
  1235             System.err.println(form.format(source));
       
  1236         }
       
  1237 
       
  1238         if (keyPass == null) {
       
  1239             keyPass = promptForKeyPass(alias, null, storePass);
       
  1240         }
       
  1241         keyStore.setKeyEntry(alias, privKey, keyPass, chain);
       
  1242     }
       
  1243 
       
  1244     /**
       
  1245      * Clones an entry
       
  1246      * @param orig original alias
       
  1247      * @param dest destination alias
       
  1248      * @changePassword if the password can be changed
       
  1249      */
       
  1250     private void doCloneEntry(String orig, String dest, boolean changePassword)
       
  1251         throws Exception
       
  1252     {
       
  1253         if (orig == null) {
       
  1254             orig = keyAlias;
       
  1255         }
       
  1256 
       
  1257         if (keyStore.containsAlias(dest)) {
       
  1258             MessageFormat form = new MessageFormat
       
  1259                 (rb.getString("Destination alias <dest> already exists"));
       
  1260             Object[] source = {dest};
       
  1261             throw new Exception(form.format(source));
       
  1262         }
       
  1263 
       
  1264         Object[] objs = recoverEntry(keyStore, orig, storePass, keyPass);
       
  1265         Entry entry = (Entry)objs[0];
       
  1266         keyPass = (char[])objs[1];
       
  1267 
       
  1268         PasswordProtection pp = null;
       
  1269 
       
  1270         if (keyPass != null) {  // protected
       
  1271             if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {
       
  1272                 keyPassNew = keyPass;
       
  1273             } else {
       
  1274                 if (keyPassNew == null) {
       
  1275                     keyPassNew = promptForKeyPass(dest, orig, keyPass);
       
  1276                 }
       
  1277             }
       
  1278             pp = new PasswordProtection(keyPassNew);
       
  1279         }
       
  1280         keyStore.setEntry(dest, entry, pp);
       
  1281     }
       
  1282 
       
  1283     /**
       
  1284      * Changes a key password.
       
  1285      */
       
  1286     private void doChangeKeyPasswd(String alias) throws Exception
       
  1287     {
       
  1288 
       
  1289         if (alias == null) {
       
  1290             alias = keyAlias;
       
  1291         }
       
  1292         Object[] objs = recoverKey(alias, storePass, keyPass);
       
  1293         Key privKey = (Key)objs[0];
       
  1294         if (keyPass == null) {
       
  1295             keyPass = (char[])objs[1];
       
  1296         }
       
  1297 
       
  1298         if (keyPassNew == null) {
       
  1299             MessageFormat form = new MessageFormat
       
  1300                 (rb.getString("key password for <alias>"));
       
  1301             Object[] source = {alias};
       
  1302             keyPassNew = getNewPasswd(form.format(source), keyPass);
       
  1303         }
       
  1304         keyStore.setKeyEntry(alias, privKey, keyPassNew,
       
  1305                              keyStore.getCertificateChain(alias));
       
  1306     }
       
  1307 
       
  1308     /**
       
  1309      * Imports a JDK 1.1-style identity database. We can only store one
       
  1310      * certificate per identity, because we use the identity's name as the
       
  1311      * alias (which references a keystore entry), and aliases must be unique.
       
  1312      */
       
  1313     private void doImportIdentityDatabase(InputStream in)
       
  1314         throws Exception
       
  1315     {
       
  1316         byte[] encoded;
       
  1317         ByteArrayInputStream bais;
       
  1318         java.security.cert.X509Certificate newCert;
       
  1319         java.security.cert.Certificate[] chain = null;
       
  1320         PrivateKey privKey;
       
  1321         boolean modified = false;
       
  1322 
       
  1323         IdentityDatabase idb = IdentityDatabase.fromStream(in);
       
  1324         for (Enumeration<Identity> enum_ = idb.identities();
       
  1325                                         enum_.hasMoreElements();) {
       
  1326             Identity id = enum_.nextElement();
       
  1327             newCert = null;
       
  1328             // only store trusted identities in keystore
       
  1329             if ((id instanceof SystemSigner && ((SystemSigner)id).isTrusted())
       
  1330                 || (id instanceof SystemIdentity
       
  1331                     && ((SystemIdentity)id).isTrusted())) {
       
  1332                 // ignore if keystore entry with same alias name already exists
       
  1333                 if (keyStore.containsAlias(id.getName())) {
       
  1334                     MessageFormat form = new MessageFormat
       
  1335                         (rb.getString("Keystore entry for <id.getName()> already exists"));
       
  1336                     Object[] source = {id.getName()};
       
  1337                     System.err.println(form.format(source));
       
  1338                     continue;
       
  1339                 }
       
  1340                 java.security.Certificate[] certs = id.certificates();
       
  1341                 if (certs!=null && certs.length>0) {
       
  1342                     // we can only store one user cert per identity.
       
  1343                     // convert old-style to new-style cert via the encoding
       
  1344                     DerOutputStream dos = new DerOutputStream();
       
  1345                     certs[0].encode(dos);
       
  1346                     encoded = dos.toByteArray();
       
  1347                     bais = new ByteArrayInputStream(encoded);
       
  1348                     newCert = (X509Certificate)cf.generateCertificate(bais);
       
  1349                     bais.close();
       
  1350 
       
  1351                     // if certificate is self-signed, make sure it verifies
       
  1352                     if (isSelfSigned(newCert)) {
       
  1353                         PublicKey pubKey = newCert.getPublicKey();
       
  1354                         try {
       
  1355                             newCert.verify(pubKey);
       
  1356                         } catch (Exception e) {
       
  1357                             // ignore this cert
       
  1358                             continue;
       
  1359                         }
       
  1360                     }
       
  1361 
       
  1362                     if (id instanceof SystemSigner) {
       
  1363                         MessageFormat form = new MessageFormat(rb.getString
       
  1364                             ("Creating keystore entry for <id.getName()> ..."));
       
  1365                         Object[] source = {id.getName()};
       
  1366                         System.err.println(form.format(source));
       
  1367                         if (chain==null) {
       
  1368                             chain = new java.security.cert.Certificate[1];
       
  1369                         }
       
  1370                         chain[0] = newCert;
       
  1371                         privKey = ((SystemSigner)id).getPrivateKey();
       
  1372                         keyStore.setKeyEntry(id.getName(), privKey, storePass,
       
  1373                                              chain);
       
  1374                     } else {
       
  1375                         keyStore.setCertificateEntry(id.getName(), newCert);
       
  1376                     }
       
  1377                     kssave = true;
       
  1378                 }
       
  1379             }
       
  1380         }
       
  1381         if (!kssave) {
       
  1382             System.err.println(rb.getString
       
  1383                 ("No entries from identity database added"));
       
  1384         }
       
  1385     }
       
  1386 
       
  1387     /**
       
  1388      * Prints a single keystore entry.
       
  1389      */
       
  1390     private void doPrintEntry(String alias, PrintStream out,
       
  1391                               boolean printWarning)
       
  1392         throws Exception
       
  1393     {
       
  1394         if (storePass == null && printWarning
       
  1395                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
       
  1396             printWarning();
       
  1397         }
       
  1398 
       
  1399         if (keyStore.containsAlias(alias) == false) {
       
  1400             MessageFormat form = new MessageFormat
       
  1401                 (rb.getString("Alias <alias> does not exist"));
       
  1402             Object[] source = {alias};
       
  1403             throw new Exception(form.format(source));
       
  1404         }
       
  1405 
       
  1406         if (verbose || rfc || debug) {
       
  1407             MessageFormat form = new MessageFormat
       
  1408                 (rb.getString("Alias name: alias"));
       
  1409             Object[] source = {alias};
       
  1410             out.println(form.format(source));
       
  1411 
       
  1412             if (!token) {
       
  1413                 form = new MessageFormat(rb.getString
       
  1414                     ("Creation date: keyStore.getCreationDate(alias)"));
       
  1415                 Object[] src = {keyStore.getCreationDate(alias)};
       
  1416                 out.println(form.format(src));
       
  1417             }
       
  1418         } else {
       
  1419             if (!token) {
       
  1420                 MessageFormat form = new MessageFormat
       
  1421                     (rb.getString("alias, keyStore.getCreationDate(alias), "));
       
  1422                 Object[] source = {alias, keyStore.getCreationDate(alias)};
       
  1423                 out.print(form.format(source));
       
  1424             } else {
       
  1425                 MessageFormat form = new MessageFormat
       
  1426                     (rb.getString("alias, "));
       
  1427                 Object[] source = {alias};
       
  1428                 out.print(form.format(source));
       
  1429             }
       
  1430         }
       
  1431 
       
  1432         if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
       
  1433             if (verbose || rfc || debug) {
       
  1434                 Object[] source = {"SecretKeyEntry"};
       
  1435                 out.println(new MessageFormat(
       
  1436                         rb.getString("Entry type: <type>")).format(source));
       
  1437             } else {
       
  1438                 out.println("SecretKeyEntry, ");
       
  1439             }
       
  1440         } else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
       
  1441             if (verbose || rfc || debug) {
       
  1442                 Object[] source = {"PrivateKeyEntry"};
       
  1443                 out.println(new MessageFormat(
       
  1444                         rb.getString("Entry type: <type>")).format(source));
       
  1445             } else {
       
  1446                 out.println("PrivateKeyEntry, ");
       
  1447             }
       
  1448 
       
  1449             // Get the chain
       
  1450             Certificate[] chain = keyStore.getCertificateChain(alias);
       
  1451             if (chain != null) {
       
  1452                 if (verbose || rfc || debug) {
       
  1453                     out.println(rb.getString
       
  1454                         ("Certificate chain length: ") + chain.length);
       
  1455                     for (int i = 0; i < chain.length; i ++) {
       
  1456                         MessageFormat form = new MessageFormat
       
  1457                                 (rb.getString("Certificate[(i + 1)]:"));
       
  1458                         Object[] source = {new Integer((i + 1))};
       
  1459                         out.println(form.format(source));
       
  1460                         if (verbose && (chain[i] instanceof X509Certificate)) {
       
  1461                             printX509Cert((X509Certificate)(chain[i]), out);
       
  1462                         } else if (debug) {
       
  1463                             out.println(chain[i].toString());
       
  1464                         } else {
       
  1465                             dumpCert(chain[i], out);
       
  1466                         }
       
  1467                     }
       
  1468                 } else {
       
  1469                     // Print the digest of the user cert only
       
  1470                     out.println
       
  1471                         (rb.getString("Certificate fingerprint (MD5): ") +
       
  1472                         getCertFingerPrint("MD5", chain[0]));
       
  1473                 }
       
  1474             }
       
  1475         } else if (keyStore.entryInstanceOf(alias,
       
  1476                 KeyStore.TrustedCertificateEntry.class)) {
       
  1477             // We have a trusted certificate entry
       
  1478             Certificate cert = keyStore.getCertificate(alias);
       
  1479             if (verbose && (cert instanceof X509Certificate)) {
       
  1480                 out.println(rb.getString("Entry type: trustedCertEntry\n"));
       
  1481                 printX509Cert((X509Certificate)cert, out);
       
  1482             } else if (rfc) {
       
  1483                 out.println(rb.getString("Entry type: trustedCertEntry\n"));
       
  1484                 dumpCert(cert, out);
       
  1485             } else if (debug) {
       
  1486                 out.println(cert.toString());
       
  1487             } else {
       
  1488                 out.println(rb.getString("trustedCertEntry,"));
       
  1489                 out.println(rb.getString("Certificate fingerprint (MD5): ")
       
  1490                             + getCertFingerPrint("MD5", cert));
       
  1491             }
       
  1492         } else {
       
  1493             out.println(rb.getString("Unknown Entry Type"));
       
  1494         }
       
  1495     }
       
  1496 
       
  1497     /**
       
  1498      * Load the srckeystore from a stream, used in -importkeystore
       
  1499      * @returns the src KeyStore
       
  1500      */
       
  1501     KeyStore loadSourceKeyStore() throws Exception {
       
  1502         boolean isPkcs11 = false;
       
  1503 
       
  1504         InputStream is = null;
       
  1505 
       
  1506         if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
       
  1507                 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
       
  1508             if (!NONE.equals(srcksfname)) {
       
  1509                 System.err.println(MessageFormat.format(rb.getString
       
  1510                     ("-keystore must be NONE if -storetype is {0}"), srcstoretype));
       
  1511                 System.err.println();
       
  1512                 tinyHelp();
       
  1513             }
       
  1514             isPkcs11 = true;
       
  1515         } else {
       
  1516             if (srcksfname != null) {
       
  1517                 File srcksfile = new File(srcksfname);
       
  1518                     if (srcksfile.exists() && srcksfile.length() == 0) {
       
  1519                         throw new Exception(rb.getString
       
  1520                                 ("Source keystore file exists, but is empty: ") +
       
  1521                                 srcksfname);
       
  1522                 }
       
  1523                 is = new FileInputStream(srcksfile);
       
  1524             } else {
       
  1525                 throw new Exception(rb.getString
       
  1526                         ("Please specify -srckeystore"));
       
  1527             }
       
  1528         }
       
  1529 
       
  1530         KeyStore store;
       
  1531         try {
       
  1532             if (srcProviderName == null) {
       
  1533                 store = KeyStore.getInstance(srcstoretype);
       
  1534             } else {
       
  1535                 store = KeyStore.getInstance(srcstoretype, srcProviderName);
       
  1536             }
       
  1537 
       
  1538             if (srcstorePass == null
       
  1539                     && !srcprotectedPath
       
  1540                     && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
       
  1541                 System.err.print(rb.getString("Enter source keystore password:  "));
       
  1542                 System.err.flush();
       
  1543                 srcstorePass = Password.readPassword(System.in);
       
  1544                 passwords.add(srcstorePass);
       
  1545             }
       
  1546 
       
  1547             // always let keypass be storepass when using pkcs12
       
  1548             if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {
       
  1549                 if (srckeyPass != null && srcstorePass != null &&
       
  1550                         !Arrays.equals(srcstorePass, srckeyPass)) {
       
  1551                     MessageFormat form = new MessageFormat(rb.getString(
       
  1552                         "Warning:  Different store and key passwords not supported " +
       
  1553                         "for PKCS12 KeyStores. Ignoring user-specified <command> value."));
       
  1554                     Object[] source = {"-srckeypass"};
       
  1555                     System.err.println(form.format(source));
       
  1556                     srckeyPass = srcstorePass;
       
  1557                 }
       
  1558             }
       
  1559 
       
  1560             store.load(is, srcstorePass);   // "is" already null in PKCS11
       
  1561         } finally {
       
  1562             if (is != null) {
       
  1563                 is.close();
       
  1564             }
       
  1565         }
       
  1566 
       
  1567         if (srcstorePass == null
       
  1568                 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
       
  1569             // anti refactoring, copied from printWarning(),
       
  1570             // but change 2 lines
       
  1571             System.err.println();
       
  1572             System.err.println(rb.getString
       
  1573                 ("*****************  WARNING WARNING WARNING  *****************"));
       
  1574             System.err.println(rb.getString
       
  1575                 ("* The integrity of the information stored in the srckeystore*"));
       
  1576             System.err.println(rb.getString
       
  1577                 ("* has NOT been verified!  In order to verify its integrity, *"));
       
  1578             System.err.println(rb.getString
       
  1579                 ("* you must provide the srckeystore password.                *"));
       
  1580             System.err.println(rb.getString
       
  1581                 ("*****************  WARNING WARNING WARNING  *****************"));
       
  1582             System.err.println();
       
  1583         }
       
  1584 
       
  1585         return store;
       
  1586     }
       
  1587 
       
  1588     /**
       
  1589      * import all keys and certs from importkeystore.
       
  1590      * keep alias unchanged if no name conflict, otherwise, prompt.
       
  1591      * keep keypass unchanged for keys
       
  1592      */
       
  1593     private void doImportKeyStore() throws Exception {
       
  1594 
       
  1595         if (alias != null) {
       
  1596             doImportKeyStoreSingle(loadSourceKeyStore(), alias);
       
  1597         } else {
       
  1598             if (dest != null || srckeyPass != null || destKeyPass != null) {
       
  1599                 throw new Exception(rb.getString(
       
  1600                         "if alias not specified, destalias, srckeypass, " +
       
  1601                         "and destkeypass must not be specified"));
       
  1602             }
       
  1603             doImportKeyStoreAll(loadSourceKeyStore());
       
  1604         }
       
  1605         /*
       
  1606          * Information display rule of -importkeystore
       
  1607          * 1. inside single, shows failure
       
  1608          * 2. inside all, shows sucess
       
  1609          * 3. inside all where there is a failure, prompt for continue
       
  1610          * 4. at the final of all, shows summary
       
  1611          */
       
  1612     }
       
  1613 
       
  1614     /**
       
  1615      * Import a single entry named alias from srckeystore
       
  1616      * @returns 1 if the import action succeed
       
  1617      *          0 if user choose to ignore an alias-dumplicated entry
       
  1618      *          2 if setEntry throws Exception
       
  1619      */
       
  1620     private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)
       
  1621             throws Exception {
       
  1622 
       
  1623         String newAlias = (dest==null) ? alias : dest;
       
  1624 
       
  1625         if (keyStore.containsAlias(newAlias)) {
       
  1626             Object[] source = {alias};
       
  1627             if (noprompt) {
       
  1628                 System.err.println(new MessageFormat(rb.getString(
       
  1629                         "Warning: Overwriting existing alias <alias> in destination keystore")).format(source));
       
  1630             } else {
       
  1631                 String reply = getYesNoReply(new MessageFormat(rb.getString(
       
  1632                         "Existing entry alias <alias> exists, overwrite? [no]:  ")).format(source));
       
  1633                 if ("NO".equals(reply)) {
       
  1634                     newAlias = inputStringFromStdin(rb.getString
       
  1635                             ("Enter new alias name\t(RETURN to cancel import for this entry):  "));
       
  1636                     if ("".equals(newAlias)) {
       
  1637                         System.err.println(new MessageFormat(rb.getString(
       
  1638                                 "Entry for alias <alias> not imported.")).format(
       
  1639                                 source));
       
  1640                         return 0;
       
  1641                     }
       
  1642                 }
       
  1643             }
       
  1644         }
       
  1645 
       
  1646         Object[] objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
       
  1647         Entry entry = (Entry)objs[0];
       
  1648 
       
  1649         PasswordProtection pp = null;
       
  1650 
       
  1651         // According to keytool.html, "The destination entry will be protected
       
  1652         // using destkeypass. If destkeypass is not provided, the destination
       
  1653         // entry will be protected with the source entry password."
       
  1654         // so always try to protect with destKeyPass.
       
  1655         if (destKeyPass != null) {
       
  1656             pp = new PasswordProtection(destKeyPass);
       
  1657         } else if (objs[1] != null) {
       
  1658             pp = new PasswordProtection((char[])objs[1]);
       
  1659         }
       
  1660 
       
  1661         try {
       
  1662             keyStore.setEntry(newAlias, entry, pp);
       
  1663             return 1;
       
  1664         } catch (KeyStoreException kse) {
       
  1665             Object[] source2 = {alias, kse.toString()};
       
  1666             MessageFormat form = new MessageFormat(rb.getString(
       
  1667                     "Problem importing entry for alias <alias>: <exception>.\nEntry for alias <alias> not imported."));
       
  1668             System.err.println(form.format(source2));
       
  1669             return 2;
       
  1670         }
       
  1671     }
       
  1672 
       
  1673     private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {
       
  1674 
       
  1675         int ok = 0;
       
  1676         int count = srckeystore.size();
       
  1677         for (Enumeration<String> e = srckeystore.aliases();
       
  1678                                         e.hasMoreElements(); ) {
       
  1679             String alias = e.nextElement();
       
  1680             int result = doImportKeyStoreSingle(srckeystore, alias);
       
  1681             if (result == 1) {
       
  1682                 ok++;
       
  1683                 Object[] source = {alias};
       
  1684                 MessageFormat form = new MessageFormat(rb.getString("Entry for alias <alias> successfully imported."));
       
  1685                 System.err.println(form.format(source));
       
  1686             } else if (result == 2) {
       
  1687                 if (!noprompt) {
       
  1688                     String reply = getYesNoReply("Do you want to quit the import process? [no]:  ");
       
  1689                     if ("YES".equals(reply)) {
       
  1690                         break;
       
  1691                     }
       
  1692                 }
       
  1693             }
       
  1694         }
       
  1695         Object[] source = {ok, count-ok};
       
  1696         MessageFormat form = new MessageFormat(rb.getString(
       
  1697                 "Import command completed:  <ok> entries successfully imported, <fail> entries failed or cancelled"));
       
  1698         System.err.println(form.format(source));
       
  1699     }
       
  1700 
       
  1701     /**
       
  1702      * Prints all keystore entries.
       
  1703      */
       
  1704     private void doPrintEntries(PrintStream out)
       
  1705         throws Exception
       
  1706     {
       
  1707         if (storePass == null
       
  1708                 && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
       
  1709             printWarning();
       
  1710         } else {
       
  1711             out.println();
       
  1712         }
       
  1713 
       
  1714         out.println(rb.getString("Keystore type: ") + keyStore.getType());
       
  1715         out.println(rb.getString("Keystore provider: ") +
       
  1716                 keyStore.getProvider().getName());
       
  1717         out.println();
       
  1718 
       
  1719         MessageFormat form;
       
  1720         form = (keyStore.size() == 1) ?
       
  1721                 new MessageFormat(rb.getString
       
  1722                         ("Your keystore contains keyStore.size() entry")) :
       
  1723                 new MessageFormat(rb.getString
       
  1724                         ("Your keystore contains keyStore.size() entries"));
       
  1725         Object[] source = {new Integer(keyStore.size())};
       
  1726         out.println(form.format(source));
       
  1727         out.println();
       
  1728 
       
  1729         for (Enumeration<String> e = keyStore.aliases();
       
  1730                                         e.hasMoreElements(); ) {
       
  1731             String alias = e.nextElement();
       
  1732             doPrintEntry(alias, out, false);
       
  1733             if (verbose || rfc) {
       
  1734                 out.println(rb.getString("\n"));
       
  1735                 out.println(rb.getString
       
  1736                         ("*******************************************"));
       
  1737                 out.println(rb.getString
       
  1738                         ("*******************************************\n\n"));
       
  1739             }
       
  1740         }
       
  1741     }
       
  1742 
       
  1743     /**
       
  1744      * Reads a certificate (or certificate chain) and prints its contents in
       
  1745      * a human readbable format.
       
  1746      */
       
  1747     private void doPrintCert(InputStream in, PrintStream out)
       
  1748         throws Exception
       
  1749     {
       
  1750         Collection<? extends Certificate> c = null;
       
  1751         try {
       
  1752             c = cf.generateCertificates(in);
       
  1753         } catch (CertificateException ce) {
       
  1754             throw new Exception(rb.getString("Failed to parse input"), ce);
       
  1755         }
       
  1756         if (c.isEmpty()) {
       
  1757             throw new Exception(rb.getString("Empty input"));
       
  1758         }
       
  1759         Certificate[] certs = c.toArray(new Certificate[c.size()]);
       
  1760         for (int i=0; i<certs.length; i++) {
       
  1761             X509Certificate x509Cert = null;
       
  1762             try {
       
  1763                 x509Cert = (X509Certificate)certs[i];
       
  1764             } catch (ClassCastException cce) {
       
  1765                 throw new Exception(rb.getString("Not X.509 certificate"));
       
  1766             }
       
  1767             if (certs.length > 1) {
       
  1768                 MessageFormat form = new MessageFormat
       
  1769                         (rb.getString("Certificate[(i + 1)]:"));
       
  1770                 Object[] source = {new Integer(i + 1)};
       
  1771                 out.println(form.format(source));
       
  1772             }
       
  1773             printX509Cert(x509Cert, out);
       
  1774             if (i < (certs.length-1)) {
       
  1775                 out.println();
       
  1776             }
       
  1777         }
       
  1778     }
       
  1779 
       
  1780     /**
       
  1781      * Creates a self-signed certificate, and stores it as a single-element
       
  1782      * certificate chain.
       
  1783      */
       
  1784     private void doSelfCert(String alias, String dname, String sigAlgName)
       
  1785         throws Exception
       
  1786     {
       
  1787         if (alias == null) {
       
  1788             alias = keyAlias;
       
  1789         }
       
  1790 
       
  1791         Object[] objs = recoverKey(alias, storePass, keyPass);
       
  1792         PrivateKey privKey = (PrivateKey)objs[0];
       
  1793         if (keyPass == null)
       
  1794             keyPass = (char[])objs[1];
       
  1795 
       
  1796         // Determine the signature algorithm
       
  1797         if (sigAlgName == null) {
       
  1798             // If no signature algorithm was specified at the command line,
       
  1799             // we choose one that is compatible with the selected private key
       
  1800             String keyAlgName = privKey.getAlgorithm();
       
  1801             if ("DSA".equalsIgnoreCase(keyAlgName)
       
  1802                    || "DSS".equalsIgnoreCase(keyAlgName)) {
       
  1803                 sigAlgName = "SHA1WithDSA";
       
  1804             } else if ("RSA".equalsIgnoreCase(keyAlgName)) {
       
  1805                 sigAlgName = "SHA1WithRSA";
       
  1806             } else if ("EC".equalsIgnoreCase(keyAlgName)) {
       
  1807                 sigAlgName = "SHA1withECDSA";
       
  1808             } else {
       
  1809                 throw new Exception
       
  1810                         (rb.getString("Cannot derive signature algorithm"));
       
  1811             }
       
  1812         }
       
  1813 
       
  1814         // Get the old certificate
       
  1815         Certificate oldCert = keyStore.getCertificate(alias);
       
  1816         if (oldCert == null) {
       
  1817             MessageFormat form = new MessageFormat
       
  1818                 (rb.getString("alias has no public key"));
       
  1819             Object[] source = {alias};
       
  1820             throw new Exception(form.format(source));
       
  1821         }
       
  1822         if (!(oldCert instanceof X509Certificate)) {
       
  1823             MessageFormat form = new MessageFormat
       
  1824                 (rb.getString("alias has no X.509 certificate"));
       
  1825             Object[] source = {alias};
       
  1826             throw new Exception(form.format(source));
       
  1827         }
       
  1828 
       
  1829         // convert to X509CertImpl, so that we can modify selected fields
       
  1830         // (no public APIs available yet)
       
  1831         byte[] encoded = oldCert.getEncoded();
       
  1832         X509CertImpl certImpl = new X509CertImpl(encoded);
       
  1833         X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME
       
  1834                                                            + "." +
       
  1835                                                            X509CertImpl.INFO);
       
  1836 
       
  1837         // Extend its validity
       
  1838         Date firstDate = getStartDate(startDate);
       
  1839         Date lastDate = new Date();
       
  1840         lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
       
  1841         CertificateValidity interval = new CertificateValidity(firstDate,
       
  1842                                                                lastDate);
       
  1843         certInfo.set(X509CertInfo.VALIDITY, interval);
       
  1844 
       
  1845         // Make new serial number
       
  1846         certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
       
  1847                      ((int)(firstDate.getTime()/1000)));
       
  1848 
       
  1849         // Set owner and issuer fields
       
  1850         X500Name owner;
       
  1851         if (dname == null) {
       
  1852             // Get the owner name from the certificate
       
  1853             owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +
       
  1854                                            CertificateSubjectName.DN_NAME);
       
  1855         } else {
       
  1856             // Use the owner name specified at the command line
       
  1857             owner = new X500Name(dname);
       
  1858             certInfo.set(X509CertInfo.SUBJECT + "." +
       
  1859                          CertificateSubjectName.DN_NAME, owner);
       
  1860         }
       
  1861         // Make issuer same as owner (self-signed!)
       
  1862         certInfo.set(X509CertInfo.ISSUER + "." +
       
  1863                      CertificateIssuerName.DN_NAME, owner);
       
  1864 
       
  1865         // The inner and outer signature algorithms have to match.
       
  1866         // The way we achieve that is really ugly, but there seems to be no
       
  1867         // other solution: We first sign the cert, then retrieve the
       
  1868         // outer sigalg and use it to set the inner sigalg
       
  1869         X509CertImpl newCert = new X509CertImpl(certInfo);
       
  1870         newCert.sign(privKey, sigAlgName);
       
  1871         AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
       
  1872         certInfo.set(CertificateAlgorithmId.NAME + "." +
       
  1873                      CertificateAlgorithmId.ALGORITHM, sigAlgid);
       
  1874 
       
  1875         // first upgrade to version 3
       
  1876 
       
  1877         certInfo.set(X509CertInfo.VERSION,
       
  1878                         new CertificateVersion(CertificateVersion.V3));
       
  1879 
       
  1880         // Sign the new certificate
       
  1881         newCert = new X509CertImpl(certInfo);
       
  1882         newCert.sign(privKey, sigAlgName);
       
  1883 
       
  1884         // Store the new certificate as a single-element certificate chain
       
  1885         keyStore.setKeyEntry(alias, privKey,
       
  1886                              (keyPass != null) ? keyPass : storePass,
       
  1887                              new Certificate[] { newCert } );
       
  1888 
       
  1889         if (verbose) {
       
  1890             System.err.println(rb.getString("New certificate (self-signed):"));
       
  1891             System.err.print(newCert.toString());
       
  1892             System.err.println();
       
  1893         }
       
  1894     }
       
  1895 
       
  1896     /**
       
  1897      * Processes a certificate reply from a certificate authority.
       
  1898      *
       
  1899      * <p>Builds a certificate chain on top of the certificate reply,
       
  1900      * using trusted certificates from the keystore. The chain is complete
       
  1901      * after a self-signed certificate has been encountered. The self-signed
       
  1902      * certificate is considered a root certificate authority, and is stored
       
  1903      * at the end of the chain.
       
  1904      *
       
  1905      * <p>The newly generated chain replaces the old chain associated with the
       
  1906      * key entry.
       
  1907      *
       
  1908      * @return true if the certificate reply was installed, otherwise false.
       
  1909      */
       
  1910     private boolean installReply(String alias, InputStream in)
       
  1911         throws Exception
       
  1912     {
       
  1913         if (alias == null) {
       
  1914             alias = keyAlias;
       
  1915         }
       
  1916 
       
  1917         Object[] objs = recoverKey(alias, storePass, keyPass);
       
  1918         PrivateKey privKey = (PrivateKey)objs[0];
       
  1919         if (keyPass == null) {
       
  1920             keyPass = (char[])objs[1];
       
  1921         }
       
  1922 
       
  1923         Certificate userCert = keyStore.getCertificate(alias);
       
  1924         if (userCert == null) {
       
  1925             MessageFormat form = new MessageFormat
       
  1926                 (rb.getString("alias has no public key (certificate)"));
       
  1927             Object[] source = {alias};
       
  1928             throw new Exception(form.format(source));
       
  1929         }
       
  1930 
       
  1931         // Read the certificates in the reply
       
  1932         Collection<? extends Certificate> c = cf.generateCertificates(in);
       
  1933         if (c.isEmpty()) {
       
  1934             throw new Exception(rb.getString("Reply has no certificates"));
       
  1935         }
       
  1936         Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);
       
  1937         Certificate[] newChain;
       
  1938         if (replyCerts.length == 1) {
       
  1939             // single-cert reply
       
  1940             newChain = establishCertChain(userCert, replyCerts[0]);
       
  1941         } else {
       
  1942             // cert-chain reply (e.g., PKCS#7)
       
  1943             newChain = validateReply(alias, userCert, replyCerts);
       
  1944         }
       
  1945 
       
  1946         // Now store the newly established chain in the keystore. The new
       
  1947         // chain replaces the old one.
       
  1948         if (newChain != null) {
       
  1949             keyStore.setKeyEntry(alias, privKey,
       
  1950                                  (keyPass != null) ? keyPass : storePass,
       
  1951                                  newChain);
       
  1952             return true;
       
  1953         } else {
       
  1954             return false;
       
  1955         }
       
  1956     }
       
  1957 
       
  1958     /**
       
  1959      * Imports a certificate and adds it to the list of trusted certificates.
       
  1960      *
       
  1961      * @return true if the certificate was added, otherwise false.
       
  1962      */
       
  1963     private boolean addTrustedCert(String alias, InputStream in)
       
  1964         throws Exception
       
  1965     {
       
  1966         if (alias == null) {
       
  1967             throw new Exception(rb.getString("Must specify alias"));
       
  1968         }
       
  1969         if (keyStore.containsAlias(alias)) {
       
  1970             MessageFormat form = new MessageFormat(rb.getString
       
  1971                 ("Certificate not imported, alias <alias> already exists"));
       
  1972             Object[] source = {alias};
       
  1973             throw new Exception(form.format(source));
       
  1974         }
       
  1975 
       
  1976         // Read the certificate
       
  1977         X509Certificate cert = null;
       
  1978         try {
       
  1979             cert = (X509Certificate)cf.generateCertificate(in);
       
  1980         } catch (ClassCastException cce) {
       
  1981             throw new Exception(rb.getString("Input not an X.509 certificate"));
       
  1982         } catch (CertificateException ce) {
       
  1983             throw new Exception(rb.getString("Input not an X.509 certificate"));
       
  1984         }
       
  1985 
       
  1986         // if certificate is self-signed, make sure it verifies
       
  1987         boolean selfSigned = false;
       
  1988         if (isSelfSigned(cert)) {
       
  1989             cert.verify(cert.getPublicKey());
       
  1990             selfSigned = true;
       
  1991         }
       
  1992 
       
  1993         if (noprompt) {
       
  1994             keyStore.setCertificateEntry(alias, cert);
       
  1995             return true;
       
  1996         }
       
  1997 
       
  1998         // check if cert already exists in keystore
       
  1999         String reply = null;
       
  2000         String trustalias = keyStore.getCertificateAlias(cert);
       
  2001         if (trustalias != null) {
       
  2002             MessageFormat form = new MessageFormat(rb.getString
       
  2003                 ("Certificate already exists in keystore under alias <trustalias>"));
       
  2004             Object[] source = {trustalias};
       
  2005             System.err.println(form.format(source));
       
  2006             reply = getYesNoReply
       
  2007                 (rb.getString("Do you still want to add it? [no]:  "));
       
  2008         } else if (selfSigned) {
       
  2009             if (trustcacerts && (caks != null) &&
       
  2010                     ((trustalias=caks.getCertificateAlias(cert)) != null)) {
       
  2011                 MessageFormat form = new MessageFormat(rb.getString
       
  2012                         ("Certificate already exists in system-wide CA keystore under alias <trustalias>"));
       
  2013                 Object[] source = {trustalias};
       
  2014                 System.err.println(form.format(source));
       
  2015                 reply = getYesNoReply
       
  2016                         (rb.getString("Do you still want to add it to your own keystore? [no]:  "));
       
  2017             }
       
  2018             if (trustalias == null) {
       
  2019                 // Print the cert and ask user if they really want to add
       
  2020                 // it to their keystore
       
  2021                 printX509Cert(cert, System.out);
       
  2022                 reply = getYesNoReply
       
  2023                         (rb.getString("Trust this certificate? [no]:  "));
       
  2024             }
       
  2025         }
       
  2026         if (reply != null) {
       
  2027             if ("YES".equals(reply)) {
       
  2028                 keyStore.setCertificateEntry(alias, cert);
       
  2029                 return true;
       
  2030             } else {
       
  2031                 return false;
       
  2032             }
       
  2033         }
       
  2034 
       
  2035         // Try to establish trust chain
       
  2036         try {
       
  2037             Certificate[] chain = establishCertChain(null, cert);
       
  2038             if (chain != null) {
       
  2039                 keyStore.setCertificateEntry(alias, cert);
       
  2040                 return true;
       
  2041             }
       
  2042         } catch (Exception e) {
       
  2043             // Print the cert and ask user if they really want to add it to
       
  2044             // their keystore
       
  2045             printX509Cert(cert, System.out);
       
  2046             reply = getYesNoReply
       
  2047                 (rb.getString("Trust this certificate? [no]:  "));
       
  2048             if ("YES".equals(reply)) {
       
  2049                 keyStore.setCertificateEntry(alias, cert);
       
  2050                 return true;
       
  2051             } else {
       
  2052                 return false;
       
  2053             }
       
  2054         }
       
  2055 
       
  2056         return false;
       
  2057     }
       
  2058 
       
  2059     /**
       
  2060      * Prompts user for new password. New password must be different from
       
  2061      * old one.
       
  2062      *
       
  2063      * @param prompt the message that gets prompted on the screen
       
  2064      * @param oldPasswd the current (i.e., old) password
       
  2065      */
       
  2066     private char[] getNewPasswd(String prompt, char[] oldPasswd)
       
  2067         throws Exception
       
  2068     {
       
  2069         char[] entered = null;
       
  2070         char[] reentered = null;
       
  2071 
       
  2072         for (int count = 0; count < 3; count++) {
       
  2073             MessageFormat form = new MessageFormat
       
  2074                 (rb.getString("New prompt: "));
       
  2075             Object[] source = {prompt};
       
  2076             System.err.print(form.format(source));
       
  2077             entered = Password.readPassword(System.in);
       
  2078             passwords.add(entered);
       
  2079             if (entered == null || entered.length < 6) {
       
  2080                 System.err.println(rb.getString
       
  2081                     ("Password is too short - must be at least 6 characters"));
       
  2082             } else if (Arrays.equals(entered, oldPasswd)) {
       
  2083                 System.err.println(rb.getString("Passwords must differ"));
       
  2084             } else {
       
  2085                 form = new MessageFormat
       
  2086                         (rb.getString("Re-enter new prompt: "));
       
  2087                 Object[] src = {prompt};
       
  2088                 System.err.print(form.format(src));
       
  2089                 reentered = Password.readPassword(System.in);
       
  2090                 passwords.add(reentered);
       
  2091                 if (!Arrays.equals(entered, reentered)) {
       
  2092                     System.err.println
       
  2093                         (rb.getString("They don't match. Try again"));
       
  2094                 } else {
       
  2095                     Arrays.fill(reentered, ' ');
       
  2096                     return entered;
       
  2097                 }
       
  2098             }
       
  2099             if (entered != null) {
       
  2100                 Arrays.fill(entered, ' ');
       
  2101                 entered = null;
       
  2102             }
       
  2103             if (reentered != null) {
       
  2104                 Arrays.fill(reentered, ' ');
       
  2105                 reentered = null;
       
  2106             }
       
  2107         }
       
  2108         throw new Exception(rb.getString("Too many failures - try later"));
       
  2109     }
       
  2110 
       
  2111     /**
       
  2112      * Prompts user for alias name.
       
  2113      * @param prompt the {0} of "Enter {0} alias name:  " in prompt line
       
  2114      * @returns the string entered by the user, without the \n at the end
       
  2115      */
       
  2116     private String getAlias(String prompt) throws Exception {
       
  2117         if (prompt != null) {
       
  2118             MessageFormat form = new MessageFormat
       
  2119                 (rb.getString("Enter prompt alias name:  "));
       
  2120             Object[] source = {prompt};
       
  2121             System.err.print(form.format(source));
       
  2122         } else {
       
  2123             System.err.print(rb.getString("Enter alias name:  "));
       
  2124         }
       
  2125         return (new BufferedReader(new InputStreamReader(
       
  2126                                         System.in))).readLine();
       
  2127     }
       
  2128 
       
  2129     /**
       
  2130      * Prompts user for an input string from the command line (System.in)
       
  2131      * @prompt the prompt string printed
       
  2132      * @returns the string entered by the user, without the \n at the end
       
  2133      */
       
  2134     private String inputStringFromStdin(String prompt) throws Exception {
       
  2135         System.err.print(prompt);
       
  2136         return (new BufferedReader(new InputStreamReader(
       
  2137                                         System.in))).readLine();
       
  2138     }
       
  2139 
       
  2140     /**
       
  2141      * Prompts user for key password. User may select to choose the same
       
  2142      * password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.
       
  2143      */
       
  2144     private char[] getKeyPasswd(String alias, String otherAlias,
       
  2145                                 char[] otherKeyPass)
       
  2146         throws Exception
       
  2147     {
       
  2148         int count = 0;
       
  2149         char[] keyPass = null;
       
  2150 
       
  2151         do {
       
  2152             if (otherKeyPass != null) {
       
  2153                 MessageFormat form = new MessageFormat(rb.getString
       
  2154                         ("Enter key password for <alias>"));
       
  2155                 Object[] source = {alias};
       
  2156                 System.err.println(form.format(source));
       
  2157 
       
  2158                 form = new MessageFormat(rb.getString
       
  2159                         ("\t(RETURN if same as for <otherAlias>)"));
       
  2160                 Object[] src = {otherAlias};
       
  2161                 System.err.print(form.format(src));
       
  2162             } else {
       
  2163                 MessageFormat form = new MessageFormat(rb.getString
       
  2164                         ("Enter key password for <alias>"));
       
  2165                 Object[] source = {alias};
       
  2166                 System.err.print(form.format(source));
       
  2167             }
       
  2168             System.err.flush();
       
  2169             keyPass = Password.readPassword(System.in);
       
  2170             passwords.add(keyPass);
       
  2171             if (keyPass == null) {
       
  2172                 keyPass = otherKeyPass;
       
  2173             }
       
  2174             count++;
       
  2175         } while ((keyPass == null) && count < 3);
       
  2176 
       
  2177         if (keyPass == null) {
       
  2178             throw new Exception(rb.getString("Too many failures - try later"));
       
  2179         }
       
  2180 
       
  2181         return keyPass;
       
  2182     }
       
  2183 
       
  2184     /**
       
  2185      * Prints a certificate in a human readable format.
       
  2186      */
       
  2187     private void printX509Cert(X509Certificate cert, PrintStream out)
       
  2188         throws Exception
       
  2189     {
       
  2190         /*
       
  2191         out.println("Owner: "
       
  2192                     + cert.getSubjectDN().toString()
       
  2193                     + "\n"
       
  2194                     + "Issuer: "
       
  2195                     + cert.getIssuerDN().toString()
       
  2196                     + "\n"
       
  2197                     + "Serial number: " + cert.getSerialNumber().toString(16)
       
  2198                     + "\n"
       
  2199                     + "Valid from: " + cert.getNotBefore().toString()
       
  2200                     + " until: " + cert.getNotAfter().toString()
       
  2201                     + "\n"
       
  2202                     + "Certificate fingerprints:\n"
       
  2203                     + "\t MD5:  " + getCertFingerPrint("MD5", cert)
       
  2204                     + "\n"
       
  2205                     + "\t SHA1: " + getCertFingerPrint("SHA1", cert));
       
  2206         */
       
  2207 
       
  2208         MessageFormat form = new MessageFormat
       
  2209                 (rb.getString("*PATTERN* printX509Cert"));
       
  2210         Object[] source = {cert.getSubjectDN().toString(),
       
  2211                         cert.getIssuerDN().toString(),
       
  2212                         cert.getSerialNumber().toString(16),
       
  2213                         cert.getNotBefore().toString(),
       
  2214                         cert.getNotAfter().toString(),
       
  2215                         getCertFingerPrint("MD5", cert),
       
  2216                         getCertFingerPrint("SHA1", cert),
       
  2217                         cert.getSigAlgName(),
       
  2218                         cert.getVersion()
       
  2219                         };
       
  2220         out.println(form.format(source));
       
  2221 
       
  2222         int extnum = 0;
       
  2223         if (cert instanceof X509CertImpl) {
       
  2224             X509CertImpl impl = (X509CertImpl)cert;
       
  2225             if (cert.getCriticalExtensionOIDs() != null) {
       
  2226                 for (String extOID : cert.getCriticalExtensionOIDs()) {
       
  2227                     if (extnum == 0) {
       
  2228                         out.println();
       
  2229                         out.println(rb.getString("Extensions: "));
       
  2230                         out.println();
       
  2231                     }
       
  2232                     out.println("#"+(++extnum)+": "+
       
  2233                             impl.getExtension(new ObjectIdentifier(extOID)));
       
  2234                 }
       
  2235             }
       
  2236             if (cert.getNonCriticalExtensionOIDs() != null) {
       
  2237                 for (String extOID : cert.getNonCriticalExtensionOIDs()) {
       
  2238                     if (extnum == 0) {
       
  2239                         out.println();
       
  2240                         out.println(rb.getString("Extensions: "));
       
  2241                         out.println();
       
  2242                     }
       
  2243                     Extension ext = impl.getExtension(new ObjectIdentifier(extOID));
       
  2244                     if (ext != null) {
       
  2245                         out.println("#"+(++extnum)+": "+ ext);
       
  2246                     } else {
       
  2247                         out.println("#"+(++extnum)+": "+
       
  2248                                 impl.getUnparseableExtension(new ObjectIdentifier(extOID)));
       
  2249                     }
       
  2250                 }
       
  2251             }
       
  2252         }
       
  2253     }
       
  2254 
       
  2255     /**
       
  2256      * Returns true if the certificate is self-signed, false otherwise.
       
  2257      */
       
  2258     private boolean isSelfSigned(X509Certificate cert) {
       
  2259         return cert.getSubjectDN().equals(cert.getIssuerDN());
       
  2260     }
       
  2261 
       
  2262     /**
       
  2263      * Returns true if the given certificate is trusted, false otherwise.
       
  2264      */
       
  2265     private boolean isTrusted(Certificate cert)
       
  2266         throws Exception
       
  2267     {
       
  2268         if (keyStore.getCertificateAlias(cert) != null) {
       
  2269             return true; // found in own keystore
       
  2270         }
       
  2271         if (trustcacerts && (caks != null) &&
       
  2272                 (caks.getCertificateAlias(cert) != null)) {
       
  2273             return true; // found in CA keystore
       
  2274         }
       
  2275         return false;
       
  2276     }
       
  2277 
       
  2278     /**
       
  2279      * Gets an X.500 name suitable for inclusion in a certification request.
       
  2280      */
       
  2281     private X500Name getX500Name() throws IOException {
       
  2282         BufferedReader in;
       
  2283         in = new BufferedReader(new InputStreamReader(System.in));
       
  2284         String commonName = "Unknown";
       
  2285         String organizationalUnit = "Unknown";
       
  2286         String organization = "Unknown";
       
  2287         String city = "Unknown";
       
  2288         String state = "Unknown";
       
  2289         String country = "Unknown";
       
  2290         X500Name name;
       
  2291         String userInput = null;
       
  2292 
       
  2293         int maxRetry = 20;
       
  2294         do {
       
  2295             if (maxRetry-- < 0) {
       
  2296                 throw new RuntimeException(rb.getString(
       
  2297                         "Too may retries, program terminated"));
       
  2298             }
       
  2299             commonName = inputString(in,
       
  2300                     rb.getString("What is your first and last name?"),
       
  2301                     commonName);
       
  2302             organizationalUnit = inputString(in,
       
  2303                     rb.getString
       
  2304                         ("What is the name of your organizational unit?"),
       
  2305                     organizationalUnit);
       
  2306             organization = inputString(in,
       
  2307                     rb.getString("What is the name of your organization?"),
       
  2308                     organization);
       
  2309             city = inputString(in,
       
  2310                     rb.getString("What is the name of your City or Locality?"),
       
  2311                     city);
       
  2312             state = inputString(in,
       
  2313                     rb.getString("What is the name of your State or Province?"),
       
  2314                     state);
       
  2315             country = inputString(in,
       
  2316                     rb.getString
       
  2317                         ("What is the two-letter country code for this unit?"),
       
  2318                     country);
       
  2319             name = new X500Name(commonName, organizationalUnit, organization,
       
  2320                                 city, state, country);
       
  2321             MessageFormat form = new MessageFormat
       
  2322                 (rb.getString("Is <name> correct?"));
       
  2323             Object[] source = {name};
       
  2324             userInput = inputString
       
  2325                 (in, form.format(source), rb.getString("no"));
       
  2326         } while (collator.compare(userInput, rb.getString("yes")) != 0 &&
       
  2327                  collator.compare(userInput, rb.getString("y")) != 0);
       
  2328 
       
  2329         System.err.println();
       
  2330         return name;
       
  2331     }
       
  2332 
       
  2333     private String inputString(BufferedReader in, String prompt,
       
  2334                                String defaultValue)
       
  2335         throws IOException
       
  2336     {
       
  2337         System.err.println(prompt);
       
  2338         MessageFormat form = new MessageFormat
       
  2339                 (rb.getString("  [defaultValue]:  "));
       
  2340         Object[] source = {defaultValue};
       
  2341         System.err.print(form.format(source));
       
  2342         System.err.flush();
       
  2343 
       
  2344         String value = in.readLine();
       
  2345         if (value == null || collator.compare(value, "") == 0) {
       
  2346             value = defaultValue;
       
  2347         }
       
  2348         return value;
       
  2349     }
       
  2350 
       
  2351     /**
       
  2352      * Writes an X.509 certificate in base64 or binary encoding to an output
       
  2353      * stream.
       
  2354      */
       
  2355     private void dumpCert(Certificate cert, PrintStream out)
       
  2356         throws IOException, CertificateException
       
  2357     {
       
  2358         if (rfc) {
       
  2359             BASE64Encoder encoder = new BASE64Encoder();
       
  2360             out.println(X509Factory.BEGIN_CERT);
       
  2361             encoder.encodeBuffer(cert.getEncoded(), out);
       
  2362             out.println(X509Factory.END_CERT);
       
  2363         } else {
       
  2364             out.write(cert.getEncoded()); // binary
       
  2365         }
       
  2366     }
       
  2367 
       
  2368     /**
       
  2369      * Converts a byte to hex digit and writes to the supplied buffer
       
  2370      */
       
  2371     private void byte2hex(byte b, StringBuffer buf) {
       
  2372         char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
       
  2373                             '9', 'A', 'B', 'C', 'D', 'E', 'F' };
       
  2374         int high = ((b & 0xf0) >> 4);
       
  2375         int low = (b & 0x0f);
       
  2376         buf.append(hexChars[high]);
       
  2377         buf.append(hexChars[low]);
       
  2378     }
       
  2379 
       
  2380     /**
       
  2381      * Converts a byte array to hex string
       
  2382      */
       
  2383     private String toHexString(byte[] block) {
       
  2384         StringBuffer buf = new StringBuffer();
       
  2385         int len = block.length;
       
  2386         for (int i = 0; i < len; i++) {
       
  2387              byte2hex(block[i], buf);
       
  2388              if (i < len-1) {
       
  2389                  buf.append(":");
       
  2390              }
       
  2391         }
       
  2392         return buf.toString();
       
  2393     }
       
  2394 
       
  2395     /**
       
  2396      * Recovers (private) key associated with given alias.
       
  2397      *
       
  2398      * @return an array of objects, where the 1st element in the array is the
       
  2399      * recovered private key, and the 2nd element is the password used to
       
  2400      * recover it.
       
  2401      */
       
  2402     private Object[] recoverKey(String alias, char[] storePass,
       
  2403                                        char[] keyPass)
       
  2404         throws Exception
       
  2405     {
       
  2406         Key key = null;
       
  2407 
       
  2408         if (keyStore.containsAlias(alias) == false) {
       
  2409             MessageFormat form = new MessageFormat
       
  2410                 (rb.getString("Alias <alias> does not exist"));
       
  2411             Object[] source = {alias};
       
  2412             throw new Exception(form.format(source));
       
  2413         }
       
  2414         if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&
       
  2415                 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
       
  2416             MessageFormat form = new MessageFormat
       
  2417                 (rb.getString("Alias <alias> has no key"));
       
  2418             Object[] source = {alias};
       
  2419             throw new Exception(form.format(source));
       
  2420         }
       
  2421 
       
  2422         if (keyPass == null) {
       
  2423             // Try to recover the key using the keystore password
       
  2424             try {
       
  2425                 key = keyStore.getKey(alias, storePass);
       
  2426 
       
  2427                 keyPass = storePass;
       
  2428                 passwords.add(keyPass);
       
  2429             } catch (UnrecoverableKeyException e) {
       
  2430                 // Did not work out, so prompt user for key password
       
  2431                 if (!token) {
       
  2432                     keyPass = getKeyPasswd(alias, null, null);
       
  2433                     key = keyStore.getKey(alias, keyPass);
       
  2434                 } else {
       
  2435                     throw e;
       
  2436                 }
       
  2437             }
       
  2438         } else {
       
  2439             key = keyStore.getKey(alias, keyPass);
       
  2440         }
       
  2441 
       
  2442         return new Object[] {key, keyPass};
       
  2443     }
       
  2444 
       
  2445     /**
       
  2446      * Recovers entry associated with given alias.
       
  2447      *
       
  2448      * @return an array of objects, where the 1st element in the array is the
       
  2449      * recovered entry, and the 2nd element is the password used to
       
  2450      * recover it (null if no password).
       
  2451      */
       
  2452     private Object[] recoverEntry(KeyStore ks,
       
  2453                             String alias,
       
  2454                             char[] pstore,
       
  2455                             char[] pkey) throws Exception {
       
  2456 
       
  2457         if (ks.containsAlias(alias) == false) {
       
  2458             MessageFormat form = new MessageFormat
       
  2459                 (rb.getString("Alias <alias> does not exist"));
       
  2460             Object[] source = {alias};
       
  2461             throw new Exception(form.format(source));
       
  2462         }
       
  2463 
       
  2464         PasswordProtection pp = null;
       
  2465         Entry entry;
       
  2466 
       
  2467         try {
       
  2468             // First attempt to access entry without key password
       
  2469             // (PKCS11 entry or trusted certificate entry, for example)
       
  2470 
       
  2471             entry = ks.getEntry(alias, pp);
       
  2472             pkey = null;
       
  2473         } catch (UnrecoverableEntryException une) {
       
  2474 
       
  2475             if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
       
  2476                 KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
       
  2477                 // should not happen, but a possibility
       
  2478                 throw une;
       
  2479             }
       
  2480 
       
  2481             // entry is protected
       
  2482 
       
  2483             if (pkey != null) {
       
  2484 
       
  2485                 // try provided key password
       
  2486 
       
  2487                 pp = new PasswordProtection(pkey);
       
  2488                 entry = ks.getEntry(alias, pp);
       
  2489 
       
  2490             } else {
       
  2491 
       
  2492                 // try store pass
       
  2493 
       
  2494                 try {
       
  2495                     pp = new PasswordProtection(pstore);
       
  2496                     entry = ks.getEntry(alias, pp);
       
  2497                     pkey = pstore;
       
  2498                 } catch (UnrecoverableEntryException une2) {
       
  2499                     if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
       
  2500 
       
  2501                         // P12 keystore currently does not support separate
       
  2502                         // store and entry passwords
       
  2503 
       
  2504                         throw une2;
       
  2505                     } else {
       
  2506 
       
  2507                         // prompt for entry password
       
  2508 
       
  2509                         pkey = getKeyPasswd(alias, null, null);
       
  2510                         pp = new PasswordProtection(pkey);
       
  2511                         entry = ks.getEntry(alias, pp);
       
  2512                     }
       
  2513                 }
       
  2514             }
       
  2515         }
       
  2516 
       
  2517         return new Object[] {entry, pkey};
       
  2518     }
       
  2519     /**
       
  2520      * Gets the requested finger print of the certificate.
       
  2521      */
       
  2522     private String getCertFingerPrint(String mdAlg, Certificate cert)
       
  2523         throws Exception
       
  2524     {
       
  2525         byte[] encCertInfo = cert.getEncoded();
       
  2526         MessageDigest md = MessageDigest.getInstance(mdAlg);
       
  2527         byte[] digest = md.digest(encCertInfo);
       
  2528         return toHexString(digest);
       
  2529     }
       
  2530 
       
  2531     /**
       
  2532      * Prints warning about missing integrity check.
       
  2533      */
       
  2534     private void printWarning() {
       
  2535         System.err.println();
       
  2536         System.err.println(rb.getString
       
  2537             ("*****************  WARNING WARNING WARNING  *****************"));
       
  2538         System.err.println(rb.getString
       
  2539             ("* The integrity of the information stored in your keystore  *"));
       
  2540         System.err.println(rb.getString
       
  2541             ("* has NOT been verified!  In order to verify its integrity, *"));
       
  2542         System.err.println(rb.getString
       
  2543             ("* you must provide your keystore password.                  *"));
       
  2544         System.err.println(rb.getString
       
  2545             ("*****************  WARNING WARNING WARNING  *****************"));
       
  2546         System.err.println();
       
  2547     }
       
  2548 
       
  2549     /**
       
  2550      * Validates chain in certification reply, and returns the ordered
       
  2551      * elements of the chain (with user certificate first, and root
       
  2552      * certificate last in the array).
       
  2553      *
       
  2554      * @param alias the alias name
       
  2555      * @param userCert the user certificate of the alias
       
  2556      * @param replyCerts the chain provided in the reply
       
  2557      */
       
  2558     private Certificate[] validateReply(String alias,
       
  2559                                         Certificate userCert,
       
  2560                                         Certificate[] replyCerts)
       
  2561         throws Exception
       
  2562     {
       
  2563         // order the certs in the reply (bottom-up).
       
  2564         // we know that all certs in the reply are of type X.509, because
       
  2565         // we parsed them using an X.509 certificate factory
       
  2566         int i;
       
  2567         PublicKey userPubKey = userCert.getPublicKey();
       
  2568         for (i=0; i<replyCerts.length; i++) {
       
  2569             if (userPubKey.equals(replyCerts[i].getPublicKey())) {
       
  2570                 break;
       
  2571             }
       
  2572         }
       
  2573         if (i == replyCerts.length) {
       
  2574             MessageFormat form = new MessageFormat(rb.getString
       
  2575                 ("Certificate reply does not contain public key for <alias>"));
       
  2576             Object[] source = {alias};
       
  2577             throw new Exception(form.format(source));
       
  2578         }
       
  2579 
       
  2580         Certificate tmpCert = replyCerts[0];
       
  2581         replyCerts[0] = replyCerts[i];
       
  2582         replyCerts[i] = tmpCert;
       
  2583         Principal issuer = ((X509Certificate)replyCerts[0]).getIssuerDN();
       
  2584 
       
  2585         for (i=1; i < replyCerts.length-1; i++) {
       
  2586             // find a cert in the reply whose "subject" is the same as the
       
  2587             // given "issuer"
       
  2588             int j;
       
  2589             for (j=i; j<replyCerts.length; j++) {
       
  2590                 Principal subject;
       
  2591                 subject = ((X509Certificate)replyCerts[j]).getSubjectDN();
       
  2592                 if (subject.equals(issuer)) {
       
  2593                     tmpCert = replyCerts[i];
       
  2594                     replyCerts[i] = replyCerts[j];
       
  2595                     replyCerts[j] = tmpCert;
       
  2596                     issuer = ((X509Certificate)replyCerts[i]).getIssuerDN();
       
  2597                     break;
       
  2598                 }
       
  2599             }
       
  2600             if (j == replyCerts.length) {
       
  2601                 throw new Exception
       
  2602                     (rb.getString("Incomplete certificate chain in reply"));
       
  2603             }
       
  2604         }
       
  2605 
       
  2606         // now verify each cert in the ordered chain
       
  2607         for (i=0; i<replyCerts.length-1; i++) {
       
  2608             PublicKey pubKey = replyCerts[i+1].getPublicKey();
       
  2609             try {
       
  2610                 replyCerts[i].verify(pubKey);
       
  2611             } catch (Exception e) {
       
  2612                 throw new Exception(rb.getString
       
  2613                         ("Certificate chain in reply does not verify: ") +
       
  2614                         e.getMessage());
       
  2615             }
       
  2616         }
       
  2617 
       
  2618         if (noprompt) {
       
  2619             return replyCerts;
       
  2620         }
       
  2621 
       
  2622         // do we trust the (root) cert at the top?
       
  2623         Certificate topCert = replyCerts[replyCerts.length-1];
       
  2624         if (!isTrusted(topCert)) {
       
  2625             boolean verified = false;
       
  2626             Certificate rootCert = null;
       
  2627             if (trustcacerts && (caks!= null)) {
       
  2628                 for (Enumeration<String> aliases = caks.aliases();
       
  2629                      aliases.hasMoreElements(); ) {
       
  2630                     String name = aliases.nextElement();
       
  2631                     rootCert = caks.getCertificate(name);
       
  2632                     if (rootCert != null) {
       
  2633                         try {
       
  2634                             topCert.verify(rootCert.getPublicKey());
       
  2635                             verified = true;
       
  2636                             break;
       
  2637                         } catch (Exception e) {
       
  2638                         }
       
  2639                     }
       
  2640                 }
       
  2641             }
       
  2642             if (!verified) {
       
  2643                 System.err.println();
       
  2644                 System.err.println
       
  2645                         (rb.getString("Top-level certificate in reply:\n"));
       
  2646                 printX509Cert((X509Certificate)topCert, System.out);
       
  2647                 System.err.println();
       
  2648                 System.err.print(rb.getString("... is not trusted. "));
       
  2649                 String reply = getYesNoReply
       
  2650                         (rb.getString("Install reply anyway? [no]:  "));
       
  2651                 if ("NO".equals(reply)) {
       
  2652                     return null;
       
  2653                 }
       
  2654             } else {
       
  2655                 if (!isSelfSigned((X509Certificate)topCert)) {
       
  2656                     // append the (self-signed) root CA cert to the chain
       
  2657                     Certificate[] tmpCerts =
       
  2658                         new Certificate[replyCerts.length+1];
       
  2659                     System.arraycopy(replyCerts, 0, tmpCerts, 0,
       
  2660                                      replyCerts.length);
       
  2661                     tmpCerts[tmpCerts.length-1] = rootCert;
       
  2662                     replyCerts = tmpCerts;
       
  2663                 }
       
  2664             }
       
  2665         }
       
  2666 
       
  2667         return replyCerts;
       
  2668     }
       
  2669 
       
  2670     /**
       
  2671      * Establishes a certificate chain (using trusted certificates in the
       
  2672      * keystore), starting with the user certificate
       
  2673      * and ending at a self-signed certificate found in the keystore.
       
  2674      *
       
  2675      * @param userCert the user certificate of the alias
       
  2676      * @param certToVerify the single certificate provided in the reply
       
  2677      */
       
  2678     private Certificate[] establishCertChain(Certificate userCert,
       
  2679                                              Certificate certToVerify)
       
  2680         throws Exception
       
  2681     {
       
  2682         if (userCert != null) {
       
  2683             // Make sure that the public key of the certificate reply matches
       
  2684             // the original public key in the keystore
       
  2685             PublicKey origPubKey = userCert.getPublicKey();
       
  2686             PublicKey replyPubKey = certToVerify.getPublicKey();
       
  2687             if (!origPubKey.equals(replyPubKey)) {
       
  2688                 throw new Exception(rb.getString
       
  2689                         ("Public keys in reply and keystore don't match"));
       
  2690             }
       
  2691 
       
  2692             // If the two certs are identical, we're done: no need to import
       
  2693             // anything
       
  2694             if (certToVerify.equals(userCert)) {
       
  2695                 throw new Exception(rb.getString
       
  2696                         ("Certificate reply and certificate in keystore are identical"));
       
  2697             }
       
  2698         }
       
  2699 
       
  2700         // Build a hash table of all certificates in the keystore.
       
  2701         // Use the subject distinguished name as the key into the hash table.
       
  2702         // All certificates associated with the same subject distinguished
       
  2703         // name are stored in the same hash table entry as a vector.
       
  2704         Hashtable<Principal, Vector<Certificate>> certs = null;
       
  2705         if (keyStore.size() > 0) {
       
  2706             certs = new Hashtable<Principal, Vector<Certificate>>(11);
       
  2707             keystorecerts2Hashtable(keyStore, certs);
       
  2708         }
       
  2709         if (trustcacerts) {
       
  2710             if (caks!=null && caks.size()>0) {
       
  2711                 if (certs == null) {
       
  2712                     certs = new Hashtable<Principal, Vector<Certificate>>(11);
       
  2713                 }
       
  2714                 keystorecerts2Hashtable(caks, certs);
       
  2715             }
       
  2716         }
       
  2717 
       
  2718         // start building chain
       
  2719         Vector<Certificate> chain = new Vector<Certificate>(2);
       
  2720         if (buildChain((X509Certificate)certToVerify, chain, certs)) {
       
  2721             Certificate[] newChain = new Certificate[chain.size()];
       
  2722             // buildChain() returns chain with self-signed root-cert first and
       
  2723             // user-cert last, so we need to invert the chain before we store
       
  2724             // it
       
  2725             int j=0;
       
  2726             for (int i=chain.size()-1; i>=0; i--) {
       
  2727                 newChain[j] = chain.elementAt(i);
       
  2728                 j++;
       
  2729             }
       
  2730             return newChain;
       
  2731         } else {
       
  2732             throw new Exception
       
  2733                 (rb.getString("Failed to establish chain from reply"));
       
  2734         }
       
  2735     }
       
  2736 
       
  2737     /**
       
  2738      * Recursively tries to establish chain from pool of trusted certs.
       
  2739      *
       
  2740      * @param certToVerify the cert that needs to be verified.
       
  2741      * @param chain the chain that's being built.
       
  2742      * @param certs the pool of trusted certs
       
  2743      *
       
  2744      * @return true if successful, false otherwise.
       
  2745      */
       
  2746     private boolean buildChain(X509Certificate certToVerify,
       
  2747                         Vector<Certificate> chain,
       
  2748                         Hashtable<Principal, Vector<Certificate>> certs) {
       
  2749         Principal subject = certToVerify.getSubjectDN();
       
  2750         Principal issuer = certToVerify.getIssuerDN();
       
  2751         if (subject.equals(issuer)) {
       
  2752             // reached self-signed root cert;
       
  2753             // no verification needed because it's trusted.
       
  2754             chain.addElement(certToVerify);
       
  2755             return true;
       
  2756         }
       
  2757 
       
  2758         // Get the issuer's certificate(s)
       
  2759         Vector<Certificate> vec = certs.get(issuer);
       
  2760         if (vec == null) {
       
  2761             return false;
       
  2762         }
       
  2763 
       
  2764         // Try out each certificate in the vector, until we find one
       
  2765         // whose public key verifies the signature of the certificate
       
  2766         // in question.
       
  2767         for (Enumeration<Certificate> issuerCerts = vec.elements();
       
  2768              issuerCerts.hasMoreElements(); ) {
       
  2769             X509Certificate issuerCert
       
  2770                 = (X509Certificate)issuerCerts.nextElement();
       
  2771             PublicKey issuerPubKey = issuerCert.getPublicKey();
       
  2772             try {
       
  2773                 certToVerify.verify(issuerPubKey);
       
  2774             } catch (Exception e) {
       
  2775                 continue;
       
  2776             }
       
  2777             if (buildChain(issuerCert, chain, certs)) {
       
  2778                 chain.addElement(certToVerify);
       
  2779                 return true;
       
  2780             }
       
  2781         }
       
  2782         return false;
       
  2783     }
       
  2784 
       
  2785     /**
       
  2786      * Prompts user for yes/no decision.
       
  2787      *
       
  2788      * @return the user's decision, can only be "YES" or "NO"
       
  2789      */
       
  2790     private String getYesNoReply(String prompt)
       
  2791         throws IOException
       
  2792     {
       
  2793         String reply = null;
       
  2794         int maxRetry = 20;
       
  2795         do {
       
  2796             if (maxRetry-- < 0) {
       
  2797                 throw new RuntimeException(rb.getString(
       
  2798                         "Too may retries, program terminated"));
       
  2799             }
       
  2800             System.err.print(prompt);
       
  2801             System.err.flush();
       
  2802             reply = (new BufferedReader(new InputStreamReader
       
  2803                                         (System.in))).readLine();
       
  2804             if (collator.compare(reply, "") == 0 ||
       
  2805                 collator.compare(reply, rb.getString("n")) == 0 ||
       
  2806                 collator.compare(reply, rb.getString("no")) == 0) {
       
  2807                 reply = "NO";
       
  2808             } else if (collator.compare(reply, rb.getString("y")) == 0 ||
       
  2809                        collator.compare(reply, rb.getString("yes")) == 0) {
       
  2810                 reply = "YES";
       
  2811             } else {
       
  2812                 System.err.println(rb.getString("Wrong answer, try again"));
       
  2813                 reply = null;
       
  2814             }
       
  2815         } while (reply == null);
       
  2816         return reply;
       
  2817     }
       
  2818 
       
  2819     /**
       
  2820      * Returns the keystore with the configured CA certificates.
       
  2821      */
       
  2822     private KeyStore getCacertsKeyStore()
       
  2823         throws Exception
       
  2824     {
       
  2825         String sep = File.separator;
       
  2826         File file = new File(System.getProperty("java.home") + sep
       
  2827                              + "lib" + sep + "security" + sep
       
  2828                              + "cacerts");
       
  2829         if (!file.exists()) {
       
  2830             return null;
       
  2831         }
       
  2832         FileInputStream fis = null;
       
  2833         KeyStore caks = null;
       
  2834         try {
       
  2835             fis = new FileInputStream(file);
       
  2836             caks = KeyStore.getInstance(JKS);
       
  2837             caks.load(fis, null);
       
  2838         } finally {
       
  2839             if (fis != null) {
       
  2840                 fis.close();
       
  2841             }
       
  2842         }
       
  2843         return caks;
       
  2844     }
       
  2845 
       
  2846     /**
       
  2847      * Stores the (leaf) certificates of a keystore in a hashtable.
       
  2848      * All certs belonging to the same CA are stored in a vector that
       
  2849      * in turn is stored in the hashtable, keyed by the CA's subject DN
       
  2850      */
       
  2851     private void keystorecerts2Hashtable(KeyStore ks,
       
  2852                 Hashtable<Principal, Vector<Certificate>> hash)
       
  2853         throws Exception {
       
  2854 
       
  2855         for (Enumeration<String> aliases = ks.aliases();
       
  2856                                         aliases.hasMoreElements(); ) {
       
  2857             String alias = aliases.nextElement();
       
  2858             Certificate cert = ks.getCertificate(alias);
       
  2859             if (cert != null) {
       
  2860                 Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
       
  2861                 Vector<Certificate> vec = hash.get(subjectDN);
       
  2862                 if (vec == null) {
       
  2863                     vec = new Vector<Certificate>();
       
  2864                     vec.addElement(cert);
       
  2865                 } else {
       
  2866                     if (!vec.contains(cert)) {
       
  2867                         vec.addElement(cert);
       
  2868                     }
       
  2869                 }
       
  2870                 hash.put(subjectDN, vec);
       
  2871             }
       
  2872         }
       
  2873     }
       
  2874 
       
  2875     /**
       
  2876      * Returns the issue time that's specified the -startdate option
       
  2877      * @param s the value of -startdate option
       
  2878      */
       
  2879     private static Date getStartDate(String s) throws IOException {
       
  2880         Calendar c = new GregorianCalendar();
       
  2881         if (s != null) {
       
  2882             IOException ioe = new IOException(
       
  2883                     rb.getString("Illegal startdate value"));
       
  2884             int len = s.length();
       
  2885             if (len == 0) {
       
  2886                 throw ioe;
       
  2887             }
       
  2888             if (s.charAt(0) == '-' || s.charAt(0) == '+') {
       
  2889                 // Form 1: ([+-]nnn[ymdHMS])+
       
  2890                 int start = 0;
       
  2891                 while (start < len) {
       
  2892                     int sign = 0;
       
  2893                     switch (s.charAt(start)) {
       
  2894                         case '+': sign = 1; break;
       
  2895                         case '-': sign = -1; break;
       
  2896                         default: throw ioe;
       
  2897                     }
       
  2898                     int i = start+1;
       
  2899                     for (; i<len; i++) {
       
  2900                         char ch = s.charAt(i);
       
  2901                         if (ch < '0' || ch > '9') break;
       
  2902                     }
       
  2903                     if (i == start+1) throw ioe;
       
  2904                     int number = Integer.parseInt(s.substring(start+1, i));
       
  2905                     if (i >= len) throw ioe;
       
  2906                     int unit = 0;
       
  2907                     switch (s.charAt(i)) {
       
  2908                         case 'y': unit = Calendar.YEAR; break;
       
  2909                         case 'm': unit = Calendar.MONTH; break;
       
  2910                         case 'd': unit = Calendar.DATE; break;
       
  2911                         case 'H': unit = Calendar.HOUR; break;
       
  2912                         case 'M': unit = Calendar.MINUTE; break;
       
  2913                         case 'S': unit = Calendar.SECOND; break;
       
  2914                         default: throw ioe;
       
  2915                     }
       
  2916                     c.add(unit, sign * number);
       
  2917                     start = i + 1;
       
  2918                 }
       
  2919             } else  {
       
  2920                 // Form 2: [yyyy/mm/dd] [HH:MM:SS]
       
  2921                 String date = null, time = null;
       
  2922                 if (len == 19) {
       
  2923                     date = s.substring(0, 10);
       
  2924                     time = s.substring(11);
       
  2925                     if (s.charAt(10) != ' ')
       
  2926                         throw ioe;
       
  2927                 } else if (len == 10) {
       
  2928                     date = s;
       
  2929                 } else if (len == 8) {
       
  2930                     time = s;
       
  2931                 } else {
       
  2932                     throw ioe;
       
  2933                 }
       
  2934                 if (date != null) {
       
  2935                     if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {
       
  2936                         c.set(Integer.valueOf(date.substring(0, 4)),
       
  2937                                 Integer.valueOf(date.substring(5, 7))-1,
       
  2938                                 Integer.valueOf(date.substring(8, 10)));
       
  2939                     } else {
       
  2940                         throw ioe;
       
  2941                     }
       
  2942                 }
       
  2943                 if (time != null) {
       
  2944                     if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {
       
  2945                         c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));
       
  2946                         c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2)));
       
  2947                         c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2)));
       
  2948                         c.set(Calendar.MILLISECOND, 0);
       
  2949                     } else {
       
  2950                         throw ioe;
       
  2951                     }
       
  2952                 }
       
  2953             }
       
  2954         }
       
  2955         return c.getTime();
       
  2956     }
       
  2957 
       
  2958     /**
       
  2959      * Prints the usage of this tool.
       
  2960      */
       
  2961     private void usage() {
       
  2962         System.err.println(rb.getString("keytool usage:\n"));
       
  2963 
       
  2964         System.err.println(rb.getString
       
  2965                 ("-certreq     [-v] [-protected]"));
       
  2966         System.err.println(rb.getString
       
  2967                 ("\t     [-alias <alias>] [-sigalg <sigalg>]"));
       
  2968         System.err.println(rb.getString
       
  2969                 ("\t     [-file <csr_file>] [-keypass <keypass>]"));
       
  2970         System.err.println(rb.getString
       
  2971                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  2972         System.err.println(rb.getString
       
  2973                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  2974         System.err.println(rb.getString
       
  2975                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  2976         System.err.println(rb.getString
       
  2977                 ("\t     [-providerpath <pathlist>]"));
       
  2978         System.err.println();
       
  2979 
       
  2980         System.err.println(rb.getString
       
  2981                 ("-changealias [-v] [-protected] -alias <alias> -destalias <destalias>"));
       
  2982         System.err.println(rb.getString
       
  2983                 ("\t     [-keypass <keypass>]"));
       
  2984         System.err.println(rb.getString
       
  2985                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  2986         System.err.println(rb.getString
       
  2987                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  2988         System.err.println(rb.getString
       
  2989                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  2990         System.err.println(rb.getString
       
  2991                 ("\t     [-providerpath <pathlist>]"));
       
  2992         System.err.println();
       
  2993 
       
  2994         System.err.println(rb.getString
       
  2995                 ("-delete      [-v] [-protected] -alias <alias>"));
       
  2996         System.err.println(rb.getString
       
  2997                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  2998         System.err.println(rb.getString
       
  2999                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3000         System.err.println(rb.getString
       
  3001                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3002         System.err.println(rb.getString
       
  3003                 ("\t     [-providerpath <pathlist>]"));
       
  3004         System.err.println();
       
  3005 
       
  3006         System.err.println(rb.getString
       
  3007                 ("-exportcert  [-v] [-rfc] [-protected]"));
       
  3008         System.err.println(rb.getString
       
  3009                 ("\t     [-alias <alias>] [-file <cert_file>]"));
       
  3010         System.err.println(rb.getString
       
  3011                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3012         System.err.println(rb.getString
       
  3013                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3014         System.err.println(rb.getString
       
  3015                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3016         System.err.println(rb.getString
       
  3017                 ("\t     [-providerpath <pathlist>]"));
       
  3018         System.err.println();
       
  3019 
       
  3020         System.err.println(rb.getString
       
  3021                 ("-genkeypair  [-v] [-protected]"));
       
  3022         System.err.println(rb.getString
       
  3023                 ("\t     [-alias <alias>]"));
       
  3024         System.err.println(rb.getString
       
  3025                 ("\t     [-keyalg <keyalg>] [-keysize <keysize>]"));
       
  3026         System.err.println(rb.getString
       
  3027                 ("\t     [-sigalg <sigalg>] [-dname <dname>]"));
       
  3028         System.err.println(rb.getString
       
  3029                 ("\t     [-startdate <startdate>]"));
       
  3030         System.err.println(rb.getString
       
  3031                 ("\t     [-validity <valDays>] [-keypass <keypass>]"));
       
  3032         System.err.println(rb.getString
       
  3033                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3034         System.err.println(rb.getString
       
  3035                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3036         System.err.println(rb.getString
       
  3037                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3038         System.err.println(rb.getString
       
  3039                 ("\t     [-providerpath <pathlist>]"));
       
  3040         System.err.println();
       
  3041 
       
  3042         System.err.println(rb.getString
       
  3043                 ("-genseckey   [-v] [-protected]"));
       
  3044         System.err.println(rb.getString
       
  3045                 ("\t     [-alias <alias>] [-keypass <keypass>]"));
       
  3046         System.err.println(rb.getString
       
  3047                 ("\t     [-keyalg <keyalg>] [-keysize <keysize>]"));
       
  3048         System.err.println(rb.getString
       
  3049                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3050         System.err.println(rb.getString
       
  3051                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3052         System.err.println(rb.getString
       
  3053                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3054         System.err.println(rb.getString
       
  3055                 ("\t     [-providerpath <pathlist>]"));
       
  3056         System.err.println();
       
  3057 
       
  3058         System.err.println(rb.getString("-help"));
       
  3059         System.err.println();
       
  3060 
       
  3061         System.err.println(rb.getString
       
  3062                 ("-importcert  [-v] [-noprompt] [-trustcacerts] [-protected]"));
       
  3063         System.err.println(rb.getString
       
  3064                 ("\t     [-alias <alias>]"));
       
  3065         System.err.println(rb.getString
       
  3066                 ("\t     [-file <cert_file>] [-keypass <keypass>]"));
       
  3067         System.err.println(rb.getString
       
  3068                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3069         System.err.println(rb.getString
       
  3070                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3071         System.err.println(rb.getString
       
  3072                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3073         System.err.println(rb.getString
       
  3074                 ("\t     [-providerpath <pathlist>]"));
       
  3075         System.err.println();
       
  3076 
       
  3077         System.err.println(rb.getString
       
  3078                 ("-importkeystore [-v] "));
       
  3079         System.err.println(rb.getString
       
  3080                 ("\t     [-srckeystore <srckeystore>] [-destkeystore <destkeystore>]"));
       
  3081         System.err.println(rb.getString
       
  3082                 ("\t     [-srcstoretype <srcstoretype>] [-deststoretype <deststoretype>]"));
       
  3083         System.err.println(rb.getString
       
  3084                 ("\t     [-srcstorepass <srcstorepass>] [-deststorepass <deststorepass>]"));
       
  3085         System.err.println(rb.getString
       
  3086                 ("\t     [-srcprotected] [-destprotected]"));
       
  3087         System.err.println(rb.getString
       
  3088                 ("\t     [-srcprovidername <srcprovidername>]\n\t     [-destprovidername <destprovidername>]"));
       
  3089         System.err.println(rb.getString
       
  3090                 ("\t     [-srcalias <srcalias> [-destalias <destalias>]"));
       
  3091         System.err.println(rb.getString
       
  3092                 ("\t       [-srckeypass <srckeypass>] [-destkeypass <destkeypass>]]"));
       
  3093         System.err.println(rb.getString
       
  3094                 ("\t     [-noprompt]"));
       
  3095         System.err.println(rb.getString
       
  3096                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3097         System.err.println(rb.getString
       
  3098                 ("\t     [-providerpath <pathlist>]"));
       
  3099         System.err.println();
       
  3100 
       
  3101         System.err.println(rb.getString
       
  3102                 ("-keypasswd   [-v] [-alias <alias>]"));
       
  3103         System.err.println(rb.getString
       
  3104                 ("\t     [-keypass <old_keypass>] [-new <new_keypass>]"));
       
  3105         System.err.println(rb.getString
       
  3106                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3107         System.err.println(rb.getString
       
  3108                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3109         System.err.println(rb.getString
       
  3110                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3111         System.err.println(rb.getString
       
  3112                 ("\t     [-providerpath <pathlist>]"));
       
  3113         System.err.println();
       
  3114 
       
  3115         System.err.println(rb.getString
       
  3116                 ("-list        [-v | -rfc] [-protected]"));
       
  3117         System.err.println(rb.getString
       
  3118                 ("\t     [-alias <alias>]"));
       
  3119         System.err.println(rb.getString
       
  3120                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3121         System.err.println(rb.getString
       
  3122                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3123         System.err.println(rb.getString
       
  3124                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3125         System.err.println(rb.getString
       
  3126                 ("\t     [-providerpath <pathlist>]"));
       
  3127         System.err.println();
       
  3128 
       
  3129         System.err.println(rb.getString
       
  3130                 ("-printcert   [-v] [-file <cert_file>]"));
       
  3131         System.err.println();
       
  3132 
       
  3133         System.err.println(rb.getString
       
  3134                 ("-storepasswd [-v] [-new <new_storepass>]"));
       
  3135         System.err.println(rb.getString
       
  3136                 ("\t     [-keystore <keystore>] [-storepass <storepass>]"));
       
  3137         System.err.println(rb.getString
       
  3138                 ("\t     [-storetype <storetype>] [-providername <name>]"));
       
  3139         System.err.println(rb.getString
       
  3140                 ("\t     [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
       
  3141         System.err.println(rb.getString
       
  3142                 ("\t     [-providerpath <pathlist>]"));
       
  3143 
       
  3144         if (debug) {
       
  3145             throw new RuntimeException("NO ERROR, SORRY");
       
  3146         } else {
       
  3147             System.exit(1);
       
  3148         }
       
  3149     }
       
  3150 
       
  3151     private void tinyHelp() {
       
  3152         System.err.println(rb.getString("Try keytool -help"));
       
  3153 
       
  3154         // do not drown user with the help lines.
       
  3155         if (debug) {
       
  3156             throw new RuntimeException("NO BIG ERROR, SORRY");
       
  3157         } else {
       
  3158             System.exit(1);
       
  3159         }
       
  3160     }
       
  3161 
       
  3162     private void errorNeedArgument(String flag) {
       
  3163         Object[] source = {flag};
       
  3164         System.err.println(new MessageFormat(
       
  3165                 rb.getString("Command option <flag> needs an argument.")).format(source));
       
  3166         tinyHelp();
       
  3167     }
       
  3168 }
       
  3169 
       
  3170 // This class is exactly the same as com.sun.tools.javac.util.Pair,
       
  3171 // it's copied here since the original one is not included in JRE.
       
  3172 class Pair<A, B> {
       
  3173 
       
  3174     public final A fst;
       
  3175     public final B snd;
       
  3176 
       
  3177     public Pair(A fst, B snd) {
       
  3178         this.fst = fst;
       
  3179         this.snd = snd;
       
  3180     }
       
  3181 
       
  3182     public String toString() {
       
  3183         return "Pair[" + fst + "," + snd + "]";
       
  3184     }
       
  3185 
       
  3186     private static boolean equals(Object x, Object y) {
       
  3187         return (x == null && y == null) || (x != null && x.equals(y));
       
  3188     }
       
  3189 
       
  3190     public boolean equals(Object other) {
       
  3191         return
       
  3192             other instanceof Pair &&
       
  3193             equals(fst, ((Pair)other).fst) &&
       
  3194             equals(snd, ((Pair)other).snd);
       
  3195     }
       
  3196 
       
  3197     public int hashCode() {
       
  3198         if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
       
  3199         else if (snd == null) return fst.hashCode() + 2;
       
  3200         else return fst.hashCode() * 17 + snd.hashCode();
       
  3201     }
       
  3202 }