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

Quick Search    Search Deep

Source code: net/jxta/impl/membership/pse/PSEMembershipService.java


1   /*
2    * Copyright (c) 2001 Sun Microsystems, Inc.  All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   *
11   * 2. Redistributions in binary form must reproduce the above copyright
12   *    notice, this list of conditions and the following disclaimer in
13   *    the documentation and/or other materials provided with the
14   *    distribution.
15   *
16   * 3. The end-user documentation included with the redistribution,
17   *    if any, must include the following acknowledgment:
18   *       "This product includes software developed by the
19   *       Sun Microsystems, Inc. for Project JXTA."
20   *    Alternately, this acknowledgment may appear in the software itself,
21   *    if and wherever such third-party acknowledgments normally appear.
22   *
23   * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
24   *    must not be used to endorse or promote products derived from this
25   *    software without prior written permission. For written
26   *    permission, please contact Project JXTA at http://www.jxta.org.
27   *
28   * 5. Products derived from this software may not be called "JXTA",
29   *    nor may "JXTA" appear in their name, without prior written
30   *    permission of Sun.
31   *
32   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
33   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35   * DISCLAIMED.  IN NO EVENT SHALL SUN MICROSYSTEMS OR
36   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
39   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
40   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
41   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
42   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43   * SUCH DAMAGE.
44   * ====================================================================
45   *
46   * This software consists of voluntary contributions made by many
47   * individuals on behalf of Project JXTA.  For more
48   * information on Project JXTA, please see
49   * <http://www.jxta.org/>.
50   *
51   * This license is based on the BSD license adopted by the Apache Foundation.
52   *
53   * $Id: PSEMembershipService.java,v 1.13 2004/11/02 00:59:45 bondolo Exp $
54   */
55  
56  package net.jxta.impl.membership.pse;
57  
58  import java.beans.PropertyChangeListener;
59  import java.beans.PropertyChangeSupport;
60  import java.io.File;
61  import java.net.URI;
62  import java.security.PrivateKey;
63  import java.security.cert.CertPath;
64  import java.security.cert.CertificateFactory;
65  import java.security.cert.X509Certificate;
66  import java.util.ArrayList;
67  import java.util.Arrays;
68  import java.util.Collections;
69  import java.util.Enumeration;
70  import java.util.Iterator;
71  import java.util.List;
72  
73  import java.io.IOException;
74  import java.security.KeyStoreException;
75  import java.security.NoSuchProviderException;
76  import java.security.cert.CertificateException;
77  import java.util.NoSuchElementException;
78  
79  import org.apache.log4j.Level;
80  import org.apache.log4j.Logger;
81  
82  import net.jxta.credential.AuthenticationCredential;
83  import net.jxta.credential.Credential;
84  import net.jxta.document.Advertisement;
85  import net.jxta.document.AdvertisementFactory;
86  import net.jxta.document.Element;
87  import net.jxta.document.MimeMediaType;
88  import net.jxta.document.StructuredDocumentFactory;
89  import net.jxta.document.StructuredDocumentUtils;
90  import net.jxta.document.XMLDocument;
91  import net.jxta.document.XMLElement;
92  import net.jxta.membership.Authenticator;
93  import net.jxta.membership.MembershipService;
94  import net.jxta.peergroup.PeerGroup;
95  import net.jxta.platform.ModuleSpecID;
96  import net.jxta.protocol.ConfigParams;
97  import net.jxta.protocol.ModuleImplAdvertisement;
98  import net.jxta.protocol.PeerAdvertisement;
99  import net.jxta.service.Service;
100 
101 import net.jxta.id.ID;
102 
103 import net.jxta.exception.PeerGroupException;
104 import net.jxta.exception.ProtocolNotSupportedException;
105 
106 import net.jxta.impl.config.Config;
107 import net.jxta.impl.protocol.Certificate;
108 import net.jxta.impl.protocol.PSEConfigAdv;
109 
110 /**
111  *  A JXTA Membership Service utilizing PKI to provide secure identities.
112  *
113  *  @see net.jxta.membership.MembershipService
114  **/
115 public final class PSEMembershipService implements MembershipService {
116     
117     /**
118      *  Log4J Logger
119      **/
120     private final static transient Logger LOG = Logger.getLogger(PSEMembershipService.class.getName());
121     
122     /**
123      * Well known service specification identifier: pse membership
124      */
125     public final static ModuleSpecID pseMembershipSpecID = (ModuleSpecID) ID.create( URI.create( ID.URIEncodingName + ":" + ID.URNNamespace + ":uuid-DeadBeefDeafBabaFeedBabe000000050306" ) );
126     
127     /**
128      * the peergroup to which this service is associated.
129      **/
130     PeerGroup group = null;
131     
132     /**
133      *  The ID assigned to this instance.
134      **/
135     private ID assignedID = null;
136     
137     /**
138      * The ModuleImplAdvertisement which was used to instantiate this service.
139      **/
140     private ModuleImplAdvertisement implAdvertisement = null;
141     
142     
143     /**
144      * The current set of principals associated with this peer within this peegroup.
145      **/
146     private final List principals;
147     
148     /**
149      * The set of AuthenticationCredentials which were used to establish the principals.
150      **/
151     private final List authCredentials;
152     
153     /**
154      *  property change support
155      **/
156     private final PropertyChangeSupport support;
157     
158     /**
159      *  the keystore we are working with.
160      **/
161     PSEConfig pseStore = null;
162     
163     /**
164      *  the default credential
165      **/
166     private PSECredential defaultCredential = null;
167     /**
168      *  The configuration we are using.
169      **/
170     private PSEConfigAdv config;
171     
172     /**
173      *  Default constructor. Normally only called by the peer group.
174      **/
175     public PSEMembershipService() throws PeerGroupException {
176         principals = new ArrayList();
177         authCredentials = new ArrayList();
178         support = new PropertyChangeSupport( getInterface() );
179     }
180     
181     /**
182      *  @inheritDoc
183      **/
184     public void addPropertyChangeListener( PropertyChangeListener listener ) {
185         support.addPropertyChangeListener(listener );
186     }
187     
188     /**
189      *  @inheritDoc
190      **/
191     public void addPropertyChangeListener( String propertyName, PropertyChangeListener listener ) {
192         support.addPropertyChangeListener( propertyName, listener );
193     }
194     
195     /**
196      *  @inheritDoc
197      **/
198     public void removePropertyChangeListener( PropertyChangeListener listener ) {
199         support.removePropertyChangeListener( listener );
200     }
201     
202     /**
203      *  @inheritDoc
204      **/
205     public void removePropertyChangeListener( String propertyName, PropertyChangeListener listener ) {
206         support.removePropertyChangeListener( propertyName, listener );
207     }
208     
209     /**
210      * {@inheritDoc}
211      **/
212     public void init( PeerGroup group, ID assignedID, Advertisement impl ) throws PeerGroupException {
213         this.group = group;
214         this.assignedID = assignedID;
215         this.implAdvertisement = (ModuleImplAdvertisement) impl;
216         
217         ConfigParams configAdv = (ConfigParams) group.getConfigAdvertisement();
218         
219         // Get our peer-defined parameters in the configAdv
220         Element param = configAdv.getServiceParam(assignedID);
221         
222         Advertisement paramsAdv = null;
223         if( null != param ) {
224             try {
225                 paramsAdv = AdvertisementFactory.newAdvertisement((XMLElement) param);
226             } catch( NoSuchElementException ignored ) {;}
227             
228             if( !(paramsAdv instanceof PSEConfigAdv) ) {
229                 throw new PeerGroupException( "Provided Advertisement was not a " + PSEConfigAdv.getAdvertisementType() );
230             }
231             
232             config = (PSEConfigAdv) paramsAdv;
233         } else {
234             throw new PeerGroupException( "No configuration information avail from ConfigParams " );
235             
236 //            config = (PSEConfigAdv) AdvertisementFactory.newAdvertisement( PSEConfigAdv.getAdvertisementType() );
237 //            
238 //            PSEUtils.IssuerInfo info = PSEUtils.genCert( group.getPeerName(), null);
239 //                    
240 //            config.setCertificate(info.cert);
241 //            // FIXME    20041013   bondolo  Initialize key.
242 //            config.setPrivateKey(info.subjectPkey, "".toCharArray());
243         }
244         
245         URI location = config.getKeyStoreLocation();
246         KeyStoreManager store_manager;
247         
248         try {
249             if( null == location ) {
250                 store_manager = new CMKeyStoreManager(config.getKeyStoreType(), config.getKeyStoreProvider(), group, assignedID);
251             } else {
252                 if( !location.isAbsolute() ) {
253                     File pseHome = new File( Config.JXTA_HOME );
254                     location = location.resolve( pseHome.toURI() );
255                 }
256                 
257                 store_manager = new URIKeyStoreManager(config.getKeyStoreType(), config.getKeyStoreProvider(), location);
258             }
259         } catch( NoSuchProviderException not_available ) {
260             throw new PeerGroupException( "Requested KeyStore provider not available", not_available );
261         } catch( KeyStoreException bad ) {
262             throw new PeerGroupException( "KeyStore failure initializing KeyStoreManager", bad );
263         }
264         
265         pseStore = new PSEConfig( store_manager, null );
266         
267         if (LOG.isEnabledFor(Level.INFO)) {
268             StringBuffer configInfo = new StringBuffer( "Configuring PSE Membership Service : " + assignedID );
269             
270             configInfo.append( "\n\tImplementation :" );
271             configInfo.append("\n\t\tModule Spec ID: " + implAdvertisement.getModuleSpecID());
272             configInfo.append("\n\t\tImpl Description : " + implAdvertisement.getDescription());
273             configInfo.append("\n\t\tImpl URI : " + implAdvertisement.getUri());
274             configInfo.append("\n\t\tImpl Code : " + implAdvertisement.getCode());
275             
276             configInfo.append( "\n\tGroup Params :" );
277             configInfo.append( "\n\t\tGroup : " + group.getPeerGroupName() );
278             configInfo.append( "\n\t\tGroup ID : " + group.getPeerGroupID() );
279             configInfo.append( "\n\t\tPeer ID : " + group.getPeerID() );
280             
281             configInfo.append( "\n\tConfiguration :" );
282             configInfo.append( "\n\t\tPSE state : " + (pseStore.isInitialized( ) ? "inited" : "new") );
283             
284             LOG.info( configInfo );
285         }
286         
287         resign();
288     }
289     
290     /**
291      * {@inheritDoc}
292      **/
293     public Service getInterface() {
294         return this;
295     }
296     
297     /**
298      * {@inheritDoc}
299      **/
300     public Advertisement getImplAdvertisement() {
301         return implAdvertisement;
302     }
303     
304     /**
305      * {@inheritDoc}
306      *
307      * <p/>Currently this service starts by itself and does not expect
308      * arguments.
309      */
310     public int startApp(String[] arg) {
311         
312         return 0;
313     }
314     
315     /**
316      * {@inheritDoc}
317      **/
318     public void stopApp() {
319         resign();
320     }
321     
322     /**
323      * {@inheritDoc}
324      *
325      * <p/>Supports methods <code>"StringAuthentication"</code>,
326      * <code>"DialogAuthentication"</code> and
327      * <code>"InteractiveAuthentication"</code> (an alias for
328      * <code>"DialogAuthentication"</code>)
329      **/
330     public Authenticator apply( AuthenticationCredential application) throws ProtocolNotSupportedException {
331         
332         String method = application.getMethod();
333         
334         if( "StringAuthentication".equals( method ) ) {
335             if( pseStore.isInitialized() ) {
336                 return new StringAuthenticator( this, application );
337             } else {
338                 return new StringAuthenticator( this, application, config.getCertificate(), config.getEncryptedPrivateKey() );
339             }
340         } else if( "DialogAuthentication".equals( method ) || "InteractiveAuthentication".equals( method ) || (null == method) ) {
341             if( pseStore.isInitialized() ) {
342                 return new DialogAuthenticator( this, application );
343             } else {
344                 return new DialogAuthenticator( this, application, config.getCertificate(), config.getEncryptedPrivateKey() );
345             }
346         } else {
347             throw new ProtocolNotSupportedException( "Authentication method not recognized" );
348         }
349     }
350     
351     /**
352      * {@inheritDoc}
353      **/
354     public Credential getDefaultCredential() {
355         return defaultCredential;
356     }
357     
358     /**
359      * Sets the default credential. Also updates the peer advertisement with
360      * the certificate of the default credential.
361      *
362      *  @param newDefault the new default credential. May also be
363      *  <code>null</code> if no default is desired.
364      **/
365     private void setDefaultCredential( PSECredential newDefault ) {
366         
367         Credential oldDefault = defaultCredential;
368         
369         synchronized( this ) {
370             defaultCredential = newDefault;
371         }
372         
373         if (LOG.isEnabledFor(Level.INFO)) {
374             LOG.info( "New Default credential : " + newDefault );
375         }
376         
377         try {
378             // include the root cert in the peer advertisement
379             PeerAdvertisement peeradv = group.getPeerAdvertisement();
380             
381             if( null != newDefault ) {
382                 // include the root cert in the peer advertisement
383                 XMLDocument paramDoc = (XMLDocument) StructuredDocumentFactory.newStructuredDocument( MimeMediaType.XMLUTF8, "Parm");
384                 
385                 Certificate peerCerts = new Certificate();
386                 peerCerts.setCertificates( newDefault.getCertificateChain() );
387                 
388                 XMLDocument peerCertsAsDoc = (XMLDocument) peerCerts.getDocument( MimeMediaType.XMLUTF8 );
389                 
390                 StructuredDocumentUtils.copyElements( paramDoc, paramDoc, peerCertsAsDoc, "RootCert" );
391                 
392                 peeradv.putServiceParam( PeerGroup.peerGroupClassID, paramDoc );
393             } else {
394                 peeradv.removeServiceParam( PeerGroup.peerGroupClassID );
395             }
396         } catch ( Exception ignored ) {;}
397         
398         support.firePropertyChange( "defaultCredential", oldDefault, newDefault );
399     }
400     
401     /**
402      * {@inheritDoc}
403      **/
404     public Enumeration getCurrentCredentials() {
405         List credList = Arrays.asList( principals.toArray() );
406         
407         return Collections.enumeration(credList);
408     }
409     
410     /**
411      * {@inheritDoc}
412      **/
413     public Enumeration getAuthCredentials() {
414         List credList = Arrays.asList( authCredentials.toArray() );
415         
416         return Collections.enumeration(credList);
417     }
418     
419     /**
420      * {@inheritDoc}
421      **/
422     public Credential join( Authenticator authenticated ) throws PeerGroupException {
423         
424         if( this != authenticated.getSourceService() ) {
425             throw new ClassCastException( "This is not my authenticator!" );
426         }
427         
428         if( !authenticated.isReadyForJoin() ) {
429             throw new PeerGroupException( "Authenticator not ready to join!" );
430         }
431         
432         PSECredential newCred;
433         
434         char [] store_password = null;
435         ID identity;
436         char [] key_password = null;
437         
438         try {
439             if( authenticated instanceof StringAuthenticator ) {
440                 StringAuthenticator auth = (StringAuthenticator) authenticated;
441                 
442                 store_password = auth.getAuth1_KeyStorePassword();
443                 identity = auth.getAuth2Identity();
444                 key_password = auth.getAuth3_IdentityPassword();
445             } else {
446                 if (LOG.isEnabledFor(Level.WARN)) {
447                     LOG.warn( "I dont know how to deal with this authenticator " + authenticated );
448                 }
449                 
450                 throw new PeerGroupException( "I dont know how to deal with this authenticator" );
451             }
452             
453             if( null != store_password ) {
454                 pseStore.setKeyStorePassword( store_password );
455             }
456             
457             boolean pseInited = pseStore.isInitialized( );
458             
459             if ( !pseInited ) {
460                 if ( LOG.isEnabledFor(Level.INFO) ) {
461                     LOG.info("Creating new PSE key store");
462                 }
463                 
464                 try {
465                     X509Certificate [] seed_cert = config.getCertificateChain();
466                     
467                     if( null == seed_cert ) {
468                         throw new IOException( "Could not read root certificate chain" );
469                     }
470                     
471                     PrivateKey seedPrivKey = config.getPrivateKey( key_password );
472                     
473                     if( null == seedPrivKey ) {
474                         throw new IOException( "Could not read private key" );
475                     }
476                     
477                     pseStore.initialize( );
478                     
479                     pseStore.setKey( identity, seed_cert, seedPrivKey,  key_password );
480                 } catch ( IOException failed ) {
481                     LOG.fatal("Could not create PSE!", failed );
482                     
483                     throw new PeerGroupException( "Could not create PSE!", failed );
484                 } catch ( KeyStoreException failed ) {
485                     LOG.fatal("Could not create PSE!", failed );
486                     
487                     throw new PeerGroupException( "Could not create PSE!", failed );
488                 }
489             }
490             
491             try {
492                 X509Certificate certList[] = (X509Certificate[]) pseStore.getTrustedCertificateChain( identity );
493                 
494                 if( null == certList ) {
495                     certList = new X509Certificate[1];
496                     
497                     certList[0] = pseStore.getTrustedCertificate( identity );
498                 }
499                 
500                 PrivateKey privateKey = pseStore.getKey( identity, key_password );
501                 
502                 CertificateFactory cf = CertificateFactory.getInstance( "X.509" );
503                 
504                 CertPath certs = cf.generateCertPath( Arrays.asList( certList ) );
505                 
506                 newCred = new PSECredential( this, identity, certs, privateKey );
507                 
508                 synchronized( this ) {
509                     principals.add( newCred );
510                     
511                     authCredentials.add( authenticated.getAuthenticationCredential() );
512                 }
513             } catch( IOException failed ) {
514                 if (LOG.isEnabledFor(Level.WARN)) {
515                     LOG.warn( "Could not create credential.", failed );
516                 }
517                 
518                 throw new PeerGroupException( "Could not create credential.", failed );
519             } catch( KeyStoreException failed ) {
520                 if (LOG.isEnabledFor(Level.WARN)) {
521                     LOG.warn( "Could not create credential.", failed );
522                 }
523                 
524                 throw new PeerGroupException( "Could not create credential.", failed );
525             } catch( CertificateException failed ) {
526                 if (LOG.isEnabledFor(Level.WARN)) {
527                     LOG.warn( "Could not create credential.", failed );
528                 }
529                 
530                 throw new PeerGroupException( "Could not create credential.", failed );
531             }
532         } finally {
533             if( null != store_password ) {
534                 Arrays.fill( store_password, '\0' );
535             }
536             
537             if( null != key_password ) {
538                 Arrays.fill( key_password, '\0' );
539             }
540         }
541         
542         // XXX bondolo potential but unlikely race condition here.
543         if( null == getDefaultCredential() ) {
544             setDefaultCredential( newCred );
545         }
546         
547         support.firePropertyChange( "addCredential", null, newCred );
548         
549         return newCred;
550     }
551     
552     /**
553      * {@inheritDoc}
554      **/
555     public void resign() {
556         Iterator eachCred = Arrays.asList( principals.toArray() ).iterator();
557         
558         synchronized( this ) {
559             principals.clear();
560             authCredentials.clear();
561         }
562         
563         setDefaultCredential( null );
564         
565         while( eachCred.hasNext() ) {
566             PSECredential aCred = (PSECredential) eachCred.next();
567             
568             aCred.setValid( false );
569         }
570     }
571     
572     /**
573      * {@inheritDoc}
574      **/
575     public Credential makeCredential(Element element) {
576         
577         return new PSECredential( this, element );
578     }
579     
580     /**
581      *  Returns the key store object associated with this PSE Membership Service.
582      **/
583     public PSEConfig getPSEConfig() {
584         return pseStore;
585     }
586 }