OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [tools/] [gnu/] [classpath/] [tools/] [keytool/] [ImportCmd.java] - Blame information for rev 779

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.