Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: gnu/classpath/tools/keytool/ImportCmd.java


1   /* ImportCmd.java -- The import 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.SystemProperties;
42  import gnu.java.security.x509.X509CertPath;
43  
44  import java.io.FileInputStream;
45  import java.io.IOException;
46  import java.security.Key;
47  import java.security.KeyStore;
48  import java.security.KeyStoreException;
49  import java.security.NoSuchAlgorithmException;
50  import java.security.PublicKey;
51  import java.security.UnrecoverableKeyException;
52  import java.security.cert.CertPathValidator;
53  import java.security.cert.CertPathValidatorException;
54  import java.security.cert.Certificate;
55  import java.security.cert.CertificateEncodingException;
56  import java.security.cert.CertificateException;
57  import java.security.cert.CertificateFactory;
58  import java.security.cert.PKIXCertPathValidatorResult;
59  import java.security.cert.PKIXParameters;
60  import java.security.interfaces.DSAParams;
61  import java.security.interfaces.DSAPublicKey;
62  import java.security.interfaces.RSAPublicKey;
63  import java.util.Collection;
64  import java.util.LinkedList;
65  import java.util.logging.Level;
66  import java.util.logging.Logger;
67  
68  import javax.security.auth.callback.Callback;
69  import javax.security.auth.callback.ConfirmationCallback;
70  import javax.security.auth.callback.UnsupportedCallbackException;
71  
72  /**
73   * The <code>-import</code> keytool command handler is used to read an X.509
74   * certificate, or a PKCS#7 Certificate Reply from a designated input source and
75   * incorporate the certificates into the key store.
76   * <p>
77   * If the <i>Alias</i> does not already exist in the key store, the tool treats
78   * the certificate read from the input source as a new Trusted Certificate. It
79   * then attempts to discover a chain-of-trust, starting from that certificate
80   * and ending at another <i>Trusted Certificate</i>, already stored in the key
81   * store.  If the <code>-trustcacerts</code> option is present, an additional
82   * key store, of type <code>JKS</code> named <code>cacerts</code>, and assumed
83   * to be present in <code>${JAVA_HOME}/lib/security</code> will also be
84   * consulted if found --<code>${JAVA_HOME}</code> refers to the location of an
85   * installed Java Runtime Environment (JRE). If no chain-of-trust can be
86   * established, and unless the <code>-noprompt</code> option has been specified,
87   * the certificate is printed to STDOUT and the user is prompted for a
88   * confirmation.
89   * <p>
90   * If <i>Alias</i> exists in the key store, the tool will treat the
91   * certificate(s) read from the input source as a <i>Certificate Reply</i>,
92   * which can be a chain of certificates, that eventually would replace the chain
93   * of certificates associated with the <i>Key Entry</i> of that <i>Alias</i>.
94   * The substitution of the certificates only occurs if a chain-of-trust can be
95   * established between the bottom certificate of the chain read from the input
96   * file and the <i>Trusted Certificates</i> already present in the key store.
97   * Again, if the <code>-trustcacerts</code> option is specified, additional
98   * <i>Trusted Certificates</i> in the same <code>cacerts</code> key store will
99   * be considered. If no chain-of-trust can be established, the operation will
100  * abort.
101  * <p>
102  * Possible options for this command are:
103  * <p>
104  * <dl>
105  *      <dt>-alias ALIAS</dt>
106  *      <dd>Every entry, be it a <i>Key Entry</i> or a <i>Trusted
107  *      Certificate</i>, in a key store is uniquely identified by a user-defined
108  *      <i>Alias</i> string. Use this option to specify the <i>Alias</i> to use
109  *      when referring to an entry in the key store. Unless specified otherwise,
110  *      a default value of <code>mykey</code> shall be used when this option is
111  *      omitted from the command line.
112  *      <p></dd>
113  *      
114  *      <dt>-file FILE_NAME</dt>
115  *      <dd>The fully qualified path of the file to read from. If omitted, the
116  *      tool will process STDIN.
117  *      <p></dd>
118  *      
119  *      <dt>-keypass PASSWORD</dt>
120  *      <dd>Use this option to specify the password which the tool will use to
121  *      protect the <i>Key Entry</i> associated with the designated <i>Alias</i>,
122  *      when replacing this <i>Alias</i>' chain of certificates with that found
123  *      in the certificate reply.
124  *      <p>
125  *      If this option is omitted, and the chain-of-trust for the certificate
126  *      reply has been established, the tool will first attempt to unlock the
127  *      <i>Key Entry</i> using the same password protecting the key store. If
128  *      this fails, you will then be prompted to provide a password.
129  *      <p></dd>
130  *      
131  *      <dt>-noprompt</dt>
132  *      <dd>Use this option to prevent the tool from prompting the user.
133  *      <p></dd>
134  *      
135  *      <dt>-trustcacerts</dt>
136  *      <dd>Use this option to indicate to the tool that a key store, of type
137  *      <code>JKS</code>, named <code>cacerts</code>, and usually located in
138  *      <code>lib/security</code> in an installed Java Runtime Environment
139  *      should be considered when trying to establish chain-of-trusts.
140  *      <p></dd>
141  *      
142  *      <dt>-storetype STORE_TYP}</dt>
143  *      <dd>Use this option to specify the type of the key store to use. The
144  *      default value, if this option is omitted, is that of the property
145  *      <code>keystore.type</code> in the security properties file, which is
146  *      obtained by invoking the {@link java.security.KeyStore#getDefaultType()}
147  *      static method.
148  *      <p></dd>
149  *      
150  *      <dt>-keystore URL</dt>
151  *      <dd>Use this option to specify the location of the key store to use.
152  *      The default value is a file {@link java.net.URL} referencing the file
153  *      named <code>.keystore</code> located in the path returned by the call to
154  *      {@link java.lang.System#getProperty(String)} using <code>user.home</code>
155  *      as argument.
156  *      <p>
157  *      If a URL was specified, but was found to be malformed --e.g. missing
158  *      protocol element-- the tool will attempt to use the URL value as a file-
159  *      name (with absolute or relative path-name) of a key store --as if the
160  *      protocol was <code>file:</code>.
161  *      <p></dd>
162  *      
163  *      <dt>-storepass PASSWORD</dt>
164  *      <dd>Use this option to specify the password protecting the key store. If
165  *      this option is omitted from the command line, you will be prompted to
166  *      provide a password.
167  *      <p></dd>
168  *      
169  *      <dt>-provider PROVIDER_CLASS_NAME</dt>
170  *      <dd>A fully qualified class name of a Security Provider to add to the
171  *      current list of Security Providers already installed in the JVM in-use.
172  *      If a provider class is specified with this option, and was successfully
173  *      added to the runtime --i.e. it was not already installed-- then the tool
174  *      will attempt to removed this Security Provider before exiting.
175  *      <p></dd>
176  *      
177  *      <dt>-v</dt>
178  *      <dd>Use this option to enable more verbose output.</dd>
179  * </dl>
180  */
181 class ImportCmd extends Command
182 {
183   private static final Logger log = Logger.getLogger(ImportCmd.class.getName());
184   private String _alias;
185   private String _certFileName;
186   private String _password;
187   private boolean noPrompt;
188   private boolean trustCACerts;
189   private String _ksType;
190   private String _ksURL;
191   private String _ksPassword;
192   private String _providerClassName;
193   private CertificateFactory x509Factory;
194   private boolean imported;
195 
196   // default 0-arguments constructor
197 
198   // public setters -----------------------------------------------------------
199 
200   /** @param alias the existing alias to use. */
201   public void setAlias(String alias)
202   {
203     this._alias = alias;
204   }
205 
206   /** @param pathName the fully qualified path name of the file to process. */
207   public void setFile(String pathName)
208   {
209     this._certFileName = pathName;
210   }
211 
212   /** @param password the existing (private) key password to use. */
213   public void setKeypass(String password)
214   {
215     this._password = password;
216   }
217 
218   /**
219    * @param flag whether to prompt, or not, the user to verify certificate
220    *          fingerprints.
221    */
222   public void setNoprompt(String flag)
223   {
224     this.noPrompt = Boolean.valueOf(flag).booleanValue();
225   }
226 
227   /**
228    * @param flag whether to trust, or not, certificates found in the
229    *          <code>cacerts</code> key store.
230    */
231   public void setTrustcacerts(String flag)
232   {
233     this.trustCACerts = Boolean.valueOf(flag).booleanValue();
234   }
235 
236   /** @param type the key-store type to use. */
237   public void setStoretype(String type)
238   {
239     this._ksType = type;
240   }
241 
242   /** @param url the key-store URL to use. */
243   public void setKeystore(String url)
244   {
245     this._ksURL = url;
246   }
247 
248   /** @param password the key-store password to use. */
249   public void setStorepass(String password)
250   {
251     this._ksPassword = password;
252   }
253 
254   /** @param className a security provider fully qualified class name to use. */
255   public void setProvider(String className)
256   {
257     this._providerClassName = className;
258   }
259 
260   // life-cycle methods -------------------------------------------------------
261 
262   int processArgs(String[] args, int i)
263   {
264     int limit = args.length;
265     String opt;
266     while (++i < limit)
267       {
268         opt = args[i];
269         log.finest("args[" + i + "]=" + opt); //$NON-NLS-1$ //$NON-NLS-2$
270         if (opt == null || opt.length() == 0)
271           continue;
272 
273         if ("-alias".equals(opt)) // -alias ALIAS //$NON-NLS-1$
274           _alias = args[++i];
275         else if ("-file".equals(opt)) // -file FILE_NAME //$NON-NLS-1$
276           _certFileName = args[++i];
277         else if ("-keypass".equals(opt)) // -keypass PASSWORD //$NON-NLS-1$
278           _password = args[++i];
279         else if ("-noprompt".equals(opt)) //$NON-NLS-1$
280           noPrompt = true;
281         else if ("-trustcacerts".equals(opt)) //$NON-NLS-1$
282           trustCACerts = true;
283         else if ("-storetype".equals(opt)) // -storetype STORE_TYPE //$NON-NLS-1$
284           _ksType = args[++i];
285         else if ("-keystore".equals(opt)) // -keystore URL //$NON-NLS-1$
286           _ksURL = args[++i];
287         else if ("-storepass".equals(opt)) // -storepass PASSWORD //$NON-NLS-1$
288           _ksPassword = args[++i];
289         else if ("-provider".equals(opt)) // -provider PROVIDER_CLASS_NAME //$NON-NLS-1$
290           _providerClassName = args[++i];
291         else if ("-v".equals(opt)) //$NON-NLS-1$
292           verbose = true;
293         else
294           break;
295       }
296 
297     return i;
298   }
299 
300   void setup() throws Exception
301   {
302     setInputStreamParam(_certFileName);
303     setKeyStoreParams(_providerClassName, _ksType, _ksPassword, _ksURL);
304     setAliasParam(_alias);
305     setKeyPasswordNoPrompt(_password);
306 
307     log.finer("-import handler will use the following options:"); //$NON-NLS-1$
308     log.finer("  -alias=" + alias); //$NON-NLS-1$
309     log.finer("  -file=" + _certFileName); //$NON-NLS-1$
310     log.finer("  -keypass=" + _password); //$NON-NLS-1$
311     log.finer("  -noprompt=" + noPrompt); //$NON-NLS-1$
312     log.finer("  -trustcacerts=" + trustCACerts); //$NON-NLS-1$
313     log.finer("  -storetype=" + storeType); //$NON-NLS-1$
314     log.finer("  -keystore=" + storeURL); //$NON-NLS-1$
315     log.finer("  -storepass=" + String.valueOf(storePasswordChars)); //$NON-NLS-1$
316     log.finer("  -provider=" + provider); //$NON-NLS-1$
317     log.finer("  -v=" + verbose); //$NON-NLS-1$
318   }
319 
320   void start() throws CertificateException, KeyStoreException, IOException,
321       UnsupportedCallbackException, NoSuchAlgorithmException,
322       CertPathValidatorException, UnrecoverableKeyException
323   {
324     log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$
325 
326     x509Factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
327     // the alias will tell us whether we're dealing with
328     // a new trusted certificate or a certificate reply
329     if (! store.containsAlias(alias))
330       importNewTrustedCertificate();
331     else
332       {
333         ensureAliasIsKeyEntry();
334         importCertificateReply();
335       }
336 
337     log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$
338   }
339 
340   // own methods --------------------------------------------------------------
341 
342   /**
343    * When importing a new trusted certificate, <i>alias</i> MUST NOT yet exist
344    * in the key store.
345    * <p>
346    * Before adding the certificate to the key store and associate it with the
347    * designated Alias, this method tries to verify it by attempting to construct
348    * a chain of trust from that certificate to a self-signed certificate
349    * (belonging to a root CA), using (already) trusted certificates that are
350    * available in the key store.
351    * <p>
352    * If the <code>-trustcacerts</code> option was detected on the command
353    * line, additional trusted certificates are considered for establishing the
354    * chain of trust. Those additional certificates are assumed to be in a key
355    * store, of type <code>JKS</code> named <code>cacerts</code> and usually
356    * located in <code>${JAVA_HOME}/lib/security</code>, where
357    * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime.
358    * <p>
359    * If this method fails to establish a trust path from the certificate to be
360    * imported up to a trusted self-signed certificate, the certificate is
361    * printed to <code>STDOUT</code>, and the user is prompted to verify it,
362    * with the option of aborting the import operation. If however the option
363    * <code>-noprompt</code> was detected on the command line, no interaction
364    * with the user will take place and the import operation will abort.
365    * 
366    * @throws CertificateException
367    * @throws KeyStoreException
368    * @throws NoSuchAlgorithmException
369    * @throws UnsupportedCallbackException 
370    * @throws IOException 
371    * @throws UnrecoverableKeyException 
372    * @throws CertPathValidatorException 
373    */
374   private void importNewTrustedCertificate() throws CertificateException,
375       KeyStoreException, NoSuchAlgorithmException, IOException,
376       UnsupportedCallbackException, CertPathValidatorException,
377       UnrecoverableKeyException
378   {
379     log.entering(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$
380 
381     Certificate certificate = x509Factory.generateCertificate(inStream);
382     log.finest("certificate = " + certificate); //$NON-NLS-1$
383     LinkedList orderedReply = new LinkedList();
384     orderedReply.addLast(certificate);
385 
386     if (findTrustAndUpdate(orderedReply, ! noPrompt))
387       {
388         store.setCertificateEntry(alias, certificate);
389         System.out.println(Messages.getString("ImportCmd.29")); //$NON-NLS-1$
390         saveKeyStore();
391       }
392     else
393       System.out.println(Messages.getString("ImportCmd.28")); //$NON-NLS-1$
394 
395     log.exiting(this.getClass().getName(), "importNewTrustedCertificate"); //$NON-NLS-1$
396   }
397 
398   /**
399    * A certificate reply is a certificate, whose Owner is stored in the key
400    * store associated to the designated Alias, and now signed by supposedly a
401    * trusted CA (Certificate Authority). In other words, the Subject in this
402    * certificate reply is Alias's own and the Issuer is a CA.
403    * <p>
404    * When importing a certificate reply, the reply is validated using trusted
405    * certificates from the key store, and optionally (if the option
406    * <code>-trustcacerts</code> was detected on the command line) certificates
407    * found in the key store, of type <code>JKS</code> named <code>cacerts</code>
408    * located in <code>${JAVA_HOME}/lib/security</code>, where
409    * <code>${JAVA_HOME}</code> is the root folder location of a Java runtime.
410    * 
411    * @throws CertificateException
412    * @throws UnsupportedCallbackException
413    * @throws IOException
414    * @throws KeyStoreException
415    * @throws CertPathValidatorException
416    * @throws NoSuchAlgorithmException
417    * @throws UnrecoverableKeyException
418    */
419   private void importCertificateReply() throws CertificateException,
420       IOException, UnsupportedCallbackException, KeyStoreException,
421       NoSuchAlgorithmException, CertPathValidatorException,
422       UnrecoverableKeyException
423   {
424     log.entering(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$
425 
426     Collection certificates = x509Factory.generateCertificates(inStream);
427     ensureReplyIsOurs(certificates);
428     // we now have established that the public keys are the same.
429     // find a chain-of-trust if one exists
430     if (certificates.size() == 1)
431       importCertificate((Certificate) certificates.iterator().next());
432     else
433       importChain(certificates);
434 
435     log.exiting(this.getClass().getName(), "importCertificateReply"); //$NON-NLS-1$
436   }
437 
438   /**
439    * If the reply is a single X.509 certificate, keytool attempts to establish a
440    * trust chain, starting at the certificate reply and ending at a self-signed
441    * certificate (belonging to a root CA). The certificate reply and the
442    * hierarchy of certificates used to authenticate the certificate reply form
443    * the new certificate chain of alias. If a trust chain cannot be established,
444    * the certificate reply is not imported. In this case, keytool does not print
445    * out the certificate, nor does it prompt the user to verify it. This is
446    * because it is very hard (if not impossible) for a user to determine the
447    * authenticity of the certificate reply.
448    * 
449    * @param certificate the certificate reply to import into the key store.
450    * @throws NoSuchAlgorithmException 
451    * @throws CertPathValidatorException 
452    * @throws UnsupportedCallbackException 
453    * @throws IOException 
454    * @throws UnrecoverableKeyException 
455    * @throws KeyStoreException 
456    * @throws CertificateException 
457    */
458   private void importCertificate(Certificate certificate)
459       throws NoSuchAlgorithmException, CertPathValidatorException,
460       KeyStoreException, UnrecoverableKeyException, IOException,
461       UnsupportedCallbackException, CertificateException
462   {
463     log.entering(this.getClass().getName(), "importCertificate", certificate); //$NON-NLS-1$
464 
465     LinkedList reply = new LinkedList();
466     reply.addLast(certificate);
467 
468     if (! findTrustAndUpdate(reply, false))
469       throw new CertPathValidatorException(Messages.getString("ImportCmd.34")); //$NON-NLS-1$
470 
471     Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]);
472     Key privateKey = getAliasPrivateKey();
473     store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain);
474     saveKeyStore();
475 
476     log.exiting(this.getClass().getName(), "importCertificate"); //$NON-NLS-1$
477   }
478 
479   /**
480    * If the reply is a PKCS#7 formatted certificate chain, the chain is first
481    * ordered (with the user certificate first and the self-signed root CA
482    * certificate last), before keytool attempts to match the root CA certificate
483    * provided in the reply with any of the trusted certificates in the key store
484    * or the "cacerts" keystore file (if the -trustcacerts option was specified).
485    * If no match can be found, the information of the root CA certificate is
486    * printed out, and the user is prompted to verify it, e.g., by comparing the
487    * displayed certificate fingerprints with the fingerprints obtained from some
488    * other (trusted) source of information, which might be the root CA itself.
489    * The user then has the option of aborting the import operation. If the
490    * -noprompt option is given, however, there will be no interaction with the
491    * user.
492    * 
493    * @param chain the collection of certificates parsed from the user
494    *          designated input.
495    * @throws UnsupportedCallbackException 
496    * @throws IOException 
497    * @throws UnrecoverableKeyException 
498    * @throws KeyStoreException 
499    * @throws CertPathValidatorException 
500    * @throws NoSuchAlgorithmException 
501    * @throws CertificateException 
502    */
503   private void importChain(Collection chain) throws NoSuchAlgorithmException,
504       CertPathValidatorException, KeyStoreException, UnrecoverableKeyException,
505       IOException, UnsupportedCallbackException, CertificateException
506   {
507     log.entering(this.getClass().getName(), "importChain", chain); //$NON-NLS-1$
508 
509     LinkedList reply = orderChain(chain);
510     if (findTrustAndUpdate(reply, ! noPrompt))
511       {
512         Certificate[] newChain = (Certificate[]) reply.toArray(new Certificate[0]);
513         Key privateKey = getAliasPrivateKey();
514         store.setKeyEntry(alias, privateKey, keyPasswordChars, newChain);
515         saveKeyStore();
516       }
517 
518     log.exiting(this.getClass().getName(), "importChain"); //$NON-NLS-1$
519   }
520 
521   /**
522    * Check to ensure that alias's public key is the subject of the first
523    * certificate in the passed certificate collection. Throws an exception if
524    * the public keys do not match.
525    * 
526    * @param certificates a {@link Collection} of certificate replies (either a
527    *          signle certificate reply, or a PKCS#7 certificate reply chain)
528    *          usually sent by a CA as a response to a Certificate Signing
529    *          Request (CSR).
530    * @throws IOException
531    * @throws UnsupportedCallbackException
532    * @throws KeyStoreException
533    */
534   private void ensureReplyIsOurs(Collection certificates) throws IOException,
535       UnsupportedCallbackException, KeyStoreException
536   {
537     log.entering(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$
538 
539     Certificate certificate = (Certificate) certificates.iterator().next();
540     log.finest("certificate = " + certificate); //$NON-NLS-1$
541     Certificate[] chain = store.getCertificateChain(alias);
542     if (chain == null)
543       throw new IllegalArgumentException(Messages.getFormattedString("ImportCmd.37", //$NON-NLS-1$
544                                                                      alias));
545     Certificate anchor = chain[0];
546     PublicKey anchorPublicKey = anchor.getPublicKey();
547     PublicKey certPublicKey = certificate.getPublicKey();
548     boolean sameKey;
549     if (anchorPublicKey instanceof DSAPublicKey)
550       {
551         DSAPublicKey pk1 = (DSAPublicKey) anchorPublicKey;
552         if (!(certPublicKey instanceof DSAPublicKey))
553           throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$
554 
555         sameKey = areEqual(pk1, (DSAPublicKey) certPublicKey);
556       }
557     else if (anchorPublicKey instanceof RSAPublicKey)
558       {
559         RSAPublicKey pk1 = (RSAPublicKey) anchorPublicKey;
560         if (!(certPublicKey instanceof RSAPublicKey))
561           throw new IllegalArgumentException(Messages.getString("ImportCmd.38")); //$NON-NLS-1$
562 
563         sameKey = areEqual(pk1, (RSAPublicKey) certPublicKey);
564       }
565     else
566       throw new IllegalArgumentException(
567           Messages.getFormattedString("ImportCmd.40", //$NON-NLS-1$
568                                       new String[] { alias,
569                                                      anchorPublicKey.getClass().getName() }));
570     if (! sameKey)
571       throw new IllegalArgumentException(Messages.getString("ImportCmd.41")); //$NON-NLS-1$
572 
573     log.exiting(this.getClass().getName(), "ensureReplyIsOurs"); //$NON-NLS-1$
574   }
575 
576   private boolean areEqual(DSAPublicKey pk1, DSAPublicKey pk2)
577   {
578     if (pk1.getY().compareTo(pk2.getY()) != 0)
579       return false;
580 
581     DSAParams p1 = pk1.getParams();
582     DSAParams p2 = pk2.getParams();
583     if (p1.getG().compareTo(p2.getG()) != 0)
584       return false;
585 
586     if (p1.getP().compareTo(p2.getP()) != 0)
587       return false;
588 
589     return p1.getQ().compareTo(p2.getQ()) == 0;
590   }
591 
592   private boolean areEqual(RSAPublicKey pk1, RSAPublicKey pk2)
593   {
594     if (pk1.getPublicExponent().compareTo(pk2.getPublicExponent()) != 0)
595       return false;
596 
597     return pk1.getModulus().compareTo(pk2.getModulus()) == 0;
598   }
599 
600   /**
601    * @param chain
602    * @return the input collection, ordered with own certificate first, and CA's
603    *         self-signed certificate last.
604    */
605   private LinkedList orderChain(Collection chain)
606   {
607     log.entering(this.getClass().getName(), "orderChain"); //$NON-NLS-1$
608 
609     LinkedList result = new LinkedList();
610     
611 
612     log.entering(this.getClass().getName(), "orderChain", result); //$NON-NLS-1$
613     return result;
614   }
615 
616   /**
617    * Given an ordered list of certificates, this method attempts to validate the
618    * chain, and if successful, updates the key store entry for the designated
619    * alias. The list of certificates is expected to be ordered as a chain, where
620    * the first is the alias's own certificate and the last being a self-signed
621    * CA certificate.
622    * <p>
623    * if <code>promptUser</code> is <code>true</code>, then even if no
624    * anchor trust certificate is found, the user is prompted to approve, or not,
625    * the import operation. On the other hand if the <code>promptUser</code>
626    * parameter is <code>false</code> then this method will throw an exception
627    * if no trust anchor is to be found.
628    * 
629    * @param reply an ordered certificate path, where the last entry is the CA's
630    *          self-signed certificate.
631    * @param promptUser a boolean flag indicating whether or not to prompt the
632    *          user for explicit trust in a CA certificate.
633    * @return <code>true</code> if the validation succeeds; or <code>false</code>
634    *         otherwise.
635    * @throws NoSuchAlgorithmException
636    * @throws CertPathValidatorException
637    * @throws UnsupportedCallbackException
638    * @throws IOException
639    * @throws UnrecoverableKeyException
640    * @throws KeyStoreException
641    * @throws CertificateEncodingException
642    */
643   private boolean findTrustAndUpdate(LinkedList reply, boolean promptUser)
644       throws IOException, NoSuchAlgorithmException, CertPathValidatorException,
645       KeyStoreException, UnrecoverableKeyException, UnsupportedCallbackException,
646       CertificateEncodingException
647   {
648     log.entering(this.getClass().getName(), "findTrustAndUpdate"); //$NON-NLS-1$
649 
650     X509CertPath certPath = new X509CertPath(reply);
651     CertPathValidator validator = CertPathValidator.getInstance("PKIX"); //$NON-NLS-1$
652     PKIXCertPathValidatorResult cpvr = findTrustInStore(certPath, validator);
653     if (cpvr == null && trustCACerts)
654       cpvr = findTrustInCACerts(certPath, validator);
655 
656     boolean result = false;
657     if (cpvr == null)
658       {
659         if (promptUser)
660           {
661             printVerbose((Certificate) reply.getLast());
662             ConfirmationCallback ccb;
663             ccb = new ConfirmationCallback(Messages.getString("ImportCmd.32"), //$NON-NLS-1$
664                                            ConfirmationCallback.INFORMATION,
665                                            ConfirmationCallback.YES_NO_OPTION,
666                                            ConfirmationCallback.NO);
667             getCallbackHandler().handle(new Callback[] { ccb });
668             int answer = ccb.getSelectedIndex();
669             result = answer == ConfirmationCallback.YES;
670           }
671       }
672     else
673       {
674         log.fine("Found a chain-of-trust anchored by " + cpvr.getTrustAnchor()); //$NON-NLS-1$
675         Certificate trustedCert = cpvr.getTrustAnchor().getTrustedCert();
676         reply.addLast(trustedCert);
677         result = true;
678       }
679 
680     log.entering(this.getClass().getName(), "findTrustAndUpdate", //$NON-NLS-1$
681                  Boolean.valueOf(result));
682     return result;
683   }
684 
685   private PKIXCertPathValidatorResult findTrustInStore(X509CertPath certPath,
686                                                        CertPathValidator validator)
687   {
688     log.entering(this.getClass().getName(), "findTrustInStore"); //$NON-NLS-1$
689 
690     PKIXCertPathValidatorResult result;
691     try
692       {
693         PKIXParameters params = new PKIXParameters(store);
694         result = (PKIXCertPathValidatorResult) validator.validate(certPath, params);
695       }
696     catch (Exception x)
697       {
698         log.log(Level.FINE,
699                 "Exception in findTrustInStore(). Ignore + Return NULL", //$NON-NLS-1$
700                 x);
701         result = null;
702       }
703 
704     log.exiting(this.getClass().getName(), "findTrustInStore", result); //$NON-NLS-1$
705     return result;
706   }
707 
708   private PKIXCertPathValidatorResult findTrustInCACerts(X509CertPath certPath,
709                                                          CertPathValidator validator)
710   {
711     log.entering(this.getClass().getName(), "findTrustInCACerts"); //$NON-NLS-1$
712 
713     FileInputStream stream = null;
714     PKIXCertPathValidatorResult result = null;
715     try
716       {
717         KeyStore cacerts = KeyStore.getInstance("jks"); //$NON-NLS-1$
718         String cacertsPath = SystemProperties.getProperty("java.home");
719         String fs = SystemProperties.getProperty("file.separator"); //$NON-NLS-1$
720         cacertsPath = new StringBuilder(cacertsPath).append(fs)
721             .append("lib").append(fs) //$NON-NLS-1$
722             .append("security").append(fs) //$NON-NLS-1$
723             .append("cacerts").toString(); //$NON-NLS-1$
724         stream = new FileInputStream(cacertsPath);
725         cacerts.load(stream, "changeit".toCharArray()); //$NON-NLS-1$
726         PKIXParameters params = new PKIXParameters(cacerts);
727         result = (PKIXCertPathValidatorResult) validator.validate(certPath,
728                                                                   params);
729       }
730     catch (Exception x)
731       {
732         log.log(Level.FINE,
733                 "Exception in findTrustInCACerts(). Ignore + Return NULL", //$NON-NLS-1$
734                 x);
735       }
736     finally
737       {
738         if (stream != null)
739           try
740             {
741               stream.close();
742             }
743           catch (Exception ignored)
744             {
745             }
746       }
747 
748     log.exiting(this.getClass().getName(), "findTrustInCACerts", result); //$NON-NLS-1$
749     return result;
750   }
751 }