1 /* ListCmd.java -- The list command handler of the keytool
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package gnu.classpath.tools.keytool;
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;
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;
58 * The <b>-list</b> keytool command handler is used to output one or all key
61 * Possible options for this command are:
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.
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()}
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>
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>.
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
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.
109 * <dd>Use RFC-1421 specifications when encoding the output.
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
119 class ListCmd extends Command
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;
130 // default 0-arguments constructor
132 // public setters -----------------------------------------------------------
134 /** @param alias the alias to use. */
135 public void setAlias(String alias)
140 /** @param type the key-store type to use. */
141 public void setStoretype(String type)
146 /** @param url the key-store URL to use. */
147 public void setKeystore(String url)
152 /** @param password the key-store password to use. */
153 public void setStorepass(String password)
155 this._ksPassword = password;
158 /** @param className a security provider fully qualified class name to use. */
159 public void setProvider(String className)
161 this._providerClassName = className;
165 * @param flag whether to use, or not, RFC-1421 format when listing the
168 public void setRfc(String flag)
170 this.rfc = Boolean.valueOf(flag).booleanValue();
173 // life-cycle methods -------------------------------------------------------
175 void setup() throws Exception
177 setOutputStreamParam(null); // use stdout
178 setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL);
179 all = _alias == null;
181 setAliasParam(_alias);
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$
190 if (Configuration.DEBUG)
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$
202 void start() throws KeyStoreException, CertificateEncodingException,
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()));
213 if (Configuration.DEBUG)
214 log.fine("About to list all aliases in key store..."); //$NON-NLS-1$
216 writer.println(Messages.getFormattedString("ListCmd.24", //$NON-NLS-1$
217 Integer.valueOf(store.size())));
218 for (Enumeration e = store.aliases(); e.hasMoreElements(); )
220 String anAlias = (String) e.nextElement();
222 list1Alias(anAlias, writer);
226 list1Alias(alias, writer);
227 if (Configuration.DEBUG)
228 log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$
231 // own methods --------------------------------------------------------------
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$
245 public void parsed(String argument) throws OptionException
250 options.add(new Option(Main.STORETYPE_OPT,
251 Messages.getString("ListCmd.15"), //$NON-NLS-1$
252 Messages.getString("ListCmd.14")) //$NON-NLS-1$
254 public void parsed(String argument) throws OptionException
259 options.add(new Option(Main.KEYSTORE_OPT,
260 Messages.getString("ListCmd.13"), //$NON-NLS-1$
261 Messages.getString("ListCmd.12")) //$NON-NLS-1$
263 public void parsed(String argument) throws OptionException
268 options.add(new Option(Main.STOREPASS_OPT,
269 Messages.getString("ListCmd.11"), //$NON-NLS-1$
270 Messages.getString("ListCmd.10")) //$NON-NLS-1$
272 public void parsed(String argument) throws OptionException
274 _ksPassword = argument;
277 options.add(new Option(Main.PROVIDER_OPT,
278 Messages.getString("ListCmd.9"), //$NON-NLS-1$
279 Messages.getString("ListCmd.8")) //$NON-NLS-1$
281 public void parsed(String argument) throws OptionException
283 _providerClassName = argument;
286 options.add(new Option(Main.VERBOSE_OPT,
287 Messages.getString("ListCmd.7")) //$NON-NLS-1$
289 public void parsed(String argument) throws OptionException
294 options.add(new Option(Main.RFC_OPT,
295 Messages.getString("ListCmd.6")) //$NON-NLS-1$
297 public void parsed(String argument) throws OptionException
303 if (Configuration.DEBUG)
304 log.exiting(this.getClass().getName(), "getParser", result); //$NON-NLS-1$
309 * Prints the certificate(s) associated with the designated alias.
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.
319 private void list1Alias(String anAlias, PrintWriter writer)
320 throws KeyStoreException, CertificateEncodingException, IOException
322 if (Configuration.DEBUG)
323 log.entering(this.getClass().getName(), "list1Alias", anAlias); //$NON-NLS-1$
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))
330 writer.println(Messages.getString("ListCmd.32")); //$NON-NLS-1$
331 Certificate certificate = store.getCertificate(anAlias);
332 print1Certificate(certificate, writer);
334 else if (store.isKeyEntry(anAlias))
336 writer.println(Messages.getString("ListCmd.33")); //$NON-NLS-1$
337 Certificate[] chain = store.getCertificateChain(anAlias);
338 print1Chain(chain, writer);
341 throw new IllegalArgumentException(Messages.getFormattedString("ListCmd.34", //$NON-NLS-1$
343 if (Configuration.DEBUG)
344 log.exiting(this.getClass().getName(), "list1Alias"); //$NON-NLS-1$
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>.
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
356 * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be
357 * <code>true</code> at the same time.
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.
364 private void print1Chain(Certificate[] chain, PrintWriter writer)
365 throws CertificateEncodingException
367 if (! verbose && ! rfc)
368 fingerprint(chain[0], writer);
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++)
379 writer.println(Messages.getFormattedString("ListCmd.40", //$NON-NLS-1$
380 Integer.valueOf(i + 1)));
381 print1Certificate(chain[i], writer);
384 writer.println(Messages.getString("ListCmd.42")); //$NON-NLS-1$
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>.
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
396 * <p>Note that both <code>v</code> and <code>rfc</code> cannot both be
397 * <code>true</code> at the same time.
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.
404 private void print1Certificate(Certificate certificate, PrintWriter writer)
405 throws CertificateEncodingException
408 printVerbose(certificate, writer);
410 printRFC1421(certificate, writer);
412 fingerprint(certificate, writer);
415 private void printRFC1421(Certificate certificate, PrintWriter writer)
416 throws CertificateEncodingException
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$
425 private void fingerprint(Certificate certificate, PrintWriter writer)
426 throws CertificateEncodingException
428 byte[] derBytes = certificate.getEncoded();
429 String fingerPrint = digestWithMD5(derBytes);
430 writer.println(Messages.getFormattedString("ListCmd.45", fingerPrint)); //$NON-NLS-1$