2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.impl.membership.pse;
60 import net.jxta.id.ID;
61 import net.jxta.id.IDFactory;
62 import net.jxta.logging.Logging;
64 import java.io.IOException;
66 import java.net.URISyntaxException;
67 import java.security.*;
68 import java.security.cert.Certificate;
69 import java.security.cert.X509Certificate;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Enumeration;
73 import java.util.List;
74 import java.util.logging.Level;
75 import java.util.logging.Logger;
79 * Manages the state of a Personal Security Enviroment.
81 public final class PSEConfig {
86 private final static transient Logger LOG = Logger.getLogger(PSEConfig.class.getName());
89 * Manager for the keystore we are using.
91 private final KeyStoreManager keystore_manager;
94 * The keystore passphrase.
96 private char[] keystore_password = null;
99 * Standard constructor.
101 * @param storeManager The StoreManager to be used for this PSEConfig
103 * @param store_password The passphrase for the keystore or <tt>null</tt>.
104 * The passphrase may be set independantly via
105 * {@link #setKeyStorePassword(char[])}.
107 PSEConfig(KeyStoreManager storeManager, char[] store_password) {
108 this.keystore_manager = storeManager;
109 setKeyStorePassword(store_password);
113 * Sets the passphrase to be used when unlocking the keystore.
115 * @param store_password The passphrase used to unlock the keystore may be
116 * {@code null} for keystores with no passphrase.
118 public final void setKeyStorePassword(char[] store_password) {
119 if (null != this.keystore_password) {
120 Arrays.fill(this.keystore_password, '\0');
123 if (null == store_password) {
124 this.keystore_password = null;
126 this.keystore_password = store_password.clone();
134 protected void finalize() throws Throwable {
135 if (null != keystore_password) {
136 Arrays.fill(keystore_password, '\0');
143 * Returns {@code true} if the PSE has been initialized (created). Some
144 * keystore formats may not require initialization and may always return
145 * {@code true}. {@code false} may also be returned if the keystore passphrase is
148 * @return {@code true} if the PSE has been previously initialized
149 * otherwise {@code false}.
151 public boolean isInitialized() {
153 if (keystore_password != null) {
154 return keystore_manager.isInitialized(keystore_password);
156 return keystore_manager.isInitialized();
158 } catch (Exception ignored) {
164 * Initializes the PSE environment.
166 * @throws KeyStoreException When the wrong keystore has been provided.
167 * @throws IOException For errors related to processing the keystore.
169 public void initialize() throws KeyStoreException, IOException {
171 if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
172 LOG.info("Initializing new PSE keystore...");
175 synchronized (keystore_manager) {
177 if (keystore_manager.isInitialized(keystore_password)) {
181 keystore_manager.createKeyStore(keystore_password);
182 } catch (KeyStoreException failed) {
183 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
184 LOG.log(Level.SEVERE, "Failure accessing or creating keystore.", failed);
187 keystore_manager.eraseKeyStore();
195 * Removes an existing PSE enviroment.
197 * @throws IOException If the PSE cannot be successfully deleted.
199 public void erase() throws IOException {
200 synchronized (keystore_manager) {
201 keystore_manager.eraseKeyStore();
206 * Gets a copy of the KeyStore associated with this PSE instance. The
207 * returned KeyStore is a copy and not tied to the instance maintained by
208 * the PSE. Changing the returned keystore will not result in changes to
211 * @return The keystore or {@code null} if it cannot be retrieved.
213 public KeyStore getKeyStore() {
217 return getKeyStore(keystore_password);
218 } catch (KeyStoreException failed) {
220 } catch (IOException failed) {
224 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
225 LOG.warning("Failure recovering keystore : " + failure);
232 * Gets a copy of the KeyStore associated with this PSE instance. The
233 * returned KeyStore is a copy and not tied to the instance maintained by
234 * the PSE. Changing the returned keystore will not result in changes to
237 * @param store_password The passphrase used to unlock the keystore may be
238 * {@code null} for keystores with no passphrase.
239 * @return The keystore.
240 * @throws KeyStoreException When the wrong keystore has been provided.
241 * @throws IOException For errors related to processing the keystore.
244 public KeyStore getKeyStore(char[] store_password) throws KeyStoreException, IOException {
245 synchronized (keystore_manager) {
246 KeyStore store = keystore_manager.loadKeyStore(store_password);
253 * Check if the provided passwords are correct for the specified identity.
255 * @param id The identity to be validated.
256 * @param store_password The passphrase used to unlock the keystore may be
257 * {@code null} for keystores with no passphrase.
258 * @param key_password The passphrase associated with the private key or
259 * {@code null} if the key has no passphrase.
260 * @return {@code true} if the passwords were valid for the given id
261 * otherwise {@code false}.
263 boolean validPasswd(ID id, char[] store_password, char[] key_password) {
272 synchronized (keystore_manager) {
275 if (null != store_password) {
276 store = keystore_manager.loadKeyStore(store_password);
278 if (null != keystore_password) {
279 store = keystore_manager.loadKeyStore(keystore_password);
281 throw new UnrecoverableKeyException("KeyStore passphrase not initialized");
285 String alias = id.toString();
287 Key key = store.getKey(alias, key_password);
289 return (null != key);
291 } catch (UnrecoverableKeyException failed) {
293 } catch (NoSuchAlgorithmException failed) {
295 } catch (KeyStoreException failed) {
297 } catch (IOException failed) {
301 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
302 LOG.warning("Failure checking passphrase : " + failure);
309 * Returns the list of the trusted certificates available in this keystore.
311 * @return an array of the IDs of the available trusted certificates.
312 * @throws KeyStoreException When the wrong keystore has been provided.
313 * @throws IOException For errors related to processing the keystore.
315 public ID[] getTrustedCertsList() throws KeyStoreException, IOException {
316 List<ID> trustedCertsList = new ArrayList<ID>();
318 synchronized (keystore_manager) {
319 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
321 Enumeration<String> eachAlias = store.aliases();
323 while (eachAlias.hasMoreElements()) {
324 String anAlias = eachAlias.nextElement();
326 if (store.isCertificateEntry(anAlias) || store.isKeyEntry(anAlias)) {
328 URI id = new URI(anAlias);
330 trustedCertsList.add(IDFactory.fromURI(id));
331 } catch (URISyntaxException badID) {// ignored
336 return trustedCertsList.toArray(new ID[trustedCertsList.size()]);
341 * Returns the list of root certificates for which there is an associated
344 * @return an array of the available keys. May be an empty array.
345 * @throws KeyStoreException When the wrong keystore has been provided.
346 * @throws IOException For errors related to processing the keystore.
348 public ID[] getKeysList() throws KeyStoreException, IOException {
349 return getKeysList(keystore_password);
353 * Returns the list of root certificates for which there is an associated
356 * @param store_password The passphrase used to unlock the keystore may be
357 * {@code null} for keystores with no passphrase.
358 * @return an array of the available keys. May be an empty array.
359 * @throws KeyStoreException When the wrong keystore has been provided.
360 * @throws IOException For errors related to processing the keystore.
362 ID[] getKeysList(char[] store_password) throws KeyStoreException, IOException {
363 List<ID> keyedRootsList = new ArrayList<ID>();
365 synchronized (keystore_manager) {
366 KeyStore store = keystore_manager.loadKeyStore(store_password);
368 Enumeration<String> eachAlias = store.aliases();
370 while (eachAlias.hasMoreElements()) {
371 String anAlias = eachAlias.nextElement();
373 if (store.isKeyEntry(anAlias)) {
375 URI id = new URI(anAlias);
377 keyedRootsList.add(IDFactory.fromURI(id));
378 } catch (URISyntaxException badID) {// ignored
383 return keyedRootsList.toArray(new ID[keyedRootsList.size()]);
388 * Returns the ID of the provided certificate or null if the certificate is
389 * not found in the keystore.
391 * @param cert The certificate who's ID is desired.
392 * @return The ID of the certificate or <tt>null</tt> if no matching
393 * Certificate was found.
394 * @throws KeyStoreException When the wrong keystore has been provided.
395 * @throws IOException For errors related to processing the keystore.
397 public ID getTrustedCertificateID(X509Certificate cert) throws KeyStoreException, IOException {
399 String anAlias = null;
401 synchronized (keystore_manager) {
402 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
404 anAlias = store.getCertificateAlias(cert);
408 if (null == anAlias) {
413 URI id = new URI(anAlias);
415 return IDFactory.fromURI(id);
416 } catch (URISyntaxException badID) {
422 * Returns the trusted cert for the specified id.
424 * @param id The id of the Certificate to retrieve.
425 * @return Certificate for the specified ID or null if the store does not
426 * contain the specified certificate.
427 * @throws KeyStoreException When the wrong keystore key has been provided.
428 * @throws IOException For errors related to processing the keystore.
430 public X509Certificate getTrustedCertificate(ID id) throws KeyStoreException, IOException {
432 return getTrustedCertificate(id, keystore_password);
436 * Returns the trusted cert for the specified id.
438 * @param id The id of the Certificate to retrieve.
439 * @param store_password The passphrase used to unlock the keystore may be
440 * {@code null} for keystores with no passphrase.
441 * @return Certificate for the specified ID or null if the store does not
442 * contain the specified certificate.
443 * @throws KeyStoreException When the wrong keystore has been provided.
444 * @throws IOException For errors related to processing the keystore.
446 X509Certificate getTrustedCertificate(ID id, char[] store_password) throws KeyStoreException, IOException {
448 String alias = id.toString();
450 synchronized (keystore_manager) {
451 KeyStore store = keystore_manager.loadKeyStore(store_password);
453 if (!store.containsAlias(alias)) {
457 return (X509Certificate) store.getCertificate(alias);
462 * Returns the trusted cert chain for the specified id.
464 * @param id The ID of the certificate who's certificate chain is desired.
465 * @return Certificate chain for the specified ID or null if the PSE does
466 * not contain the specified certificate.
467 * @throws KeyStoreException When the wrong keystore has been provided.
468 * @throws IOException For errors related to processing the keystore.
470 public X509Certificate[] getTrustedCertificateChain(ID id) throws KeyStoreException, IOException {
472 String alias = id.toString();
474 synchronized (keystore_manager) {
475 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
477 if (!store.containsAlias(alias)) {
481 Certificate certs[] = store.getCertificateChain(alias);
487 X509Certificate x509certs[] = new X509Certificate[certs.length];
489 System.arraycopy(certs, 0, x509certs, 0, certs.length);
496 * Returns the private key for the specified ID.
498 * @param id The ID of the requested private key.
499 * @param key_password The passphrase associated with the private key or
500 * {@code null} if the key has no passphrase.
501 * @return PrivateKey for the specified ID.
502 * @throws KeyStoreException When the wrong keystore has been provided.
503 * @throws IOException For errors related to processing the keystore.
505 public PrivateKey getKey(ID id, char[] key_password) throws KeyStoreException, IOException {
507 String alias = id.toString();
510 synchronized (keystore_manager) {
511 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
513 if (!store.containsAlias(alias) || !store.isKeyEntry(alias)) {
517 return (PrivateKey) store.getKey(alias, key_password);
519 } catch (NoSuchAlgorithmException failed) {
520 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
521 LOG.log(Level.SEVERE, "Something failed", failed);
524 KeyStoreException failure = new KeyStoreException("Something Failed");
526 failure.initCause(failed);
528 } catch (UnrecoverableKeyException failed) {
529 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
530 LOG.log(Level.SEVERE, "Key passphrase failure", failed);
533 KeyStoreException failure = new KeyStoreException("Key passphrase failure");
535 failure.initCause(failed);
541 * Returns <tt>true</tt> if the specified id is associated with a private
544 * @param id The ID of the requested private key.
545 * @return <tt>true</tt> if a private key with the specified ID is present
546 * otherwise <tt>false</tt>
547 * @throws KeyStoreException When the wrong keystore has been provided.
548 * @throws IOException For errors related to processing the keystore.
550 public boolean isKey(ID id) throws KeyStoreException, IOException {
551 return isKey(id, keystore_password);
555 * Returns <tt>true</tt> if the specified id is associated with a private
558 * @param id The ID of the requested private key.
559 * @param store_password The passphrase used to unlock the keystore may be
560 * {@code null} for keystores with no passphrase.
561 * @return <tt>true</tt> if a private key with the specified ID is present
562 * otherwise <tt>false</tt>
563 * @throws KeyStoreException When the wrong keystore has been provided.
564 * @throws IOException For errors related to processing the keystore.
566 public boolean isKey(ID id, char[] store_password) throws KeyStoreException, IOException {
567 String alias = id.toString();
569 synchronized (keystore_manager) {
570 KeyStore store = keystore_manager.loadKeyStore(store_password);
572 return store.containsAlias(alias) & store.isKeyEntry(alias);
577 * Adds a trusted certificate with the specified id to the key store. The
578 * certificate replaces any existing certificate or private key stored at
581 * @param id The ID under which the certificate will be stored.
582 * @param cert Certificate for the specified ID.
583 * @throws KeyStoreException When the wrong keystore has been provided.
584 * @throws IOException For errors related to processing the keystore.
586 public void setTrustedCertificate(ID id, X509Certificate cert) throws KeyStoreException, IOException {
587 String alias = id.toString();
589 synchronized (keystore_manager) {
590 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
592 store.deleteEntry(alias);
594 store.setCertificateEntry(alias, cert);
596 keystore_manager.saveKeyStore(store, keystore_password);
601 * Adds a private key to the PSE using the specified ID. The key replaces
602 * any existing certificate or private key stored at this ID. The key is
603 * stored using the provided key passphrase.
605 * @param id The ID under which the certificate chain and private key will be stored.
606 * @param certchain The certificate chain matching the private key.
607 * @param key The private key to be stored in the kestore.
608 * @param key_password The passphrase associated with the private key or
609 * {@code null} if the key has no passphrase.
610 * @throws KeyStoreException When the wrong keystore key has been provided.
611 * @throws IOException For errors related to processing the keystore.
613 public void setKey(ID id, Certificate[] certchain, PrivateKey key, char[] key_password) throws KeyStoreException, IOException {
615 String alias = id.toString();
617 synchronized (keystore_manager) {
618 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
620 // Remove any existing entry.
621 store.deleteEntry(alias);
623 store.setKeyEntry(alias, key, key_password, certchain);
625 keystore_manager.saveKeyStore(store, keystore_password);
630 * Erases the specified id from the keystore.
632 * @param id The ID of the key or certificate to be deleted.
633 * @throws KeyStoreException When the wrong keystore password has been
635 * @throws IOException For errors related to processing the keystore.
637 public void erase(ID id) throws KeyStoreException, IOException {
638 String alias = id.toString();
640 synchronized (keystore_manager) {
641 KeyStore store = keystore_manager.loadKeyStore(keystore_password);
643 store.deleteEntry(alias);
645 keystore_manager.saveKeyStore(store, keystore_password);