KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > servicemix > soap > handlers > security > WSSecurityHandler


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.servicemix.soap.handlers.security;
18
19 import java.io.IOException JavaDoc;
20 import java.security.GeneralSecurityException JavaDoc;
21 import java.security.Principal JavaDoc;
22 import java.security.cert.X509Certificate JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.Vector JavaDoc;
29
30 import javax.security.auth.Subject JavaDoc;
31 import javax.security.auth.callback.CallbackHandler JavaDoc;
32 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
33 import javax.xml.namespace.QName JavaDoc;
34
35 import org.apache.servicemix.jbi.security.auth.AuthenticationService;
36 import org.apache.servicemix.jbi.security.keystore.KeystoreManager;
37 import org.apache.servicemix.soap.Context;
38 import org.apache.servicemix.soap.Handler;
39 import org.apache.servicemix.soap.SoapFault;
40 import org.apache.ws.security.WSConstants;
41 import org.apache.ws.security.WSDocInfo;
42 import org.apache.ws.security.WSDocInfoStore;
43 import org.apache.ws.security.WSPasswordCallback;
44 import org.apache.ws.security.WSSConfig;
45 import org.apache.ws.security.WSSecurityEngine;
46 import org.apache.ws.security.WSSecurityEngineResult;
47 import org.apache.ws.security.WSSecurityException;
48 import org.apache.ws.security.WSUsernameTokenPrincipal;
49 import org.apache.ws.security.components.crypto.Crypto;
50 import org.apache.ws.security.handler.RequestData;
51 import org.apache.ws.security.handler.WSHandler;
52 import org.apache.ws.security.handler.WSHandlerConstants;
53 import org.apache.ws.security.handler.WSHandlerResult;
54 import org.apache.ws.security.message.token.Timestamp;
55 import org.apache.ws.security.processor.Processor;
56 import org.apache.ws.security.util.WSSecurityUtil;
57 import org.w3c.dom.Document JavaDoc;
58 import org.w3c.dom.Element JavaDoc;
59
60 /**
61  * WS-Security handler.
62  * This handler is heavily based on xfire-ws-security project.
63  *
64  * @org.apache.xbean.XBean element="ws-security"
65  */

66 public class WSSecurityHandler extends WSHandler implements Handler JavaDoc {
67
68     private Map JavaDoc properties = new HashMap JavaDoc();
69     private String JavaDoc domain = "servicemix-domain";
70     private AuthenticationService authenticationService;
71     private boolean required;
72     private String JavaDoc sendAction;
73     private String JavaDoc receiveAction;
74     private String JavaDoc actor;
75     private String JavaDoc username;
76     private String JavaDoc keystore;
77     private Crypto crypto;
78     private CallbackHandler JavaDoc handler = new DefaultHandler();
79     
80     private ThreadLocal JavaDoc currentSubject = new ThreadLocal JavaDoc();
81
82     public WSSecurityHandler() {
83         WSSecurityEngine.setWssConfig(new ServiceMixWssConfig());
84     }
85     
86     /**
87      * @return the authenticationService
88      */

89     public AuthenticationService getAuthenticationService() {
90         return authenticationService;
91     }
92
93     /**
94      * @param authenticationService the authenticationService to set
95      */

96     public void setAuthenticationService(AuthenticationService authenticationService) {
97         this.authenticationService = authenticationService;
98     }
99
100     private class ServiceMixWssConfig extends WSSConfig {
101         public Processor getProcessor(QName JavaDoc el) throws WSSecurityException {
102             if (el.equals(WSSecurityEngine.SIGNATURE)) {
103                 return new SignatureProcessor();
104             } else {
105                 return super.getProcessor(el);
106             }
107         }
108     }
109     
110     private class SignatureProcessor extends org.apache.ws.security.processor.SignatureProcessor {
111         private String JavaDoc signatureId;
112         public void handleToken(Element JavaDoc elem, Crypto crypto, Crypto decCrypto, CallbackHandler JavaDoc cb, WSDocInfo wsDocInfo, Vector JavaDoc returnResults, WSSConfig wsc) throws WSSecurityException {
113             WSDocInfoStore.store(wsDocInfo);
114             X509Certificate JavaDoc[] returnCert = new X509Certificate JavaDoc[1];
115             Set JavaDoc returnElements = new HashSet JavaDoc();
116             byte[][] signatureValue = new byte[1][];
117             Principal JavaDoc lastPrincipalFound = null;
118             try {
119                 lastPrincipalFound = verifyXMLSignature((Element JavaDoc) elem,
120                         crypto, returnCert, returnElements, signatureValue);
121                 if (lastPrincipalFound instanceof WSUsernameTokenPrincipal) {
122                     WSUsernameTokenPrincipal p = (WSUsernameTokenPrincipal) lastPrincipalFound;
123                     checkUser(p.getName(), p.getPassword());
124                 } else {
125                     checkUser(returnCert[0].getSubjectX500Principal().getName(), returnCert[0]);
126                 }
127             } catch (GeneralSecurityException JavaDoc e) {
128                 throw new WSSecurityException("Unable to authenticate user", e);
129             } finally {
130                 WSDocInfoStore.delete(wsDocInfo);
131             }
132             if (lastPrincipalFound instanceof WSUsernameTokenPrincipal) {
133                 returnResults.add(0, new WSSecurityEngineResult(
134                         WSConstants.UT_SIGN, lastPrincipalFound, null,
135                         returnElements, signatureValue[0]));
136
137             } else {
138                 returnResults.add(0, new WSSecurityEngineResult(
139                         WSConstants.SIGN, lastPrincipalFound,
140                         returnCert[0], returnElements, signatureValue[0]));
141             }
142             signatureId = elem.getAttributeNS(null, "Id");
143         }
144         public String JavaDoc getId() {
145             return signatureId;
146         }
147     }
148     
149     /**
150      * @return the username
151      */

152     public String JavaDoc getUsername() {
153         return username;
154     }
155
156     /**
157      * @param username the username to set
158      */

159     public void setUsername(String JavaDoc username) {
160         this.username = username;
161     }
162
163     /**
164      * @return the crypto
165      */

166     public Crypto getCrypto() {
167         return crypto;
168     }
169
170     /**
171      * @param crypto the crypto to set
172      */

173     public void setCrypto(Crypto crypto) {
174         this.crypto = crypto;
175     }
176
177     /**
178      * @return the actor
179      */

180     public String JavaDoc getActor() {
181         return actor;
182     }
183
184     /**
185      * @param actor the actor to set
186      */

187     public void setActor(String JavaDoc actor) {
188         this.actor = actor;
189     }
190
191     /**
192      * @return the domain
193      */

194     public String JavaDoc getDomain() {
195         return domain;
196     }
197
198     /**
199      * @param domain the domain to set
200      */

201     public void setDomain(String JavaDoc domain) {
202         this.domain = domain;
203     }
204
205     /**
206      * @return the receiveAction
207      */

208     public String JavaDoc getReceiveAction() {
209         return receiveAction;
210     }
211
212     /**
213      * @param receiveAction the receiveAction to set
214      */

215     public void setReceiveAction(String JavaDoc receiveAction) {
216         this.receiveAction = receiveAction;
217     }
218
219     /**
220      * @return the action
221      */

222     public String JavaDoc getSendAction() {
223         return sendAction;
224     }
225
226     /**
227      * @param action the action to set
228      */

229     public void setSendAction(String JavaDoc action) {
230         this.sendAction = action;
231     }
232
233     /**
234      * @return the required
235      */

236     public boolean isRequired() {
237         return required;
238     }
239
240     public boolean requireDOM() {
241         return true;
242     }
243
244     /**
245      * @param required the required to set
246      */

247     public void setRequired(boolean required) {
248         this.required = required;
249     }
250
251     public Object JavaDoc getOption(String JavaDoc key) {
252         return properties.get(key);
253     }
254
255     public void setOption(String JavaDoc key, Object JavaDoc value) {
256         this.properties.put(key, value);
257     }
258
259     public String JavaDoc getPassword(Object JavaDoc msgContext) {
260         return (String JavaDoc) ((Context) msgContext).getProperty("password");
261     }
262
263     public Object JavaDoc getProperty(Object JavaDoc msgContext, String JavaDoc key) {
264         if (WSHandlerConstants.PW_CALLBACK_REF.equals(key)) {
265             return handler;
266         }
267         return ((Context) msgContext).getProperty(key);
268     }
269
270     public void setPassword(Object JavaDoc msgContext, String JavaDoc password) {
271         ((Context) msgContext).setProperty("password", password);
272     }
273
274     public void setProperty(Object JavaDoc msgContext, String JavaDoc key, Object JavaDoc value) {
275         ((Context) msgContext).setProperty(key, value);
276     }
277
278     protected Crypto loadDecryptionCrypto(RequestData reqData) throws WSSecurityException {
279         return crypto;
280     }
281     
282     protected Crypto loadEncryptionCrypto(RequestData reqData) throws WSSecurityException {
283         return crypto;
284     }
285     
286     public Crypto loadSignatureCrypto(RequestData reqData) throws WSSecurityException {
287         return crypto;
288     }
289     
290     public void onReceive(Context context) throws Exception JavaDoc {
291         RequestData reqData = new RequestData();
292         init(context);
293         try {
294             reqData.setNoSerialization(true);
295             reqData.setMsgContext(context);
296
297             Vector JavaDoc actions = new Vector JavaDoc();
298             String JavaDoc action = this.receiveAction;
299             if (action == null) {
300                 throw new IllegalStateException JavaDoc("WSSecurityHandler: No receiveAction defined");
301             }
302             int doAction = WSSecurityUtil.decodeAction(action, actions);
303
304             Document JavaDoc doc = context.getInMessage().getDocument();
305             if (doc == null) {
306                 throw new IllegalStateException JavaDoc("WSSecurityHandler: The soap message has not been parsed using DOM");
307             }
308
309             /*
310              * Get and check the Signature specific parameters first because
311              * they may be used for encryption too.
312              */

313             doReceiverAction(doAction, reqData);
314
315             Vector JavaDoc wsResult = null;
316
317             try {
318                 wsResult = secEngine.processSecurityHeader(
319                                 doc, actor, handler,
320                                 reqData.getSigCrypto(),
321                                 reqData.getDecCrypto());
322             } catch (WSSecurityException ex) {
323                 throw new SoapFault(ex);
324             }
325
326             if (wsResult == null) { // no security header found
327
if (doAction == WSConstants.NO_SECURITY) {
328                     return;
329                 } else {
330                     throw new SoapFault(new WSSecurityException(
331                                     "WSSecurityHandler: Request does not contain required Security header"));
332                 }
333             }
334
335             if (reqData.getWssConfig().isEnableSignatureConfirmation()) {
336                 checkSignatureConfirmation(reqData, wsResult);
337             }
338
339             /*
340              * Now we can check the certificate used to sign the message. In the
341              * following implementation the certificate is only trusted if
342              * either it itself or the certificate of the issuer is installed in
343              * the keystore.
344              *
345              * Note: the method verifyTrust(X509Certificate) allows custom
346              * implementations with other validation algorithms for subclasses.
347              */

348
349             // Extract the signature action result from the action vector
350
WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(wsResult, WSConstants.SIGN);
351
352             if (actionResult != null) {
353                 X509Certificate JavaDoc returnCert = actionResult.getCertificate();
354
355                 if (returnCert != null) {
356                     if (!verifyTrust(returnCert, reqData)) {
357                         throw new SoapFault(new WSSecurityException(
358                                         "WSSecurityHandler: the certificate used for the signature is not trusted"));
359                     }
360                 }
361             }
362
363             /*
364              * Perform further checks on the timestamp that was transmitted in
365              * the header. In the following implementation the timestamp is
366              * valid if it was created after (now-ttl), where ttl is set on
367              * server side, not by the client.
368              *
369              * Note: the method verifyTimestamp(Timestamp) allows custom
370              * implementations with other validation algorithms for subclasses.
371              */

372
373             // Extract the timestamp action result from the action vector
374
actionResult = WSSecurityUtil.fetchActionResult(wsResult, WSConstants.TS);
375
376             if (actionResult != null) {
377                 Timestamp timestamp = actionResult.getTimestamp();
378
379                 if (timestamp != null) {
380                     if (!verifyTimestamp(timestamp, decodeTimeToLive(reqData))) {
381                         throw new SoapFault(new WSSecurityException(
382                                         "WSSecurityHandler: the timestamp could not be validated"));
383                     }
384                 }
385             }
386
387             /*
388              * now check the security actions: do they match, in right order?
389              */

390             if (!checkReceiverResults(wsResult, actions)) {
391                 throw new SoapFault(new WSSecurityException(
392                                 "WSSecurityHandler: security processing failed (actions mismatch)"));
393
394             }
395             /*
396              * All ok up to this point. Now construct and setup the security
397              * result structure. The service may fetch this and check it.
398              */

399             Vector JavaDoc results = null;
400             if ((results = (Vector JavaDoc) context.getProperty(WSHandlerConstants.RECV_RESULTS)) == null) {
401                 results = new Vector JavaDoc();
402                 context.setProperty(WSHandlerConstants.RECV_RESULTS, results);
403             }
404             WSHandlerResult rResult = new WSHandlerResult(actor, wsResult);
405             results.add(0, rResult);
406
407             // Add principals to the message
408
for (Iterator JavaDoc iter = results.iterator(); iter.hasNext();) {
409                 WSHandlerResult hr = (WSHandlerResult) iter.next();
410                 for (Iterator JavaDoc it = hr.getResults().iterator(); it.hasNext();) {
411                     WSSecurityEngineResult er = (WSSecurityEngineResult) it.next();
412                     if (er.getPrincipal() != null) {
413                         context.getInMessage().addPrincipal(er.getPrincipal());
414                     }
415                 }
416             }
417             Subject JavaDoc s = (Subject JavaDoc) currentSubject.get();
418             if (s != null) {
419                 for (Iterator JavaDoc iterator = s.getPrincipals().iterator(); iterator.hasNext();) {
420                     Principal JavaDoc p = (Principal JavaDoc) iterator.next();
421                     context.getInMessage().addPrincipal(p);
422                 }
423             }
424
425         } finally {
426             reqData.clear();
427             currentSubject.set(null);
428         }
429     }
430
431     public void onReply(Context context) throws Exception JavaDoc {
432         // TODO Auto-generated method stub
433

434     }
435     
436     public void onFault(Context context) throws Exception JavaDoc {
437         // TODO Auto-generated method stub
438

439     }
440
441     public void onSend(Context context) throws Exception JavaDoc {
442         RequestData reqData = new RequestData();
443         reqData.setMsgContext(context);
444         init(context);
445         /*
446          * The overall try, just to have a finally at the end to perform some
447          * housekeeping.
448          */

449         try {
450             /*
451              * Get the action first.
452              */

453             Vector JavaDoc actions = new Vector JavaDoc();
454             String JavaDoc action = this.sendAction;
455             if (action == null) {
456                 throw new IllegalStateException JavaDoc("WSSecurityHandler: No sendAction defined");
457             }
458             
459             int doAction = WSSecurityUtil.decodeAction(action, actions);
460             if (doAction == WSConstants.NO_SECURITY) {
461                 return;
462             }
463
464             /*
465              * For every action we need a username, so get this now. The
466              * username defined in the deployment descriptor takes precedence.
467              */

468             reqData.setUsername((String JavaDoc) getOption(WSHandlerConstants.USER));
469             if (reqData.getUsername() == null || reqData.getUsername().equals("")) {
470                 String JavaDoc username = (String JavaDoc) getProperty(reqData.getMsgContext(), WSHandlerConstants.USER);
471                 if (username != null) {
472                     reqData.setUsername(username);
473                 } else {
474                     reqData.setUsername(this.username);
475                 }
476             }
477             
478             /*
479              * Now we perform some set-up for UsernameToken and Signature
480              * functions. No need to do it for encryption only. Check if
481              * username is available and then get a passowrd.
482              */

483             if ((doAction & (WSConstants.SIGN | WSConstants.UT | WSConstants.UT_SIGN)) != 0) {
484                 /*
485                  * We need a username - if none throw an XFireFault. For
486                  * encryption there is a specific parameter to get a username.
487                  */

488                 if (reqData.getUsername() == null || reqData.getUsername().equals("")) {
489                     throw new IllegalStateException JavaDoc("WSSecurityHandler: Empty username for specified action");
490                 }
491             }
492             /*
493              * Now get the SOAP part from the request message and convert it
494              * into a Document.
495              *
496              * Now we can perform our security operations on this request.
497              */

498             Document JavaDoc doc = context.getInMessage().getDocument();
499             if (doc == null) {
500                 throw new IllegalStateException JavaDoc("WSSecurityHandler: The soap message has not been parsed using DOM");
501             }
502             
503             doSenderAction(doAction, doc, reqData, actions, true);
504         }
505         catch (WSSecurityException e) {
506             throw new SoapFault(e);
507         }
508         finally {
509             reqData.clear();
510             reqData = null;
511         }
512     }
513
514     public void onAnswer(Context context) {
515         // TODO Auto-generated method stub
516

517     }
518     
519     protected void checkUser(String JavaDoc user, Object JavaDoc credentials) throws GeneralSecurityException JavaDoc {
520         if (authenticationService == null) {
521             throw new IllegalArgumentException JavaDoc("authenticationService is null");
522         }
523         Subject JavaDoc subject = (Subject JavaDoc) currentSubject.get();
524         if (subject == null) {
525             subject = new Subject JavaDoc();
526             currentSubject.set(subject);
527         }
528         authenticationService.authenticate(subject, domain, user, credentials);
529     }
530
531     protected class DefaultHandler extends BaseSecurityCallbackHandler {
532
533         protected void processSignature(WSPasswordCallback callback) throws IOException JavaDoc, UnsupportedCallbackException JavaDoc {
534             callback.setPassword("");
535         }
536         
537         protected void processUsernameTokenUnkown(WSPasswordCallback callback) throws IOException JavaDoc, UnsupportedCallbackException JavaDoc {
538             try {
539                 checkUser(callback.getIdentifer(), callback.getPassword());
540             } catch (GeneralSecurityException JavaDoc e) {
541                 throw new UnsupportedCallbackException JavaDoc(callback, "Unable to authenticate user");
542             }
543         }
544
545     }
546
547     /**
548      * @return the keystore
549      */

550     public String JavaDoc getKeystore() {
551         return keystore;
552     }
553
554     /**
555      * @param keystore the keystore to set
556      */

557     public void setKeystore(String JavaDoc keystore) {
558         this.keystore = keystore;
559     }
560     
561     protected void init(Context context) {
562         currentSubject.set(null);
563         if (context.getProperty(Context.AUTHENTICATION_SERVICE) != null) {
564             setAuthenticationService((AuthenticationService) context.getProperty(Context.AUTHENTICATION_SERVICE));
565         }
566         if (crypto == null && context.getProperty(Context.KEYSTORE_MANAGER) != null) {
567             KeystoreManager km = (KeystoreManager) context.getProperty(Context.KEYSTORE_MANAGER);
568             setCrypto(new KeystoreInstanceCrypto(km, keystore));
569         }
570     }
571
572 }
573
Popular Tags