View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.handler;
21  
22  import java.security.cert.X509Certificate;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Properties;
30  import java.util.concurrent.ConcurrentHashMap;
31  import java.util.regex.Pattern;
32  import java.util.regex.PatternSyntaxException;
33  
34  import javax.security.auth.callback.Callback;
35  import javax.security.auth.callback.CallbackHandler;
36  
37  import org.apache.ws.security.WSConstants;
38  import org.apache.ws.security.WSEncryptionPart;
39  import org.apache.ws.security.WSPasswordCallback;
40  import org.apache.ws.security.WSSConfig;
41  import org.apache.ws.security.WSSecurityEngine;
42  import org.apache.ws.security.WSSecurityEngineResult;
43  import org.apache.ws.security.WSSecurityException;
44  import org.apache.ws.security.action.Action;
45  import org.apache.ws.security.components.crypto.AlgorithmSuite;
46  import org.apache.ws.security.components.crypto.Crypto;
47  import org.apache.ws.security.components.crypto.CryptoFactory;
48  import org.apache.ws.security.message.WSSecHeader;
49  import org.apache.ws.security.message.token.SignatureConfirmation;
50  import org.apache.ws.security.util.Loader;
51  import org.apache.ws.security.util.StringUtil;
52  import org.apache.ws.security.util.WSSecurityUtil;
53  import org.w3c.dom.Document;
54  
55  
56  /**
57   * Extracted from WSDoAllReceiver and WSDoAllSender
58   * Extended to all passwordless UsernameTokens and configurable identities.
59   *
60   * @author Davanum Srinivas (dims@yahoo.com).
61   * @author Werner Dittmann (Werner.Dittmann@t-online.de).
62   * @author Marcel Ammerlaan (marcel.ammerlaan@gmail.com).
63   */
64  public abstract class WSHandler {
65      private static org.apache.commons.logging.Log log = 
66          org.apache.commons.logging.LogFactory.getLog(WSHandler.class);
67      protected WSSecurityEngine secEngine = new WSSecurityEngine();
68      protected Map<String, Crypto> cryptos = new ConcurrentHashMap<String, Crypto>();
69  
70      private boolean doDebug = log.isDebugEnabled();
71  
72      /**                                                             
73       * Performs all defined security actions to set-up the SOAP request.
74       * 
75       * 
76       * @param doAction a set defining the actions to do 
77       * @param doc   the request as DOM document 
78       * @param reqData a data storage to pass values around between methods
79       * @param actions a list holding the actions to do in the order defined
80       *                in the deployment file or property
81       * @throws WSSecurityException
82       */
83      @SuppressWarnings("unchecked")
84      protected void doSenderAction(
85              int doAction, 
86              Document doc,
87              RequestData reqData, 
88              List<Integer> actions, 
89              boolean isRequest
90      ) throws WSSecurityException {
91  
92          boolean mu = decodeMustUnderstand(reqData);
93  
94          WSSConfig wssConfig = reqData.getWssConfig();
95          if (wssConfig == null) {
96              wssConfig = secEngine.getWssConfig();
97          }
98  
99          boolean enableSigConf = decodeEnableSignatureConfirmation(reqData);
100         wssConfig.setEnableSignatureConfirmation(
101             enableSigConf || ((doAction & WSConstants.SC) != 0)
102         );
103         wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
104 
105         wssConfig.setPrecisionInMilliSeconds(
106             decodeTimestampPrecision(reqData)
107         );
108         reqData.setWssConfig(wssConfig);
109 
110         Object mc = reqData.getMsgContext();
111         String actor = getString(WSHandlerConstants.ACTOR, mc);
112         reqData.setActor(actor);
113 
114         WSSecHeader secHeader = new WSSecHeader(actor, mu);
115         secHeader.insertSecurityHeader(doc);
116 
117         reqData.setSecHeader(secHeader);
118         reqData.setSoapConstants(
119             WSSecurityUtil.getSOAPConstants(doc.getDocumentElement())
120         );
121         wssConfig.setWsiBSPCompliant(decodeBSPCompliance(reqData));
122         /*
123          * Here we have action, username, password, and actor, mustUnderstand.
124          * Now get the action specific parameters.
125          */
126         if ((doAction & WSConstants.UT) == WSConstants.UT) {
127             decodeUTParameter(reqData);
128         }
129         /*
130          * Here we have action, username, password, and actor, mustUnderstand.
131          * Now get the action specific parameters.
132          */
133         if ((doAction & WSConstants.UT_SIGN) == WSConstants.UT_SIGN) {
134             decodeUTParameter(reqData);
135             decodeSignatureParameter(reqData);
136         }
137         /*
138          * Get and check the Signature specific parameters first because they
139          * may be used for encryption too.
140          */
141         if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
142             if (reqData.getSigCrypto() == null) {
143                 reqData.setSigCrypto(loadSignatureCrypto(reqData));
144             }
145             decodeSignatureParameter(reqData);
146         }
147         /*
148          * If we need to handle signed SAML token then we may need the
149          * Signature parameters. The handle procedure loads the signature crypto
150          * file on demand, thus don't do it here.
151          */
152         if ((doAction & WSConstants.ST_SIGNED) == WSConstants.ST_SIGNED) {
153             decodeSignatureParameter(reqData);
154         }
155         /*
156          * Set and check the encryption specific parameters, if necessary take
157          * over signature parameters username and crypto instance.
158          */
159         if ((doAction & WSConstants.ENCR) == WSConstants.ENCR) {
160             if (reqData.getEncCrypto() == null) {
161                 reqData.setEncCrypto(loadEncryptionCrypto(reqData));
162             }
163             decodeEncryptionParameter(reqData);
164         }
165         /*
166          * If after all the parsing no Signature parts defined, set here a
167          * default set. This is necessary because we add SignatureConfirmation
168          * and therefore the default (Body) must be set here. The default setting
169          * in WSSignEnvelope doesn't work because the vector is not empty anymore.
170          */
171         if (reqData.getSignatureParts().isEmpty()) {
172             WSEncryptionPart encP = new WSEncryptionPart(reqData.getSoapConstants()
173                     .getBodyQName().getLocalPart(), reqData.getSoapConstants()
174                     .getEnvelopeURI(), "Content");
175             reqData.getSignatureParts().add(encP);
176         }
177         /*
178          * If SignatureConfirmation is enabled and this is a response then
179          * insert SignatureConfirmation elements, note their wsu:id in the signature
180          * parts. They will be signed automatically during a (probably) defined
181          * SIGN action.
182          */
183         if (wssConfig.isEnableSignatureConfirmation() && !isRequest) {
184             String done = 
185                 (String)getProperty(reqData.getMsgContext(), WSHandlerConstants.SIG_CONF_DONE);
186             if (done == null) {
187                 wssConfig.getAction(WSConstants.SC).execute(this, WSConstants.SC, doc, reqData);
188             }
189         }
190         
191         // See if the Signature and Timestamp actions (in that order) are defined, and if
192         // the Timestamp is to be signed. In this case we need to swap the actions, as the 
193         // Timestamp must appear in the security header first for signature creation to work.
194         List<Integer> actionsToPerform = actions;
195         if (actions.contains(WSConstants.SIGN) && actions.contains(WSConstants.TS)
196             && (actions.indexOf(WSConstants.SIGN) < actions.indexOf(WSConstants.TS))) {
197             boolean signTimestamp = false;
198             for (WSEncryptionPart encP : reqData.getSignatureParts()) {
199                 if (WSConstants.WSU_NS.equals(encP.getNamespace()) 
200                     && "Timestamp".equals(encP.getName())) {
201                     signTimestamp = true;
202                 }
203             }
204             if (signTimestamp) {
205                 actionsToPerform = new ArrayList<Integer>(actions);
206                 Collections.copy(actionsToPerform, actions);
207                 actionsToPerform.remove(actions.indexOf(WSConstants.SIGN));
208                 actionsToPerform.add(WSConstants.SIGN);
209                 reqData.setAppendSignatureAfterTimestamp(true);
210             }
211         }
212         
213         /*
214          * Here we have all necessary information to perform the requested
215          * action(s).
216          */
217         for (Integer actionToDo : actionsToPerform) {
218             if (doDebug) {
219                 log.debug("Performing Action: " + actionToDo);
220             }
221 
222             switch (actionToDo) {
223             case WSConstants.UT:
224             case WSConstants.ENCR:
225             case WSConstants.SIGN:
226             case WSConstants.ST_SIGNED:
227             case WSConstants.ST_UNSIGNED:
228             case WSConstants.TS:
229             case WSConstants.UT_SIGN:
230                 wssConfig.getAction(actionToDo).execute(this, actionToDo, doc, reqData);
231                 break;
232                 //
233                 // Handle any "custom" actions, similarly,
234                 // but to preserve behavior from previous
235                 // versions, consume (but log) action lookup failures.
236                 //
237             default:
238                 Action doit = null;
239             try {
240                 doit = wssConfig.getAction(actionToDo);
241             } catch (final WSSecurityException e) {
242                 log.warn(
243                         "Error trying to locate a custom action (" + actionToDo + ")", 
244                         e
245                 );
246             }
247             if (doit != null) {
248                 doit.execute(this, actionToDo, doc, reqData);
249             }
250             }
251         }
252         
253         /*
254          * If this is a request then store all signature values. Add ours to
255          * already gathered values because of chained handlers, e.g. for
256          * other actors.
257          */
258         if (wssConfig.isEnableSignatureConfirmation() 
259             && isRequest && reqData.getSignatureValues().size() > 0) {
260             List<byte[]> savedSignatures = 
261                 (List<byte[]>)getProperty(reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV);
262             if (savedSignatures == null) {
263                 savedSignatures = new ArrayList<byte[]>();
264                 setProperty(
265                     reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV, savedSignatures
266                 );
267             }
268             savedSignatures.addAll(reqData.getSignatureValues());
269         }
270     }
271 
272     protected void doReceiverAction(int doAction, RequestData reqData)
273         throws WSSecurityException {
274 
275         WSSConfig wssConfig = reqData.getWssConfig();
276         if (wssConfig == null) {
277             wssConfig = secEngine.getWssConfig();
278         }
279         boolean enableSigConf = decodeEnableSignatureConfirmation(reqData);
280         wssConfig.setEnableSignatureConfirmation(
281             enableSigConf || ((doAction & WSConstants.SC) != 0)
282         );
283         wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
284         if (decodePasswordTypeStrict(reqData)) {
285             String passwordType = decodePasswordType(reqData);
286             wssConfig.setRequiredPasswordType(passwordType);
287         }
288         wssConfig.setTimeStampTTL(decodeTimeToLive(reqData));
289         wssConfig.setTimeStampFutureTTL(decodeFutureTimeToLive(reqData));
290         wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
291         wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
292         wssConfig.setAllowNamespaceQualifiedPasswordTypes(
293             decodeNamespaceQualifiedPasswordTypes(reqData)
294         );
295         wssConfig.setSecretKeyLength(reqData.getSecretKeyLength());
296         wssConfig.setWsiBSPCompliant(decodeBSPCompliance(reqData));
297         reqData.setWssConfig(wssConfig);
298 
299         if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
300             decodeSignatureParameter2(reqData);
301         }
302         
303         if (((doAction & WSConstants.ST_SIGNED) == WSConstants.ST_SIGNED)
304             || ((doAction & WSConstants.ST_UNSIGNED) == WSConstants.ST_UNSIGNED)) {
305             decodeSignatureParameter2(reqData);
306         }
307         
308         if ((doAction & WSConstants.ENCR) == WSConstants.ENCR) {
309             decodeDecryptionParameter(reqData);
310         }
311         decodeRequireSignedEncryptedDataElements(reqData);
312     }
313 
314     protected boolean checkReceiverResults(
315         List<WSSecurityEngineResult> wsResult, List<Integer> actions
316     ) {
317         int size = actions.size();
318         int ai = 0;
319         for (WSSecurityEngineResult result : wsResult) {
320             final Integer actInt = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
321             int act = actInt.intValue();
322             if (act == WSConstants.SC || act == WSConstants.BST) {
323                 continue;
324             }
325             
326             if (ai >= size || actions.get(ai++).intValue() != act) {
327                 return false;
328             }
329         }
330 
331         if (ai != size) {
332             return false;
333         }
334 
335         return true;
336     }
337     
338     protected boolean checkReceiverResultsAnyOrder(
339         List<WSSecurityEngineResult> wsResult, List<Integer> actions
340     ) {
341         List<Integer> recordedActions = new ArrayList<Integer>(actions.size());
342         for (Integer action : actions) {
343             recordedActions.add(action);
344         }
345         
346         for (WSSecurityEngineResult result : wsResult) {
347             final Integer actInt = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
348             int act = actInt.intValue();
349             if (act == WSConstants.SC || act == WSConstants.BST) {
350                 continue;
351             }
352             
353             if (!recordedActions.remove(actInt)) {
354                 return false;
355             }
356         }
357 
358         if (!recordedActions.isEmpty()) {
359             return false;
360         }
361 
362         return true;
363     }
364 
365     @SuppressWarnings("unchecked")
366     protected void checkSignatureConfirmation(
367         RequestData reqData,
368         List<WSSecurityEngineResult> resultList
369     ) throws WSSecurityException{
370         if (doDebug) {
371             log.debug("Check Signature confirmation");
372         }
373         //
374         // First get all Signature values stored during sending the request
375         //
376         List<byte[]> savedSignatures = 
377             (List<byte[]>) getProperty(reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV);
378         //
379         // Now get all results that hold a SignatureConfirmation element from
380         // the current run of receiver (we can have more than one run: if we
381         // have several security header blocks with different actors/roles)
382         //
383         List<WSSecurityEngineResult> sigConf = new ArrayList<WSSecurityEngineResult>();
384         WSSecurityUtil.fetchAllActionResults(resultList, WSConstants.SC, sigConf);
385         //
386         // now loop over all SignatureConfirmation results and check:
387         // - if there is a signature value and no Signature value generated in request: error
388         // - if there is a signature value and no matching Signature value found: error
389         // 
390         //  If a matching value found: remove from vector of stored signature values
391         //
392         for (WSSecurityEngineResult result : sigConf) {
393             SignatureConfirmation sc = 
394                 (SignatureConfirmation)result.get(
395                     WSSecurityEngineResult.TAG_SIGNATURE_CONFIRMATION
396                 );
397 
398             byte[] sigVal = sc.getSignatureValue();
399             if (sigVal != null) {
400                 if (savedSignatures == null || savedSignatures.size() == 0) {
401                     //
402                     // If there are no stored signature values, and we've received a 
403                     // SignatureConfirmation element then throw an Exception
404                     //
405                     if (sigVal.length != 0) {
406                         throw new WSSecurityException(
407                             "Received a SignatureConfirmation element, but there are no stored"
408                              + " signature values"
409                         );
410                     }
411                 } else {
412                     boolean found = false;
413                     for (int j = 0; j < savedSignatures.size(); j++) {
414                         byte[] storedValue = (byte[]) savedSignatures.get(j);
415                         if (Arrays.equals(sigVal, storedValue)) {
416                             found = true;
417                             savedSignatures.remove(j);
418                             break;
419                         }
420                     }
421                     if (!found) {
422                         throw new WSSecurityException(
423                             "Received a SignatureConfirmation element, but there are no matching"
424                             + " stored signature values"
425                         );
426                     } 
427                 }
428             }
429         }
430 
431         //
432         // This indicates this is the last handler: the list holding the
433         // stored Signature values must be empty, otherwise we have an error
434         //
435         if (!reqData.isNoSerialization()) {
436             if (doDebug) {
437                 log.debug("Check Signature confirmation - last handler");
438             }
439             if (savedSignatures != null && !savedSignatures.isEmpty()) {
440                 throw new WSSecurityException(
441                     "Check Signature confirmation: the stored signature values list is not empty"
442                 );
443             }
444         }
445     }
446     
447     protected void decodeUTParameter(RequestData reqData) 
448         throws WSSecurityException {
449         Object mc = reqData.getMsgContext();
450         
451         String type = getString(WSHandlerConstants.PASSWORD_TYPE, mc);
452         if (type != null) {
453             if (WSConstants.PW_TEXT.equals(type)) {
454                 reqData.setPwType(WSConstants.PASSWORD_TEXT);
455             } else if (WSConstants.PW_DIGEST.equals(type)) {
456                 reqData.setPwType(WSConstants.PASSWORD_DIGEST);
457             } else if (WSConstants.PW_NONE.equals(type)) {
458                 reqData.setPwType(null);
459             } else {
460                 throw new WSSecurityException("Unknown password type encoding: " + type);
461             }
462         }
463         
464         String add = getString(WSHandlerConstants.ADD_UT_ELEMENTS, mc);
465         if (add != null) {
466             reqData.setUtElements(StringUtil.split(add, ' '));
467         }
468         
469         String derived = getString(WSHandlerConstants.USE_DERIVED_KEY, mc);
470         if (derived != null) {
471             boolean useDerivedKey = Boolean.parseBoolean(derived);
472             reqData.setUseDerivedKey(useDerivedKey);
473         }
474         
475         String derivedMAC = getString(WSHandlerConstants.USE_DERIVED_KEY_FOR_MAC, mc);
476         boolean useDerivedKeyForMAC = Boolean.parseBoolean(derivedMAC);
477         if (useDerivedKeyForMAC) {
478             reqData.setUseDerivedKeyForMAC(useDerivedKeyForMAC);
479         }
480         
481         String iterations = getString(WSHandlerConstants.DERIVED_KEY_ITERATIONS, mc);
482         if (iterations != null) {
483             int iIterations = Integer.parseInt(iterations);
484             reqData.setDerivedKeyIterations(iIterations);
485         }
486     }
487 
488     protected void decodeSignatureParameter(RequestData reqData) 
489         throws WSSecurityException {
490         Object mc = reqData.getMsgContext();
491         String signatureUser = getString(WSHandlerConstants.SIGNATURE_USER, mc);
492 
493         if (signatureUser != null) {
494             reqData.setSignatureUser(signatureUser);
495         } else {
496             reqData.setSignatureUser(reqData.getUsername());
497         }
498         
499         String keyId = getString(WSHandlerConstants.SIG_KEY_ID, mc);
500         if (keyId != null) {
501             Integer id = (Integer) WSHandlerConstants.getKeyIdentifier(keyId);
502             if (id == null) {
503                 throw new WSSecurityException(
504                     "WSHandler: Signature: unknown key identification"
505                 );
506             }
507             int tmp = id.intValue();
508             if (!(tmp == WSConstants.ISSUER_SERIAL
509                     || tmp == WSConstants.BST_DIRECT_REFERENCE
510                     || tmp == WSConstants.X509_KEY_IDENTIFIER
511                     || tmp == WSConstants.SKI_KEY_IDENTIFIER
512                     || tmp == WSConstants.THUMBPRINT_IDENTIFIER
513                     || tmp == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER)) {
514                 throw new WSSecurityException(
515                     "WSHandler: Signature: illegal key identification"
516                 );
517             }
518             reqData.setSigKeyId(tmp);
519         }
520         String algo = getString(WSHandlerConstants.SIG_ALGO, mc);
521         reqData.setSigAlgorithm(algo);
522         
523         String digestAlgo = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc);
524         reqData.setSigDigestAlgorithm(digestAlgo);
525 
526         String parts = getString(WSHandlerConstants.SIGNATURE_PARTS, mc);
527         if (parts != null) {
528             splitEncParts(parts, reqData.getSignatureParts(), reqData);
529         }
530         
531         String secretKeyLength = getString(WSHandlerConstants.WSE_SECRET_KEY_LENGTH, mc);
532         if (secretKeyLength != null) {
533             int iSecretKeyLength = Integer.parseInt(secretKeyLength);
534             reqData.setSecretKeyLength(iSecretKeyLength);
535         }
536         
537         boolean useSingleCert = decodeUseSingleCertificate(reqData);
538         reqData.setUseSingleCert(useSingleCert);
539     }
540 
541     protected void decodeAlgorithmSuite(RequestData reqData) throws WSSecurityException {
542         Object mc = reqData.getMsgContext();
543         if (mc == null || reqData.getAlgorithmSuite() != null) {
544             return;
545         }
546         
547         AlgorithmSuite algorithmSuite = new AlgorithmSuite();
548         
549         String signatureAlgorithm = getString(WSHandlerConstants.SIG_ALGO, mc);
550         if (signatureAlgorithm != null && !"".equals(signatureAlgorithm)) {
551             algorithmSuite.addSignatureMethod(signatureAlgorithm);
552         }
553         String signatureDigestAlgorithm = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc);
554         if (signatureDigestAlgorithm != null && !"".equals(signatureDigestAlgorithm)) {
555             algorithmSuite.addDigestAlgorithm(signatureDigestAlgorithm);
556         }
557         
558         String encrAlgorithm = getString(WSHandlerConstants.ENC_SYM_ALGO, mc);
559         if (encrAlgorithm != null && !"".equals(encrAlgorithm)) {
560             algorithmSuite.addEncryptionMethod(encrAlgorithm);
561         }
562         String transportAlgorithm = getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc);
563         if (transportAlgorithm != null && !"".equals(transportAlgorithm)) {
564             algorithmSuite.addKeyWrapAlgorithm(transportAlgorithm);
565         }
566         
567         reqData.setAlgorithmSuite(algorithmSuite);
568     }
569     
570     protected void decodeEncryptionParameter(RequestData reqData) 
571         throws WSSecurityException {
572         Object mc = reqData.getMsgContext();
573         String encUser = getString(WSHandlerConstants.ENCRYPTION_USER, mc);
574 
575         if (encUser != null) {
576             reqData.setEncUser(encUser);
577         } else {
578             reqData.setEncUser(reqData.getUsername());
579         }
580         if (reqData.getEncUser() == null) {
581             throw new WSSecurityException("WSHandler: Encryption: no username");
582         }
583         /*
584          * String msgType = msgContext.getCurrentMessage().getMessageType(); if
585          * (msgType != null && msgType.equals(Message.RESPONSE)) {
586          * handleSpecialUser(encUser); }
587          */
588         handleSpecialUser(reqData);
589 
590         /*
591          * If the following parameters are no used (they return null) then the
592          * default values of WSS4J are used.
593          */
594         String encKeyId = getString(WSHandlerConstants.ENC_KEY_ID, mc);
595         if (encKeyId != null) {
596             Integer id = (Integer) WSHandlerConstants.getKeyIdentifier(encKeyId);
597             if (id == null) {
598                 throw new WSSecurityException(
599                     "WSHandler: Encryption: unknown key identification"
600                 );
601             }
602             int tmp = id.intValue();
603             reqData.setEncKeyId(tmp);
604             if (!(tmp == WSConstants.ISSUER_SERIAL
605                     || tmp == WSConstants.X509_KEY_IDENTIFIER
606                     || tmp == WSConstants.SKI_KEY_IDENTIFIER
607                     || tmp == WSConstants.BST_DIRECT_REFERENCE
608                     || tmp == WSConstants.EMBEDDED_KEYNAME
609                     || tmp == WSConstants.THUMBPRINT_IDENTIFIER
610                     || tmp == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER)) {
611                 throw new WSSecurityException(
612                     "WSHandler: Encryption: illegal key identification"
613                 );
614             }
615         }
616         String encSymAlgo = getString(WSHandlerConstants.ENC_SYM_ALGO, mc);
617         reqData.setEncSymmAlgo(encSymAlgo);
618 
619         String encKeyTransport = 
620             getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc);
621         reqData.setEncKeyTransport(encKeyTransport);
622         
623         String digestAlgo = getString(WSHandlerConstants.ENC_DIGEST_ALGO, mc);
624         reqData.setEncDigestAlgorithm(digestAlgo);
625         
626         String encSymEncKey = getString(WSHandlerConstants.ENC_SYM_ENC_KEY, mc);
627         if (encSymEncKey != null) {
628             boolean encSymEndKeyBoolean = Boolean.parseBoolean(encSymEncKey);
629             reqData.setEncryptSymmetricEncryptionKey(encSymEndKeyBoolean);
630         }
631 
632         String encParts = getString(WSHandlerConstants.ENCRYPTION_PARTS, mc);
633         if (encParts != null) {
634             splitEncParts(encParts, reqData.getEncryptParts(), reqData);
635         }
636     }
637 
638     public int decodeTimeToLive(RequestData reqData) {
639         String ttl = 
640             getString(WSHandlerConstants.TTL_TIMESTAMP, reqData.getMsgContext());
641         int ttlI = 0;
642         if (ttl != null) {
643             try {
644                 ttlI = Integer.parseInt(ttl);
645             } catch (NumberFormatException e) {
646                 ttlI = reqData.getTimeToLive();
647             }
648         }
649         if (ttlI <= 0) {
650             ttlI = reqData.getTimeToLive();
651         }
652         return ttlI;
653     }
654     
655     protected int decodeFutureTimeToLive(RequestData reqData) {
656         String ttl = 
657             getString(WSHandlerConstants.TTL_FUTURE_TIMESTAMP, reqData.getMsgContext());
658         int defaultFutureTimeToLive = 60;
659         if (ttl != null) {
660             try {
661                 int ttlI = Integer.parseInt(ttl);
662                 if (ttlI < 0) {
663                     return defaultFutureTimeToLive;
664                 }
665                 return ttlI;
666             } catch (NumberFormatException e) {
667                 return defaultFutureTimeToLive;
668             }
669         }
670         return defaultFutureTimeToLive;
671     }
672     
673     protected boolean decodeBSPCompliance(RequestData reqData)
674         throws WSSecurityException {
675         return decodeBooleanConfigValue(
676             reqData, WSHandlerConstants.IS_BSP_COMPLIANT, true
677         );
678     }
679     
680     protected String decodePasswordType(RequestData reqData) throws WSSecurityException {
681         String type = getString(WSHandlerConstants.PASSWORD_TYPE, reqData.getMsgContext());
682         if (type != null) {
683             if (WSConstants.PW_TEXT.equals(type)) {
684                 return WSConstants.PASSWORD_TEXT;
685             } else if (WSConstants.PW_DIGEST.equals(type)) {
686                 return WSConstants.PASSWORD_DIGEST;
687             }
688         }
689         return null;
690     }
691     
692     protected boolean decodeMustUnderstand(RequestData reqData) 
693         throws WSSecurityException {
694         return decodeBooleanConfigValue(
695             reqData, WSHandlerConstants.MUST_UNDERSTAND, true
696         );
697     }
698 
699     protected boolean decodeEnableSignatureConfirmation(
700         RequestData reqData
701     ) throws WSSecurityException {
702         return decodeBooleanConfigValue(
703             reqData, WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, false
704         );
705     }
706     
707     protected boolean decodeTimestampPrecision(
708         RequestData reqData
709     ) throws WSSecurityException {
710         return decodeBooleanConfigValue(
711             reqData, WSHandlerConstants.TIMESTAMP_PRECISION, true
712         );
713     }
714     
715     protected boolean decodeCustomPasswordTypes(
716         RequestData reqData
717     ) throws WSSecurityException {
718         return decodeBooleanConfigValue(
719             reqData, WSHandlerConstants.HANDLE_CUSTOM_PASSWORD_TYPES, false
720         );
721     }
722     
723     protected boolean decodeUseEncodedPasswords(RequestData reqData) 
724         throws WSSecurityException {
725         return decodeBooleanConfigValue(
726             reqData, WSHandlerConstants.USE_ENCODED_PASSWORDS, false
727         );
728     }
729     
730     protected boolean decodeNamespaceQualifiedPasswordTypes(RequestData reqData) 
731         throws WSSecurityException {
732         return decodeBooleanConfigValue(
733             reqData, WSHandlerConstants.ALLOW_NAMESPACE_QUALIFIED_PASSWORD_TYPES, false
734         );
735     }
736 
737     protected boolean decodeTimestampStrict(RequestData reqData) 
738         throws WSSecurityException {
739         return decodeBooleanConfigValue(
740             reqData, WSHandlerConstants.TIMESTAMP_STRICT, true
741         );
742     }
743     
744     protected boolean decodePasswordTypeStrict(RequestData reqData) 
745         throws WSSecurityException {
746         return decodeBooleanConfigValue(
747             reqData, WSHandlerConstants.PASSWORD_TYPE_STRICT, false
748         );
749     }
750     
751     protected boolean decodeUseSingleCertificate(RequestData reqData) 
752         throws WSSecurityException {
753         return decodeBooleanConfigValue(
754             reqData, WSHandlerConstants.USE_SINGLE_CERTIFICATE, true
755         );
756     }
757     
758     protected void decodeRequireSignedEncryptedDataElements(RequestData reqData) 
759         throws WSSecurityException {
760         reqData.setRequireSignedEncryptedDataElements(decodeBooleanConfigValue(
761             reqData, WSHandlerConstants.REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS, false
762         ));
763     }
764 
765     protected boolean decodeBooleanConfigValue(
766         RequestData reqData, String configTag, boolean defaultToTrue
767     ) throws WSSecurityException {
768 
769         String value = getString(configTag, reqData.getMsgContext());
770 
771         if (value == null) {
772             return defaultToTrue;
773         }
774         if ("0".equals(value) || "false".equals(value)) {
775             return false;
776         } 
777         if ("1".equals(value) || "true".equals(value)) {
778             return true;
779         }
780 
781         throw new WSSecurityException(
782             "WSHandler: illegal " + configTag + " parameter"
783         );
784     }
785     
786     /**
787      * Hook to allow subclasses to load their Signature Crypto however they see
788      * fit. 
789      * 
790      * @param requestData the RequestData object
791      * @return a Crypto instance to use for Signature creation/verification
792      */
793     public Crypto loadSignatureCrypto(RequestData requestData) throws WSSecurityException {
794         return 
795             loadCrypto(
796                 WSHandlerConstants.SIG_PROP_FILE,
797                 WSHandlerConstants.SIG_PROP_REF_ID,
798                 requestData
799             );
800     }
801     
802     /**
803      * Hook to allow subclasses to load their Decryption Crypto however they see
804      * fit. 
805      * 
806      * @param requestData the RequestData object
807      * @return a Crypto instance to use for Decryption creation/verification
808      */
809     protected Crypto loadDecryptionCrypto(RequestData requestData) throws WSSecurityException {
810         return 
811             loadCrypto(
812                 WSHandlerConstants.DEC_PROP_FILE,
813                 WSHandlerConstants.DEC_PROP_REF_ID,
814                 requestData
815             );
816     }
817     
818     /**
819      * Hook to allow subclasses to load their Encryption Crypto however they see
820      * fit. 
821      * 
822      * @param requestData the RequestData object
823      * @return a Crypto instance to use for Encryption creation/verification
824      */
825     protected Crypto loadEncryptionCrypto(RequestData requestData) throws WSSecurityException {
826         return 
827             loadCrypto(
828                 WSHandlerConstants.ENC_PROP_FILE,
829                 WSHandlerConstants.ENC_PROP_REF_ID,
830                 requestData
831             );
832     }
833     
834     /**
835      * Load a Crypto instance. Firstly, it tries to use the cryptoPropertyRefId tag to retrieve
836      * a Crypto object via a custom reference Id. Failing this, it tries to load the crypto 
837      * instance via the cryptoPropertyFile tag.
838      * 
839      * @param requestData the RequestData object
840      * @return a Crypto instance to use for Encryption creation/verification
841      */
842     protected Crypto loadCrypto(
843         String cryptoPropertyFile,
844         String cryptoPropertyRefId,
845         RequestData requestData
846     ) throws WSSecurityException {
847         Object mc = requestData.getMsgContext();
848         Crypto crypto = null;
849         
850         //
851         // Try the Property Ref Id first
852         //
853         String refId = getString(cryptoPropertyRefId, mc);
854         if (refId != null) {
855             crypto = cryptos.get(refId);
856             if (crypto == null) {
857                 Object obj = getProperty(mc, refId);
858                 if (obj instanceof Properties) {
859                     crypto = CryptoFactory.getInstance((Properties)obj);
860                     cryptos.put(refId, crypto);
861                 } else if (obj instanceof Crypto) {
862                     crypto = (Crypto)obj;
863                     cryptos.put(refId, crypto);
864                 }
865             }
866         }
867         
868         //
869         // Now try loading the properties file
870         //
871         if (crypto == null) {
872             String propFile = getString(cryptoPropertyFile, mc);
873             if (propFile != null) {
874                 crypto = cryptos.get(propFile);
875                 if (crypto == null) {
876                     crypto = loadCryptoFromPropertiesFile(propFile, requestData);
877                     cryptos.put(propFile, crypto);
878                 }
879             } 
880         }
881         return crypto;
882     }
883 
884     /**
885      * A hook to allow subclass to load Crypto instances from property files in a different
886      * way.
887      * @param propFilename The property file name
888      * @param reqData The RequestData object
889      * @return A Crypto instance that has been loaded
890      */
891     protected Crypto loadCryptoFromPropertiesFile(
892         String propFilename, 
893         RequestData reqData
894     ) throws WSSecurityException {
895         return 
896             CryptoFactory.getInstance(
897                 propFilename, this.getClassLoader(reqData.getMsgContext())
898             );
899     }
900 
901     /**
902      * Get a CallbackHandler instance. First try to get an instance via the 
903      * callbackHandlerRef on the message context. Failing that, try to load a new 
904      * instance of the CallbackHandler via the callbackHandlerClass argument.
905      * 
906      * @param callbackHandlerClass The class name of the CallbackHandler instance
907      * @param callbackHandlerRef The reference name of the CallbackHandler instance
908      * @param requestData The RequestData which supplies the message context
909      * @return a CallbackHandler instance
910      * @throws WSSecurityException
911      */
912     public CallbackHandler getCallbackHandler(
913         String callbackHandlerClass,
914         String callbackHandlerRef,
915         RequestData requestData
916     ) throws WSSecurityException {
917         Object mc = requestData.getMsgContext();
918         CallbackHandler cbHandler = (CallbackHandler) getOption(callbackHandlerRef);
919         if (cbHandler == null) {
920             cbHandler = (CallbackHandler) getProperty(mc, callbackHandlerRef);
921         }
922         if (cbHandler == null) {
923             String callback = getString(callbackHandlerClass, mc);
924             if (callback != null) {
925                 cbHandler = loadCallbackHandler(callback, requestData);
926             }
927         }
928         return cbHandler;
929     }
930     
931     /**
932      * Get a CallbackHandler instance to obtain passwords.
933      * @param reqData The RequestData which supplies the message context
934      * @return the CallbackHandler instance to obtain passwords.
935      * @throws WSSecurityException
936      */
937     public CallbackHandler getPasswordCallbackHandler(RequestData reqData) 
938         throws WSSecurityException {
939         return 
940             getCallbackHandler(
941                 WSHandlerConstants.PW_CALLBACK_CLASS,
942                 WSHandlerConstants.PW_CALLBACK_REF,
943                 reqData
944             );
945     }
946     
947     /**
948      * Load a CallbackHandler instance.
949      * @param callbackHandlerClass The class name of the CallbackHandler instance
950      * @param requestData The RequestData which supplies the message context
951      * @return a CallbackHandler instance
952      * @throws WSSecurityException
953      */
954     private CallbackHandler loadCallbackHandler(
955         String callbackHandlerClass,
956         RequestData requestData
957     ) throws WSSecurityException {
958 
959         Class<? extends CallbackHandler> cbClass = null;
960         CallbackHandler cbHandler = null;
961         try {
962             cbClass = 
963                 Loader.loadClass(getClassLoader(requestData.getMsgContext()), 
964                                  callbackHandlerClass,
965                                  CallbackHandler.class);
966         } catch (ClassNotFoundException e) {
967             throw new WSSecurityException(
968                 "WSHandler: cannot load callback handler class: " + callbackHandlerClass, e
969             );
970         }
971         try {
972             cbHandler = (CallbackHandler) cbClass.newInstance();
973         } catch (Exception e) {
974             throw new WSSecurityException(
975                 "WSHandler: cannot create instance of callback handler: " + callbackHandlerClass, e
976             );
977         }
978         return cbHandler;
979     }
980     
981     /**
982      * Get a password callback (WSPasswordCallback object) from a CallbackHandler instance
983      * @param username The username to supply to the CallbackHandler
984      * @param doAction The action to perform
985      * @param callbackHandler The CallbackHandler instance
986      * @param requestData The RequestData which supplies the message context
987      * @return the WSPasswordCallback object containing the password
988      * @throws WSSecurityException
989      */
990     public WSPasswordCallback getPasswordCB(
991          String username,
992          int doAction,
993          CallbackHandler callbackHandler,
994          RequestData requestData
995     ) throws WSSecurityException {
996         
997         if (callbackHandler != null) { 
998             return performPasswordCallback(callbackHandler, username, doAction);
999         } else {
1000             //
1001             // If a callback isn't configured then try to get the password
1002             // from the message context
1003             //
1004             String password = getPassword(requestData.getMsgContext());
1005             if (password == null) {
1006                 String err = "provided null or empty password";
1007                 throw new WSSecurityException("WSHandler: application " + err);
1008             }
1009             WSPasswordCallback pwCb = constructPasswordCallback(username, doAction);
1010             pwCb.setPassword(password);
1011             return pwCb;
1012         }
1013     }
1014 
1015     /**
1016      * Perform a callback on a CallbackHandler instance
1017      * @param cbHandler the CallbackHandler instance
1018      * @param username The username to supply to the CallbackHandler
1019      * @param doAction The action to perform
1020      * @return a WSPasswordCallback instance
1021      * @throws WSSecurityException
1022      */
1023     private WSPasswordCallback performPasswordCallback(
1024         CallbackHandler cbHandler,
1025         String username,
1026         int doAction
1027     ) throws WSSecurityException {
1028 
1029         WSPasswordCallback pwCb = constructPasswordCallback(username, doAction);
1030         Callback[] callbacks = new Callback[1];
1031         callbacks[0] = pwCb;
1032         //
1033         // Call back the application to get the password
1034         //
1035         try {
1036             cbHandler.handle(callbacks);
1037         } catch (Exception e) {
1038             throw new WSSecurityException("WSHandler: password callback failed", e);
1039         }
1040         return pwCb;
1041     }
1042 
1043     /**
1044      * Construct a WSPasswordCallback instance
1045      * @param username The username
1046      * @param doAction The action to perform
1047      * @return a WSPasswordCallback instance
1048      * @throws WSSecurityException
1049      */
1050     private WSPasswordCallback constructPasswordCallback(
1051         String username,
1052         int doAction
1053     ) throws WSSecurityException {
1054 
1055         int reason = WSPasswordCallback.UNKNOWN;
1056 
1057         switch (doAction) {
1058         case WSConstants.UT:
1059         case WSConstants.UT_SIGN:
1060             reason = WSPasswordCallback.USERNAME_TOKEN;
1061             break;
1062         case WSConstants.SIGN:
1063             reason = WSPasswordCallback.SIGNATURE;
1064             break;
1065         case WSConstants.ENCR:
1066             reason = WSPasswordCallback.SECRET_KEY;
1067             break;
1068         }
1069         return new WSPasswordCallback(username, reason);
1070     }
1071 
1072     private void splitEncParts(String tmpS, List<WSEncryptionPart> parts, RequestData reqData)
1073         throws WSSecurityException {
1074         WSEncryptionPart encPart = null;
1075         String[] rawParts = StringUtil.split(tmpS, ';');
1076 
1077         for (int i = 0; i < rawParts.length; i++) {
1078             String[] partDef = StringUtil.split(rawParts[i], '}');
1079 
1080             if (partDef.length == 1) {
1081                 if (doDebug) {
1082                     log.debug("single partDef: '" + partDef[0] + "'");
1083                 }
1084                 encPart =
1085                     new WSEncryptionPart(partDef[0].trim(),
1086                             reqData.getSoapConstants().getEnvelopeURI(),
1087                             "Content");
1088             } else if (partDef.length == 3) {
1089                 String mode = partDef[0].trim();
1090                 if (mode.length() <= 1) {
1091                     mode = "Content";
1092                 } else {
1093                     mode = mode.substring(1);
1094                 }
1095                 String nmSpace = partDef[1].trim();
1096                 if (nmSpace.length() <= 1) {
1097                     nmSpace = reqData.getSoapConstants().getEnvelopeURI();
1098                 } else {
1099                     nmSpace = nmSpace.substring(1);
1100                     if (nmSpace.equals(WSConstants.NULL_NS)) {
1101                         nmSpace = null;
1102                     }
1103                 }
1104                 String element = partDef[2].trim();
1105                 if (doDebug) {
1106                     log.debug(
1107                         "partDefs: '" + mode + "' ,'" + nmSpace + "' ,'" + element + "'"
1108                     );
1109                 }
1110                 encPart = new WSEncryptionPart(element, nmSpace, mode);
1111             } else {
1112                 throw new WSSecurityException("WSHandler: wrong part definition: " + tmpS);
1113             }
1114             parts.add(encPart);
1115         }
1116     }
1117 
1118     @SuppressWarnings("unchecked")
1119     private void handleSpecialUser(RequestData reqData) {
1120         if (!WSHandlerConstants.USE_REQ_SIG_CERT.equals(reqData.getEncUser())) {
1121             return;
1122         }
1123         List<WSHandlerResult> results = 
1124             (List<WSHandlerResult>) getProperty(
1125                 reqData.getMsgContext(), WSHandlerConstants.RECV_RESULTS
1126             );
1127         if (results == null) {
1128             return;
1129         }
1130         /*
1131          * Scan the results for a matching actor. Use results only if the
1132          * receiving Actor and the sending Actor match.
1133          */
1134         for (WSHandlerResult rResult : results) {
1135             String hActor = rResult.getActor();
1136             if (!WSSecurityUtil.isActorEqual(reqData.getActor(), hActor)) {
1137                 continue;
1138             }
1139             List<WSSecurityEngineResult> wsSecEngineResults = rResult.getResults();
1140             /*
1141              * Scan the results for the first Signature action. Use the
1142              * certificate of this Signature to set the certificate for the
1143              * encryption action :-).
1144              */
1145             for (WSSecurityEngineResult wser : wsSecEngineResults) {
1146                 int wserAction = 
1147                     ((java.lang.Integer)wser.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
1148                 if (wserAction == WSConstants.SIGN) {
1149                     X509Certificate cert = 
1150                         (X509Certificate)wser.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
1151                     reqData.setEncCert(cert);
1152                     return;
1153                 }
1154             }
1155         }
1156     }
1157 
1158     protected void decodeSignatureParameter2(RequestData reqData) 
1159         throws WSSecurityException {
1160         if (reqData.getSigCrypto() == null) {
1161             reqData.setSigCrypto(loadSignatureCrypto(reqData));
1162         }
1163         boolean enableRevocation = 
1164             decodeBooleanConfigValue(
1165                 reqData, WSHandlerConstants.ENABLE_REVOCATION, false
1166             );
1167         reqData.setEnableRevocation(enableRevocation);
1168         
1169         String certConstraints = 
1170             getString(WSHandlerConstants.SIG_SUBJECT_CERT_CONSTRAINTS, reqData.getMsgContext());
1171         if (certConstraints != null) {
1172             String[] certConstraintsList = certConstraints.split(",");
1173             if (certConstraintsList != null) {
1174                 Collection<Pattern> subjectCertConstraints = 
1175                     new ArrayList<Pattern>(certConstraintsList.length);
1176                 for (String certConstraint : certConstraintsList) {
1177                     try {
1178                         subjectCertConstraints.add(Pattern.compile(certConstraint.trim()));
1179                     } catch (PatternSyntaxException ex) {
1180                         log.debug(ex.getMessage(), ex);
1181                         throw new WSSecurityException(ex.getMessage(), ex);
1182                     }
1183                 }
1184                 reqData.setSubjectCertConstraints(subjectCertConstraints);
1185             }
1186         }
1187     }
1188 
1189     /*
1190      * Set and check the decryption specific parameters, if necessary
1191      * take over signature crypto instance.
1192      */
1193     protected void decodeDecryptionParameter(RequestData reqData) 
1194         throws WSSecurityException {
1195         if (reqData.getDecCrypto() == null) {
1196             reqData.setDecCrypto(loadDecryptionCrypto(reqData));
1197         }
1198     }
1199 
1200     /**
1201      * Looks up key first via {@link #getOption(String)} and if not found
1202      * there, via {@link #getProperty(Object, String)}
1203      *
1204      * @param key the key to search for. May not be null.
1205      * @param mc the message context to search. 
1206      * @return the value found.
1207      * @throws IllegalArgumentException if <code>key</code> is null.
1208      */
1209     public String getString(String key, Object mc) { 
1210         if (key == null) {
1211             throw new IllegalArgumentException("Key cannot be null");
1212         }
1213         String s = getStringOption(key);
1214         if (s != null) {
1215             return s;
1216         }
1217         if (mc == null) {
1218             throw new IllegalArgumentException("Message context cannot be null");
1219         }
1220         return (String) getProperty(mc, key);
1221     }
1222 
1223 
1224     /**
1225      * Returns the option on <code>name</code>.
1226      *
1227      * @param key the non-null key of the option.
1228      * @return the option on <code>key</code> if <code>key</code>
1229      *  exists and is of type java.lang.String; otherwise null.
1230      */
1231     public String getStringOption(String key) {
1232         Object o = getOption(key);
1233         if (o instanceof String){
1234             return (String) o;
1235         } else {
1236             return null;
1237         }
1238     }
1239 
1240     /**
1241      * Returns the classloader to be used for loading the callback class
1242      * @param msgCtx The MessageContext 
1243      * @return class loader
1244      */
1245     public ClassLoader getClassLoader(Object msgCtx) {
1246         try {
1247             return Loader.getTCL();
1248         } catch (Exception ex) {
1249             return null;
1250         }
1251     }
1252 
1253     public abstract Object getOption(String key);
1254     public abstract Object getProperty(Object msgContext, String key);
1255 
1256     public abstract void setProperty(Object msgContext, String key, 
1257             Object value);
1258 
1259 
1260     public abstract String getPassword(Object msgContext);
1261 
1262     public abstract void setPassword(Object msgContext, String password);
1263 }