OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / tools / gnu / classpath / tools / keytool / ListCmd.java
1 /* ListCmd.java -- The list command handler of the keytool
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.classpath.tools.keytool;
40
41 import gnu.classpath.Configuration;
42 import gnu.classpath.tools.common.ClasspathToolParser;
43 import gnu.classpath.tools.getopt.Option;
44 import gnu.classpath.tools.getopt.OptionException;
45 import gnu.classpath.tools.getopt.OptionGroup;
46 import gnu.classpath.tools.getopt.Parser;
47 import gnu.java.security.util.Base64;
48
49 import java.io.IOException;
50 import java.io.PrintWriter;
51 import java.security.KeyStoreException;
52 import java.security.cert.Certificate;
53 import java.security.cert.CertificateEncodingException;
54 import java.util.Enumeration;
55 import java.util.logging.Logger;
56
57 /**
58  * The <b>-list</b> keytool command handler is used to output one or all key
59  * store entries.
60  * <p>
61  * Possible options for this command are:
62  * <p>
63  * <dl>
64  *      <dt>-alias ALIAS</dt>
65  *      <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted
66  *      Certificate</i>, in a key store is uniquely identified by a user-defined
67  *      <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use
68  *      when referring to an entry in the key store. Unless specified otherwise,
69  *      a default value of <code>mykey</code> shall be used when this option is
70  *      omitted from the command line.
71  *      <p></dd>
72  *      
73  *      <dt>-storetype STORE_TYPE</dt>
74  *      <dd>Use this option to specify the type of the key store to use. The
75  *      default value, if this option is omitted, is that of the property
76  *      <code>keystore.type</code> in the security properties file, which is
77  *      obtained by invoking the {@link java.security.KeyStore#getDefaultType()}
78  *      static method.
79  *      <p></dd>
80  *      
81  *      <dt>-keystore URL</dt>
82  *      <dd>Use this option to specify the location of the key store to use.
83  *      The default value is a file {@link java.net.URL} referencing the file
84  *      named <code>.keystore</code> located in the path returned by the call to
85  *      {@link java.lang.System#getProperty(String)} using <code>user.home</code>
86  *      as argument.
87  *      <p>
88  *      If a URL was specified, but was found to be malformed --e.g. missing
89  *      protocol element-- the tool will attempt to use the URL value as a file-
90  *      name (with absolute or relative path-name) of a key store --as if the
91  *      protocol was <code>file:</code>.
92  *      <p></dd>
93  *      
94  *      <dt>-storepass PASSWORD</dt>
95  *      <dd>Use this option to specify the password protecting the key store. If
96  *      this option is omitted from the command line, you will be prompted to
97  *      provide a password.
98  *      <p></dd>
99  *      
100  *      <dt>-provider PROVIDER_CLASS_NAME</dt>
101  *      <dd>A fully qualified class name of a Security Provider to add to the
102  *      current list of Security Providers already installed in the JVM in-use.
103  *      If a provider class is specified with this option, and was successfully
104  *      added to the runtime --i.e. it was not already installed-- then the tool
105  *      will attempt to removed this Security Provider before exiting.
106  *      <p></dd>
107  *      
108  *      <dt>-rfc</dt>
109  *      <dd>Use RFC-1421 specifications when encoding the output.
110  *      <p></dd>
111  *      
112  *      <dt>-v</dt>
113  *      <dd>Output the certificate in human-readable format. If both this option
114  *      and the <code>-rfc</code> option are detected on the command line, the
115  *      tool will opt for the human-readable form and will not abort the
116  *      command.</dd>
117  * </dl>
118  */
119 class ListCmd extends Command
120 {
121   private static final Logger log = Logger.getLogger(ListCmd.class.getName());
122   protected String _alias;
123   protected String _ksType;
124   protected String _ksURL;
125   protected String _ksPassword;
126   protected String _providerClassName;
127   protected boolean rfc;
128   private boolean all;
129
130   // default 0-arguments constructor
131
132   // public setters -----------------------------------------------------------
133
134   /** @param alias the alias to use. */
135   public void setAlias(String alias)
136   {
137     this._alias = alias;
138   }
139
140   /** @param type the key-store type to use. */
141   public void setStoretype(String type)
142   {
143     this._ksType = type;
144   }
145
146   /** @param url the key-store URL to use. */
147   public void setKeystore(String url)
148   {
149     this._ksURL = url;
150   }
151
152   /** @param password the key-store password to use. */
153   public void setStorepass(String password)
154   {
155     this._ksPassword = password;
156   }
157
158   /** @param className a security provider fully qualified class name to use. */
159   public void setProvider(String className)
160   {
161     this._providerClassName = className;
162   }
163
164   /**
165    * @param flag whether to use, or not, RFC-1421 format when listing the
166    *          certificate(s).
167    */
168   public void setRfc(String flag)
169   {
170     this.rfc = Boolean.valueOf(flag).booleanValue();
171   }
172
173   // life-cycle methods -------------------------------------------------------
174
175   void setup() throws Exception
176   {
177     setOutputStreamParam(null); // use stdout
178     setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL);
179     all = _alias == null;
180     if (! all)
181       setAliasParam(_alias);
182
183     if (verbose & rfc)
184       {
185         if (Configuration.DEBUG)
186           log.fine("Both -v and -rfc options were found on the command line. " //$NON-NLS-1$
187                    + "Only the former will be considered"); //$NON-NLS-1$
188         rfc = false;
189       }
190     if (Configuration.DEBUG)
191       {
192         log.fine("-list handler will use the following options:"); //$NON-NLS-1$
193         log.fine("  -alias=" + alias); //$NON-NLS-1$
194         log.fine("  -storetype=" + storeType); //$NON-NLS-1$
195         log.fine("  -keystore=" + storeURL); //$NON-NLS-1$
196         log.fine("  -provider=" + provider); //$NON-NLS-1$
197         log.fine("  -v=" + verbose); //$NON-NLS-1$
198         log.fine("  -rfc=" + rfc); //$NON-NLS-1$
199       }
200   }
201
202   void start() throws KeyStoreException, CertificateEncodingException,
203       IOException
204   {
205     if (Configuration.DEBUG)
206       log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$
207     PrintWriter writer = new PrintWriter(outStream, true);
208     writer.println(Messages.getFormattedString("ListCmd.21", store.getType())); //$NON-NLS-1$
209     writer.println(Messages.getFormattedString("ListCmd.22", //$NON-NLS-1$
210                                                store.getProvider().getName()));
211     if (all)
212       {
213         if (Configuration.DEBUG)
214           log.fine("About to list all aliases in key store..."); //$NON-NLS-1$
215         writer.println();
216         writer.println(Messages.getFormattedString("ListCmd.24", //$NON-NLS-1$
217                                                    Integer.valueOf(store.size())));
218         for (Enumeration e = store.aliases(); e.hasMoreElements(); )
219           {
220             String anAlias = (String) e.nextElement();
221             if (anAlias != null)
222               list1Alias(anAlias, writer);
223           }
224       }
225     else
226       list1Alias(alias, writer);
227     if (Configuration.DEBUG)
228       log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$
229   }
230
231   // own methods --------------------------------------------------------------
232
233   Parser getParser()
234   {
235     if (Configuration.DEBUG)
236       log.entering(this.getClass().getName(), "getParser"); //$NON-NLS-1$
237     Parser result = new ClasspathToolParser(Main.LIST_CMD, true);
238     result.setHeader(Messages.getString("ListCmd.20")); //$NON-NLS-1$
239     result.setFooter(Messages.getString("ListCmd.19")); //$NON-NLS-1$
240     OptionGroup options = new OptionGroup(Messages.getString("ListCmd.18")); //$NON-NLS-1$
241     options.add(new Option(Main.ALIAS_OPT,
242                            Messages.getString("ListCmd.17"), //$NON-NLS-1$
243                            Messages.getString("ListCmd.16")) //$NON-NLS-1$
244     {
245       public void parsed(String argument) throws OptionException
246       {
247         _alias = argument;
248       }
249     });
250     options.add(new Option(Main.STORETYPE_OPT,
251                            Messages.getString("ListCmd.15"), //$NON-NLS-1$
252                            Messages.getString("ListCmd.14")) //$NON-NLS-1$
253     {
254       public void parsed(String argument) throws OptionException
255       {
256         _ksType = argument;
257       }
258     });
259     options.add(new Option(Main.KEYSTORE_OPT,
260                            Messages.getString("ListCmd.13"), //$NON-NLS-1$
261                            Messages.getString("ListCmd.12")) //$NON-NLS-1$
262     {
263       public void parsed(String argument) throws OptionException
264       {
265         _ksURL = argument;
266       }
267     });
268     options.add(new Option(Main.STOREPASS_OPT,
269                            Messages.getString("ListCmd.11"), //$NON-NLS-1$
270                            Messages.getString("ListCmd.10")) //$NON-NLS-1$
271     {
272       public void parsed(String argument) throws OptionException
273       {
274         _ksPassword = argument;
275       }
276     });
277     options.add(new Option(Main.PROVIDER_OPT,
278                            Messages.getString("ListCmd.9"), //$NON-NLS-1$
279                            Messages.getString("ListCmd.8")) //$NON-NLS-1$
280     {
281       public void parsed(String argument) throws OptionException
282       {
283         _providerClassName = argument;
284       }
285     });
286     options.add(new Option(Main.VERBOSE_OPT,
287                            Messages.getString("ListCmd.7")) //$NON-NLS-1$
288     {
289       public void parsed(String argument) throws OptionException
290       {
291         verbose = true;
292       }
293     });
294     options.add(new Option(Main.RFC_OPT,
295                            Messages.getString("ListCmd.6")) //$NON-NLS-1$
296     {
297       public void parsed(String argument) throws OptionException
298       {
299         rfc = true;
300       }
301     });
302     result.add(options);
303     if (Configuration.DEBUG)
304       log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$
305     return result;
306   }
307
308   /**
309    * Prints the certificate(s) associated with the designated alias.
310    * 
311    * @param anAlias a non-null string denoting an alias in the key-store.
312    * @param writer where to print.
313    * @throws KeyStoreException if an exception occurs while obtaining the
314    *           certificate associated to the designated alias.
315    * @throws CertificateEncodingException if an exception occurs while obtaining
316    *           the DER encoded form of the certificate.
317    * @throws IOException if an I/O related exception occurs during the process.
318    */
319   private void list1Alias(String anAlias, PrintWriter writer)
320       throws KeyStoreException, CertificateEncodingException, IOException
321   {
322     if (Configuration.DEBUG)
323       log.entering(this.getClass().getName(), "list1Alias", anAlias); //$NON-NLS-1$
324     writer.println();
325     writer.println(Messages.getFormattedString("ListCmd.30", anAlias)); //$NON-NLS-1$
326     writer.println(Messages.getFormattedString("ListCmd.31", //$NON-NLS-1$
327                                                store.getCreationDate(anAlias)));
328     if (store.isCertificateEntry(anAlias))
329       {
330         writer.println(Messages.getString("ListCmd.32")); //$NON-NLS-1$
331         Certificate certificate = store.getCertificate(anAlias);
332         print1Certificate(certificate, writer);
333       }
334     else if (store.isKeyEntry(anAlias))
335       {
336         writer.println(Messages.getString("ListCmd.33")); //$NON-NLS-1$
337         Certificate[] chain = store.getCertificateChain(anAlias);
338         print1Chain(chain, writer);
339       }
340     else
341       throw new IllegalArgumentException(Messages.getFormattedString("ListCmd.34", //$NON-NLS-1$
342                                                                      anAlias));
343     if (Configuration.DEBUG)
344       log.exiting(this.getClass().getName(), "list1Alias"); //$NON-NLS-1$
345   }
346
347   /**
348    * Prints the designated certificate chain, or a fingerprint of the first
349    * certificate (bottom) in the chain, depending on the values of the flags
350    * <code>v</code> (for verbose) and <code>rfc</code>.
351    * <p>
352    * If both flags are <code>false</code>, only the fingerprint is generated,
353    * otherwise, if the <code>v</code> flag is set, then a human readable output
354    * is generated. If <code>rfc</code> is set, then an RFC-1421 like output
355    * is generated.
356    * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be
357    * <code>true</code> at the same time.
358    * 
359    * @param chain the certificate chain to process.
360    * @param writer where to print.
361    * @throws CertificateEncodingException if an exception occurs while obtaining
362    *           the DER encoded form of the certificate.
363    */
364   private void print1Chain(Certificate[] chain, PrintWriter writer)
365       throws CertificateEncodingException
366   {
367     if (! verbose && ! rfc)
368       fingerprint(chain[0], writer);
369     else
370       {
371         int limit = chain.length;
372         writer.println(Messages.getFormattedString("ListCmd.38", //$NON-NLS-1$
373                                                    Integer.valueOf(limit)));
374         writer.println(Messages.getString("ListCmd.39")); //$NON-NLS-1$
375         print1Certificate(chain[0], writer);
376         for (int i = 1; i < limit; i++)
377           {
378             writer.println();
379             writer.println(Messages.getFormattedString("ListCmd.40", //$NON-NLS-1$
380                                                        Integer.valueOf(i + 1)));
381             print1Certificate(chain[i], writer);
382           }
383         writer.println();
384         writer.println(Messages.getString("ListCmd.42")); //$NON-NLS-1$
385       }
386   }
387
388   /**
389    * Prints the designated certificate, or its fingerprint, depending on the
390    * values of the flags <code>v</code> (for verbose) and <code>rfc</code>.
391    * <p>
392    * If both flags are <code>false</code>, only a fingerprint is generated,
393    * otherwise, if the <code>v</code> flag is set, then a human readable output
394    * is generated. If <code>rfc</code> is set, then an RFC-1421 like output
395    * is generated.
396    * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be
397    * <code>true</code> at the same time.
398    * 
399    * @param certificate the certificate to process.
400    * @param writer where to print.
401    * @throws CertificateEncodingException if an exception occurs while obtaining
402    *           the DER encoded form of the certificate.
403    */
404   private void print1Certificate(Certificate certificate, PrintWriter writer)
405       throws CertificateEncodingException
406   {
407     if (verbose)
408       printVerbose(certificate, writer);
409     else if (rfc)
410       printRFC1421(certificate, writer);
411     else
412       fingerprint(certificate, writer);
413   }
414
415   private void printRFC1421(Certificate certificate, PrintWriter writer)
416       throws CertificateEncodingException
417   {
418     byte[] derBytes = certificate.getEncoded();
419     String encoded = Base64.encode(derBytes, 0, derBytes.length, true);
420     writer.println(Messages.getString("ListCmd.43")); //$NON-NLS-1$
421     writer.println(encoded);
422     writer.println(Messages.getString("ListCmd.44")); //$NON-NLS-1$
423   }
424
425   private void fingerprint(Certificate certificate, PrintWriter writer)
426       throws CertificateEncodingException
427   {
428     byte[] derBytes = certificate.getEncoded();
429     String fingerPrint = digestWithMD5(derBytes);
430     writer.println(Messages.getFormattedString("ListCmd.45", fingerPrint)); //$NON-NLS-1$
431   }
432 }