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.wss4j.dom.handler;
21  
22  import java.security.cert.X509Certificate;
23  import java.util.*;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.regex.Pattern;
26  import java.util.regex.PatternSyntaxException;
27  
28  import javax.security.auth.callback.Callback;
29  import javax.security.auth.callback.CallbackHandler;
30  
31  import org.apache.wss4j.dom.WSConstants;
32  import org.apache.wss4j.dom.WSDocInfo;
33  import org.apache.wss4j.dom.engine.WSSConfig;
34  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
35  import org.apache.wss4j.common.EncryptionActionToken;
36  import org.apache.wss4j.common.SignatureActionToken;
37  import org.apache.wss4j.common.SignatureEncryptionActionToken;
38  import org.apache.wss4j.common.WSEncryptionPart;
39  import org.apache.wss4j.common.crypto.AlgorithmSuite;
40  import org.apache.wss4j.common.crypto.Crypto;
41  import org.apache.wss4j.common.crypto.CryptoFactory;
42  import org.apache.wss4j.common.crypto.JasyptPasswordEncryptor;
43  import org.apache.wss4j.common.crypto.PasswordEncryptor;
44  import org.apache.wss4j.common.ext.WSPasswordCallback;
45  import org.apache.wss4j.common.ext.WSSecurityException;
46  import org.apache.wss4j.common.util.Loader;
47  import org.apache.wss4j.dom.message.WSSecHeader;
48  import org.apache.wss4j.dom.message.token.SignatureConfirmation;
49  import org.apache.wss4j.dom.util.WSSecurityUtil;
50  import org.apache.xml.security.encryption.params.KeyDerivationParameters;
51  import org.w3c.dom.Document;
52  
53  /**
54   * Extracted from WSDoAllReceiver and WSDoAllSender
55   * Extended to all passwordless UsernameTokens and configurable identities.
56   */
57  public abstract class WSHandler {
58      private static final org.slf4j.Logger LOG =
59          org.slf4j.LoggerFactory.getLogger(WSHandler.class);
60      protected Map<String, Crypto> cryptos = new ConcurrentHashMap<>();
61  
62      /**
63       * Performs all defined security actions to set-up the SOAP request.
64       *
65       * @param doc   the request as DOM document
66       * @param reqData a data storage to pass values around between methods
67       * @param actions a list holding the actions to do in the order defined
68       *                in the deployment file or property, plus an optional
69       *                associated SecurityActionToken object for that Action
70       * @throws WSSecurityException
71       */
72      protected void doSenderAction(
73              Document doc,
74              RequestData reqData,
75              List<HandlerAction> actions,
76              boolean isRequest
77      ) throws WSSecurityException {
78  
79          WSSConfig wssConfig = reqData.getWssConfig();
80          if (wssConfig == null) {
81              wssConfig = WSSConfig.getNewInstance();
82              reqData.setWssConfig(wssConfig);
83          }
84  
85          if (reqData.getWsDocInfo() == null) {
86              WSDocInfo wsDocInfo = new WSDocInfo(doc);
87              reqData.setWsDocInfo(wsDocInfo);
88          }
89  
90          Object mc = reqData.getMsgContext();
91          reqData.setEncodePasswords(
92              decodeBooleanConfigValue(mc, WSHandlerConstants.USE_ENCODED_PASSWORDS, false)
93          );
94          reqData.setPrecisionInMilliSeconds(
95              decodeBooleanConfigValue(mc, WSHandlerConstants.TIMESTAMP_PRECISION, true)
96          );
97          reqData.setAddInclusivePrefixes(
98              decodeBooleanConfigValue(mc, WSHandlerConstants.ADD_INCLUSIVE_PREFIXES, true)
99          );
100         reqData.setEnableSignatureConfirmation(
101             decodeBooleanConfigValue(mc, WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, false)
102         );
103         reqData.setTimeStampTTL(decodeTimeToLive(reqData, true));
104 
105         String actor = getString(WSHandlerConstants.ACTOR, mc);
106         reqData.setActor(actor);
107 
108         boolean mu = decodeBooleanConfigValue(mc, WSHandlerConstants.MUST_UNDERSTAND, true);
109         WSSecHeader secHeader = new WSSecHeader(actor, mu, doc);
110         secHeader.insertSecurityHeader();
111         reqData.setSecHeader(secHeader);
112         reqData.setSoapConstants(WSSecurityUtil.getSOAPConstants(doc.getDocumentElement()));
113 
114         // Load CallbackHandler
115         if (reqData.getCallbackHandler() == null) {
116             CallbackHandler passwordCallbackHandler = getPasswordCallbackHandler(reqData);
117             reqData.setCallbackHandler(passwordCallbackHandler);
118         }
119 
120         if (!reqData.isStoreBytesInAttachment()) {
121             boolean storeBytesInAttachment =
122                 decodeBooleanConfigValue(mc, WSHandlerConstants.STORE_BYTES_IN_ATTACHMENT, false);
123             reqData.setStoreBytesInAttachment(storeBytesInAttachment);
124         }
125 
126         // Perform configuration
127         boolean encryptionFound = false;
128         for (HandlerAction actionToDo : actions) {
129             if (actionToDo.getAction() == WSConstants.SC) {
130                 reqData.setEnableSignatureConfirmation(true);
131             } else if ((actionToDo.getAction() == WSConstants.UT
132                 || actionToDo.getAction() == WSConstants.UT_NOPASSWORD)
133                 && actionToDo.getActionToken() == null) {
134                 decodeUTParameter(reqData);
135                 if (actionToDo.getAction() == WSConstants.UT_NOPASSWORD) {
136                     reqData.setPwType(null);
137                 }
138             } else if (actionToDo.getAction() == WSConstants.UT_SIGN
139                 && actionToDo.getActionToken() == null) {
140                 decodeUTParameter(reqData);
141                 decodeSignatureParameter(reqData);
142             } else if ((actionToDo.getAction() == WSConstants.SIGN
143                 || actionToDo.getAction() == WSConstants.DKT_SIGN)
144                 && actionToDo.getActionToken() == null) {
145                 SignatureActionToken actionToken = reqData.getSignatureToken();
146                 if (actionToken == null) {
147                     actionToken = new SignatureActionToken();
148                     reqData.setSignatureToken(actionToken);
149                 }
150                 if (actionToken.getCrypto() == null) {
151                     actionToken.setCrypto(loadSignatureCrypto(reqData));
152                 }
153                 decodeSignatureParameter(reqData);
154                 if (encryptionFound && reqData.isStoreBytesInAttachment()) {
155                     LOG.warn("Turning off storeBytesInAttachment as we have encryption before signature."
156                              + " The danger here is that the actual encryption bytes will not be signed");
157                     reqData.setStoreBytesInAttachment(false);
158                 }
159             } else if (actionToDo.getAction() == WSConstants.ST_SIGNED
160                 && actionToDo.getActionToken() == null) {
161                 decodeSignatureParameter(reqData);
162             } else if ((actionToDo.getAction() == WSConstants.ENCR
163                 || actionToDo.getAction() == WSConstants.DKT_ENCR)
164                 && actionToDo.getActionToken() == null) {
165                 encryptionFound = true;
166                 EncryptionActionToken actionToken = reqData.getEncryptionToken();
167                 if (actionToken == null) {
168                     actionToken = new EncryptionActionToken();
169                     reqData.setEncryptionToken(actionToken);
170                 }
171                 if (actionToken.getCrypto() == null) {
172                     actionToken.setCrypto(loadEncryptionCrypto(reqData));
173                 }
174                 decodeEncryptionParameter(reqData);
175             }
176         }
177 
178         /*
179          * If after all the parsing no Signature parts defined, set here a
180          * default set. This is necessary because we add SignatureConfirmation
181          * and therefore the default (Body) must be set here. The default setting
182          * in WSSignEnvelope doesn't work because the vector is not empty anymore.
183          */
184         SignatureActionToken signatureToken = reqData.getSignatureToken();
185         if (signatureToken == null) {
186             signatureToken = new SignatureActionToken();
187             reqData.setSignatureToken(signatureToken);
188         }
189         if (signatureToken.getParts().isEmpty()) {
190             signatureToken.getParts().add(WSSecurityUtil.getDefaultEncryptionPart(doc));
191         }
192         /*
193          * If SignatureConfirmation is enabled and this is a response then
194          * insert SignatureConfirmation elements, note their wsu:id in the signature
195          * parts. They will be signed automatically during a (probably) defined
196          * SIGN action.
197          */
198         if (reqData.isEnableSignatureConfirmation() && !isRequest) {
199             String done =
200                 (String)getProperty(reqData.getMsgContext(), WSHandlerConstants.SIG_CONF_DONE);
201             if (done == null) {
202                 wssConfig.getAction(WSConstants.SC).execute(this, null, reqData);
203             }
204         }
205 
206         // See if the Signature and Timestamp actions (in that order) are defined, and if
207         // the Timestamp is to be signed. In this case we need to swap the actions, as the
208         // Timestamp must appear in the security header first for signature creation to work.
209         List<HandlerAction> actionsToPerform = actions;
210         HandlerAction signingAction = getSignatureActionThatSignsATimestamp(actions, reqData);
211 
212         if (signingAction != null) {
213             actionsToPerform = new ArrayList<>(actions);
214 
215             // Find TimestampAction
216             int timestampIndex = -1;
217             for (int i = 0; i < actionsToPerform.size(); i++) {
218                 if (actionsToPerform.get(i).getAction() == WSConstants.TS) {
219                     timestampIndex = i;
220                     break;
221                 }
222             }
223 
224             int signatureIndex = actionsToPerform.indexOf(signingAction);
225             if (timestampIndex >= 0) {
226                 actionsToPerform.set(signatureIndex, actionsToPerform.get(timestampIndex));
227                 actionsToPerform.set(timestampIndex, signingAction);
228             }
229             reqData.setAppendSignatureAfterTimestamp(true);
230             reqData.setOriginalSignatureActionPosition(signatureIndex);
231         }
232 
233         /*
234          * Here we have all necessary information to perform the requested
235          * action(s).
236          */
237         for (HandlerAction actionToDo : actionsToPerform) {
238             LOG.debug("Performing Action: {}", actionToDo.getAction());
239 
240             if (WSConstants.NO_SECURITY != actionToDo.getAction()) {
241                 wssConfig.getAction(actionToDo.getAction()).execute(
242                     this, actionToDo.getActionToken(), reqData);
243             }
244         }
245 
246         /*
247          * If this is a request then store all signature values. Add ours to
248          * already gathered values because of chained handlers, e.g. for
249          * other actors.
250          */
251         if (reqData.isEnableSignatureConfirmation()
252             && isRequest && !reqData.getSignatureValues().isEmpty()) {
253             @SuppressWarnings("unchecked")
254             Set<Integer> savedSignatures =
255                 (Set<Integer>)getProperty(reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV);
256             if (savedSignatures == null) {
257                 savedSignatures = new HashSet<>();
258                 setProperty(
259                     reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV, savedSignatures
260                 );
261             }
262             for (byte[] signatureValue : reqData.getSignatureValues()) {
263                 savedSignatures.add(Arrays.hashCode(signatureValue));
264             }
265         }
266     }
267 
268     private HandlerAction getSignatureActionThatSignsATimestamp(
269         List<HandlerAction> actions, RequestData reqData
270     ) {
271         for (HandlerAction action : actions) {
272             // Only applies if a Signature is before a Timestamp
273             if (action.getAction() == WSConstants.TS) {
274                 return null;
275             } else if (action.getAction() == WSConstants.SIGN) {
276                 if (action.getActionToken() != null
277                     && ((SignatureEncryptionActionToken)action.getActionToken()).getParts() != null) {
278                     for (WSEncryptionPart encP
279                         : ((SignatureEncryptionActionToken)action.getActionToken()).getParts()) {
280                         if (WSConstants.WSU_NS.equals(encP.getNamespace())
281                             && "Timestamp".equals(encP.getName())) {
282                             return action;
283                         }
284                     }
285                 } else {
286                     for (WSEncryptionPart encP : reqData.getSignatureToken().getParts()) {
287                         if (WSConstants.WSU_NS.equals(encP.getNamespace())
288                             && "Timestamp".equals(encP.getName())) {
289                             return action;
290                         }
291                     }
292                 }
293             }
294         }
295         return null;
296     }
297 
298     protected void doReceiverAction(List<Integer> actions, RequestData reqData)
299         throws WSSecurityException {
300 
301         WSSConfig wssConfig = reqData.getWssConfig();
302         if (wssConfig == null) {
303             wssConfig = WSSConfig.getNewInstance();
304             reqData.setWssConfig(wssConfig);
305         }
306 
307         Object mc = reqData.getMsgContext();
308         boolean enableSigConf =
309             decodeBooleanConfigValue(mc, WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, false);
310         reqData.setEnableSignatureConfirmation(
311             enableSigConf || actions.contains(WSConstants.SC)
312         );
313         reqData.setTimeStampStrict(
314             decodeBooleanConfigValue(mc, WSHandlerConstants.TIMESTAMP_STRICT, true)
315         );
316         reqData.setRequiredPasswordType(decodePasswordType(reqData));
317 
318         reqData.setTimeStampTTL(decodeTimeToLive(reqData, true));
319         reqData.setTimeStampFutureTTL(decodeFutureTimeToLive(reqData, true));
320         reqData.setUtTTL(decodeTimeToLive(reqData, false));
321         reqData.setUtFutureTTL(decodeFutureTimeToLive(reqData, false));
322 
323         reqData.setHandleCustomPasswordTypes(
324             decodeBooleanConfigValue(mc, WSHandlerConstants.HANDLE_CUSTOM_PASSWORD_TYPES, false)
325         );
326         reqData.setEncodePasswords(
327             decodeBooleanConfigValue(mc, WSHandlerConstants.USE_ENCODED_PASSWORDS, false)
328         );
329         reqData.setAllowNamespaceQualifiedPasswordTypes(
330             decodeBooleanConfigValue(mc, WSHandlerConstants.ALLOW_NAMESPACE_QUALIFIED_PASSWORD_TYPES, false)
331         );
332         reqData.setAllowUsernameTokenNoPassword(
333             decodeBooleanConfigValue(mc, WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD, false)
334         );
335         reqData.setValidateSamlSubjectConfirmation(
336             decodeBooleanConfigValue(mc, WSHandlerConstants.VALIDATE_SAML_SUBJECT_CONFIRMATION, true)
337         );
338 
339         boolean bspCompliant =
340             decodeBooleanConfigValue(mc, WSHandlerConstants.IS_BSP_COMPLIANT, true);
341         if (!bspCompliant) {
342             reqData.setDisableBSPEnforcement(true);
343         }
344 
345         // Load CallbackHandler
346         if (reqData.getCallbackHandler() == null) {
347             CallbackHandler passwordCallbackHandler = getPasswordCallbackHandler(reqData);
348             reqData.setCallbackHandler(passwordCallbackHandler);
349         }
350 
351         if (actions.contains(WSConstants.SIGN) || actions.contains(WSConstants.ST_SIGNED)
352             || actions.contains(WSConstants.ST_UNSIGNED)) {
353             decodeSignatureParameter2(reqData);
354         }
355 
356         if (actions.contains(WSConstants.ENCR)) {
357             decodeDecryptionParameter(reqData);
358         }
359         reqData.setRequireSignedEncryptedDataElements(
360             decodeBooleanConfigValue(
361                 mc, WSHandlerConstants.REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS, false
362             )
363         );
364         reqData.setRequireTimestampExpires(
365             decodeBooleanConfigValue(mc, WSHandlerConstants.REQUIRE_TIMESTAMP_EXPIRES, false)
366         );
367     }
368 
369     protected boolean checkReceiverResults(
370         List<WSSecurityEngineResult> wsResult, List<Integer> actions
371     ) {
372         int size = actions.size();
373         int ai = 0;
374         for (WSSecurityEngineResult result : wsResult) {
375             final Integer actInt = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
376             if (actInt != null) {
377                 int act = actInt;
378                 if (act == WSConstants.SC || act == WSConstants.BST) {
379                     continue;
380                 }
381 
382                 if (ai >= size || actions.get(ai++) != act) {
383                     return false;
384                 }
385             }
386         }
387 
388         return ai == size;
389     }
390 
391     protected boolean checkReceiverResultsAnyOrder(
392         List<WSSecurityEngineResult> wsResult, List<Integer> actions
393     ) {
394         List<Integer> recordedActions = new ArrayList<>(actions.size());
395         for (Integer action : actions) {
396             recordedActions.add(action);
397         }
398 
399         for (WSSecurityEngineResult result : wsResult) {
400             final Integer actInt = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
401             if (actInt != null) {
402                 int act = actInt;
403                 if (act == WSConstants.SC || act == WSConstants.BST) {
404                     continue;
405                 } else if (act == WSConstants.ENCR
406                     && (result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS) == null
407                         || ((List<?>)result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS)).isEmpty())) {
408                     continue;
409                 }
410 
411 
412                 if (!recordedActions.remove(actInt)) {
413                     return false;
414                 }
415             }
416         }
417 
418         return recordedActions.isEmpty();
419     }
420 
421     @SuppressWarnings("unchecked")
422     protected void checkSignatureConfirmation(
423         RequestData reqData,
424         WSHandlerResult handlerResults
425     ) throws WSSecurityException {
426         LOG.debug("Check Signature confirmation");
427         //
428         // First get all Signature values stored during sending the request
429         //
430         Set<Integer> savedSignatures =
431             (Set<Integer>) getProperty(reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV);
432         //
433         // Now get all results that hold a SignatureConfirmation element from
434         // the current run of receiver (we can have more than one run: if we
435         // have several security header blocks with different actors/roles)
436         //
437         List<WSSecurityEngineResult> sigConf =
438             handlerResults.getActionResults().get(WSConstants.SC);
439         //
440         // now loop over all SignatureConfirmation results and check:
441         // - if there is a signature value and no Signature value generated in request: error
442         // - if there is a signature value and no matching Signature value found: error
443         //
444         //  If a matching value found: remove from vector of stored signature values
445         //
446         if (sigConf != null) {
447             for (WSSecurityEngineResult result : sigConf) {
448                 SignatureConfirmation sc =
449                     (SignatureConfirmation)result.get(
450                         WSSecurityEngineResult.TAG_SIGNATURE_CONFIRMATION
451                     );
452 
453                 if (sc != null && sc.getSignatureValue() != null) {
454                     if (savedSignatures == null || savedSignatures.isEmpty()) {
455                         //
456                         // If there are no stored signature values, and we've received a
457                         // SignatureConfirmation element then throw an Exception
458                         //
459                         if (sc.getSignatureValue().length != 0) {
460                             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "empty",
461                                  new Object[] {"Received a SignatureConfirmation element, but there are no stored"
462                                  + " signature values"}
463                             );
464                         }
465                     } else {
466                         Integer hash = Arrays.hashCode(sc.getSignatureValue());
467                         if (savedSignatures.contains(hash)) {
468                             savedSignatures.remove(hash);
469                         } else {
470                             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
471                                 new Object[] {"Received a SignatureConfirmation element, but there are no matching"
472                                 + " stored signature values"}
473                             );
474                         }
475                     }
476                 }
477             }
478         }
479 
480         //
481         // the set holding the stored Signature values must be empty, otherwise we have an error
482         //
483         if (savedSignatures != null && !savedSignatures.isEmpty()) {
484             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
485                                           new Object[] {"Check Signature confirmation: the stored signature values"
486                                                         + " list is not empty"}
487             );
488         }
489     }
490 
491     protected void decodeUTParameter(RequestData reqData)
492         throws WSSecurityException {
493         Object mc = reqData.getMsgContext();
494 
495         String type = getString(WSHandlerConstants.PASSWORD_TYPE, mc);
496         if (type != null) {
497             if (WSConstants.PW_TEXT.equals(type)) {
498                 reqData.setPwType(WSConstants.PASSWORD_TEXT);
499             } else if (WSConstants.PW_DIGEST.equals(type)) {
500                 reqData.setPwType(WSConstants.PASSWORD_DIGEST);
501             } else if (WSConstants.PW_NONE.equals(type)) {
502                 reqData.setPwType(null);
503             } else {
504                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
505                         "empty", new Object[] {"Unknown password type encoding: " + type});
506             }
507         }
508 
509         boolean addNonce =
510             decodeBooleanConfigValue(mc, WSHandlerConstants.ADD_USERNAMETOKEN_NONCE, false);
511         reqData.setAddUsernameTokenNonce(addNonce);
512 
513         boolean addCreated =
514             decodeBooleanConfigValue(mc, WSHandlerConstants.ADD_USERNAMETOKEN_CREATED, false);
515         reqData.setAddUsernameTokenCreated(addCreated);
516 
517         String derivedMAC = getString(WSHandlerConstants.USE_DERIVED_KEY_FOR_MAC, mc);
518         boolean useDerivedKeyForMAC = Boolean.parseBoolean(derivedMAC);
519         if (useDerivedKeyForMAC) {
520             reqData.setUseDerivedKeyForMAC(useDerivedKeyForMAC);
521         }
522 
523         String iterations = getString(WSHandlerConstants.DERIVED_KEY_ITERATIONS, mc);
524         if (iterations != null) {
525             try {
526                 int iIterations = Integer.parseInt(iterations);
527                 reqData.setDerivedKeyIterations(iIterations);
528             } catch (NumberFormatException e) {
529                 LOG.warn("Error in configuring a derived key iteration count: " + e.getMessage());
530             }
531         }
532     }
533 
534     // Convert various Signature configuration into a single SignatureActionToken to be set on
535     // the RequestData object
536     protected void decodeSignatureParameter(RequestData reqData)
537         throws WSSecurityException {
538         Object mc = reqData.getMsgContext();
539         String signatureUser = getString(WSHandlerConstants.SIGNATURE_USER, mc);
540 
541         SignatureActionToken actionToken = reqData.getSignatureToken();
542         if (actionToken == null) {
543             actionToken = new SignatureActionToken();
544             reqData.setSignatureToken(actionToken);
545         }
546 
547         if (signatureUser != null) {
548             actionToken.setUser(signatureUser);
549         } else {
550             actionToken.setUser(reqData.getUsername());
551         }
552 
553         String keyId = getString(WSHandlerConstants.SIG_KEY_ID, mc);
554         if (keyId != null) {
555             Integer id = WSHandlerConstants.getKeyIdentifier(keyId);
556             if (id == null) {
557                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
558                         "empty",
559                         new Object[] {"WSHandler: Signature: unknown key identification"}
560                 );
561             }
562             int tmp = id;
563             if (!(tmp == WSConstants.ISSUER_SERIAL
564                     || tmp == WSConstants.ISSUER_SERIAL_QUOTE_FORMAT
565                     || tmp == WSConstants.BST_DIRECT_REFERENCE
566                     || tmp == WSConstants.X509_KEY_IDENTIFIER
567                     || tmp == WSConstants.SKI_KEY_IDENTIFIER
568                     || tmp == WSConstants.THUMBPRINT_IDENTIFIER
569                     || tmp == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER
570                     || tmp == WSConstants.KEY_VALUE)) {
571                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
572                         "empty",
573                         new Object[] {"WSHandler: Signature: illegal key identification"}
574                 );
575             }
576             actionToken.setKeyIdentifierId(tmp);
577         }
578         String algo = getString(WSHandlerConstants.SIG_ALGO, mc);
579         actionToken.setSignatureAlgorithm(algo);
580 
581         String derivedKeyReference = getString(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, mc);
582         actionToken.setDerivedKeyTokenReference(derivedKeyReference);
583 
584         String derivedKeyIdentifier = getString(WSHandlerConstants.DERIVED_TOKEN_KEY_ID, mc);
585         if (derivedKeyIdentifier != null) {
586             Integer id = WSHandlerConstants.getKeyIdentifier(derivedKeyIdentifier);
587             actionToken.setDerivedKeyIdentifier(id);
588         }
589 
590         String derivedKeyLength = getString(WSHandlerConstants.DERIVED_SIGNATURE_KEY_LENGTH, mc);
591         if (derivedKeyLength != null) {
592             try {
593                 int dKL = Integer.parseInt(derivedKeyLength);
594                 if (dKL > 0) {
595                     actionToken.setDerivedKeyLength(dKL);
596                 }
597             } catch (NumberFormatException e) {
598                 LOG.warn("Error in configuring a derived key length: " + e.getMessage());
599             }
600         }
601 
602         String digestAlgo = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc);
603         actionToken.setDigestAlgorithm(digestAlgo);
604 
605         String c14nAlgo = getString(WSHandlerConstants.SIG_C14N_ALGO, mc);
606         actionToken.setC14nAlgorithm(c14nAlgo);
607 
608         boolean use200512Namespace =
609             decodeBooleanConfigValue(mc, WSHandlerConstants.USE_2005_12_NAMESPACE, true);
610         reqData.setUse200512Namespace(use200512Namespace);
611 
612         String parts = getString(WSHandlerConstants.SIGNATURE_PARTS, mc);
613         if (parts != null) {
614             splitEncParts(true, parts, actionToken.getParts(), reqData);
615         }
616         parts = getString(WSHandlerConstants.OPTIONAL_SIGNATURE_PARTS, mc);
617         if (parts != null) {
618             splitEncParts(false, parts, actionToken.getParts(), reqData);
619         }
620 
621         boolean useSingleCert =
622             decodeBooleanConfigValue(mc, WSHandlerConstants.USE_SINGLE_CERTIFICATE, true);
623         actionToken.setUseSingleCert(useSingleCert);
624 
625         boolean includeToken =
626             decodeBooleanConfigValue(mc, WSHandlerConstants.INCLUDE_SIGNATURE_TOKEN, false);
627         actionToken.setIncludeToken(includeToken);
628 
629         if (!reqData.isExpandXopInclude()) {
630             boolean expandXOP =
631                 decodeBooleanConfigValue(
632                     reqData.getMsgContext(), WSHandlerConstants.EXPAND_XOP_INCLUDE, false
633             );
634             reqData.setExpandXopInclude(expandXOP);
635         }
636     }
637 
638     protected void decodeAlgorithmSuite(RequestData reqData) throws WSSecurityException {
639         Object mc = reqData.getMsgContext();
640         if (mc == null || reqData.getAlgorithmSuite() != null) {
641             return;
642         }
643 
644         AlgorithmSuite algorithmSuite = new AlgorithmSuite();
645 
646         String signatureAlgorithm = getString(WSHandlerConstants.SIG_ALGO, mc);
647         if (signatureAlgorithm != null && signatureAlgorithm.length() != 0) {
648             algorithmSuite.addSignatureMethod(signatureAlgorithm);
649         }
650         String signatureDigestAlgorithm = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc);
651         if (signatureDigestAlgorithm != null && !signatureDigestAlgorithm.isEmpty()) {
652             algorithmSuite.addDigestAlgorithm(signatureDigestAlgorithm);
653         }
654 
655         String encrAlgorithm = getString(WSHandlerConstants.ENC_SYM_ALGO, mc);
656         if (encrAlgorithm != null && !encrAlgorithm.isEmpty()) {
657             algorithmSuite.addEncryptionMethod(encrAlgorithm);
658         }
659         String transportAlgorithm = getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc);
660         if (transportAlgorithm != null && !transportAlgorithm.isEmpty()) {
661             algorithmSuite.addKeyWrapAlgorithm(transportAlgorithm);
662         }
663 
664         String keyAgreementMethodAlgorithm = getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc);
665         if (keyAgreementMethodAlgorithm != null && !keyAgreementMethodAlgorithm.isEmpty()) {
666             algorithmSuite.addKeyAgreementMethodAlgorithm(keyAgreementMethodAlgorithm);
667         }
668 
669         String keyDerivationAlgorithm = getString(WSHandlerConstants.ENC_KEY_DERIVATION_FUNCTION, mc);
670         if (keyDerivationAlgorithm != null && !keyDerivationAlgorithm.isEmpty()) {
671             algorithmSuite.addDerivedKeyAlgorithm(keyDerivationAlgorithm);
672         }
673 
674         reqData.setAlgorithmSuite(algorithmSuite);
675     }
676 
677     // Convert various Encryption configuration into a single EncryptionActionToken to be set on
678     // the RequestData object
679     protected void decodeEncryptionParameter(RequestData reqData)
680         throws WSSecurityException {
681         Object mc = reqData.getMsgContext();
682 
683         EncryptionActionToken actionToken = reqData.getEncryptionToken();
684         if (actionToken == null) {
685             actionToken = new EncryptionActionToken();
686             reqData.setEncryptionToken(actionToken);
687         }
688         //
689         // If the following parameters are no used (they return null) then the
690         // default values of WSS4J are used.
691         //
692         String encKeyId = getString(WSHandlerConstants.ENC_KEY_ID, mc);
693         if (encKeyId != null) {
694             Integer id = WSHandlerConstants.getKeyIdentifier(encKeyId);
695             if (id == null) {
696                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
697                         "empty",
698                         new Object[] {"WSHandler: Encryption: unknown key identification"}
699                 );
700             }
701             int tmp = id;
702             actionToken.setKeyIdentifierId(tmp);
703             if (!(tmp == WSConstants.ISSUER_SERIAL
704                     || tmp == WSConstants.ISSUER_SERIAL_QUOTE_FORMAT
705                     || tmp == WSConstants.X509_KEY_IDENTIFIER
706                     || tmp == WSConstants.SKI_KEY_IDENTIFIER
707                     || tmp == WSConstants.BST_DIRECT_REFERENCE
708                     || tmp == WSConstants.THUMBPRINT_IDENTIFIER
709                     || tmp == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER)) {
710                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
711                         "empty",
712                         new Object[] {"WSHandler: Encryption: illegal key identification"}
713                 );
714             }
715         }
716         String encSymAlgo = getString(WSHandlerConstants.ENC_SYM_ALGO, mc);
717         actionToken.setSymmetricAlgorithm(encSymAlgo);
718 
719         String encKeyTransport =
720             getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc);
721         actionToken.setKeyTransportAlgorithm(encKeyTransport);
722 
723         String encKeyAgreementMethod =
724                 getString(WSHandlerConstants.ENC_KEY_AGREEMENT_METHOD, mc);
725         actionToken.setKeyAgreementMethodAlgorithm(encKeyAgreementMethod);
726 
727         String encKeyDerivationAlgorithm =
728                 getString(WSHandlerConstants.ENC_KEY_DERIVATION_FUNCTION, mc);
729         actionToken.setKeyDerivationFunction(encKeyDerivationAlgorithm);
730 
731         Object obj = getProperty(mc, WSHandlerConstants.ENC_KEY_DERIVATION_PARAMS);
732         if (obj instanceof KeyDerivationParameters) {
733             actionToken.setKeyDerivationParameters((KeyDerivationParameters)obj);
734         }
735 
736         String derivedKeyReference = getString(WSHandlerConstants.DERIVED_TOKEN_REFERENCE, mc);
737         actionToken.setDerivedKeyTokenReference(derivedKeyReference);
738 
739         String derivedKeyIdentifier = getString(WSHandlerConstants.DERIVED_TOKEN_KEY_ID, mc);
740         if (derivedKeyIdentifier != null) {
741             Integer id = WSHandlerConstants.getKeyIdentifier(derivedKeyIdentifier);
742             actionToken.setDerivedKeyIdentifier(id);
743         }
744 
745         String derivedKeyLength = getString(WSHandlerConstants.DERIVED_ENCRYPTION_KEY_LENGTH, mc);
746         if (derivedKeyLength != null) {
747             try {
748                 int dKL = Integer.parseInt(derivedKeyLength);
749                 if (dKL > 0) {
750                     actionToken.setDerivedKeyLength(dKL);
751                 }
752             } catch (NumberFormatException e) {
753                 LOG.warn("Error in configuring a derived key length: " + e.getMessage());
754             }
755         }
756 
757         boolean use200512Namespace =
758             decodeBooleanConfigValue(mc, WSHandlerConstants.USE_2005_12_NAMESPACE, true);
759         reqData.setUse200512Namespace(use200512Namespace);
760 
761         boolean getSecretKeyFromCallbackHandler =
762             decodeBooleanConfigValue(mc, WSHandlerConstants.GET_SECRET_KEY_FROM_CALLBACK_HANDLER, false);
763         actionToken.setGetSymmetricKeyFromCallbackHandler(getSecretKeyFromCallbackHandler);
764 
765         String digestAlgo = getString(WSHandlerConstants.ENC_DIGEST_ALGO, mc);
766         actionToken.setDigestAlgorithm(digestAlgo);
767 
768         String mgfAlgo = getString(WSHandlerConstants.ENC_MGF_ALGO, mc);
769         actionToken.setMgfAlgorithm(mgfAlgo);
770 
771         String encSymEncKey = getString(WSHandlerConstants.ENC_SYM_ENC_KEY, mc);
772         if (encSymEncKey != null) {
773             boolean encSymEndKeyBoolean = Boolean.parseBoolean(encSymEncKey);
774             actionToken.setEncSymmetricEncryptionKey(encSymEndKeyBoolean);
775         }
776 
777         String encUser = getString(WSHandlerConstants.ENCRYPTION_USER, mc);
778         if (encUser != null) {
779             actionToken.setUser(encUser);
780         } else {
781             actionToken.setUser(reqData.getUsername());
782         }
783         if (actionToken.isEncSymmetricEncryptionKey() && actionToken.getUser() == null) {
784             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
785                     "empty",
786                     new Object[] {"WSHandler: Encryption: no username"});
787         }
788 
789         handleSpecialUser(reqData);
790 
791         String encParts = getString(WSHandlerConstants.ENCRYPTION_PARTS, mc);
792         if (encParts != null) {
793             splitEncParts(true, encParts, actionToken.getParts(), reqData);
794         }
795         encParts = getString(WSHandlerConstants.OPTIONAL_ENCRYPTION_PARTS, mc);
796         if (encParts != null) {
797             splitEncParts(false, encParts, actionToken.getParts(), reqData);
798         }
799 
800         boolean includeToken =
801             decodeBooleanConfigValue(mc, WSHandlerConstants.INCLUDE_ENCRYPTION_TOKEN, false);
802         actionToken.setIncludeToken(includeToken);
803     }
804 
805     /**
806      * Decode the TimeToLive parameter for either a Timestamp or a UsernameToken Created element,
807      * depending on the boolean argument
808      */
809     public int decodeTimeToLive(RequestData reqData, boolean timestamp) {
810         String tag = WSHandlerConstants.TTL_TIMESTAMP;
811         if (!timestamp) {
812             tag = WSHandlerConstants.TTL_USERNAMETOKEN;
813         }
814         String ttl = getString(tag, reqData.getMsgContext());
815         int defaultTimeToLive = 300;
816         if (ttl != null) {
817             try {
818                 int ttlI = Integer.parseInt(ttl);
819                 if (ttlI < 0) {
820                     return defaultTimeToLive;
821                 }
822                 return ttlI;
823             } catch (NumberFormatException e) {
824                 return defaultTimeToLive;
825             }
826         }
827         return defaultTimeToLive;
828     }
829 
830     /**
831      * Decode the FutureTimeToLive parameter for either a Timestamp or a UsernameToken Created
832      * element, depending on the boolean argument
833      */
834     protected int decodeFutureTimeToLive(RequestData reqData, boolean timestamp) {
835         String tag = WSHandlerConstants.TTL_FUTURE_TIMESTAMP;
836         if (!timestamp) {
837             tag = WSHandlerConstants.TTL_FUTURE_USERNAMETOKEN;
838         }
839         String ttl = getString(tag, reqData.getMsgContext());
840         int defaultFutureTimeToLive = 60;
841         if (ttl != null) {
842             try {
843                 int ttlI = Integer.parseInt(ttl);
844                 if (ttlI < 0) {
845                     return defaultFutureTimeToLive;
846                 }
847                 return ttlI;
848             } catch (NumberFormatException e) {
849                 return defaultFutureTimeToLive;
850             }
851         }
852         return defaultFutureTimeToLive;
853     }
854 
855     protected String decodePasswordType(RequestData reqData) throws WSSecurityException {
856         String type = getString(WSHandlerConstants.PASSWORD_TYPE, reqData.getMsgContext());
857         if (type != null) {
858             if (WSConstants.PW_TEXT.equals(type)) {
859                 return WSConstants.PASSWORD_TEXT;
860             } else if (WSConstants.PW_DIGEST.equals(type)) {
861                 return WSConstants.PASSWORD_DIGEST;
862             }
863         }
864         return null;
865     }
866 
867     protected boolean decodeBooleanConfigValue(
868         Object messageContext, String configTag, boolean defaultToTrue
869     ) throws WSSecurityException {
870 
871         String value = getString(configTag, messageContext);
872 
873         if (value == null) {
874             return defaultToTrue;
875         }
876         if ("0".equals(value) || "false".equals(value)) {
877             return false;
878         }
879         if ("1".equals(value) || "true".equals(value)) {
880             return true;
881         }
882 
883         throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
884                 "empty",
885                 new Object[] {"WSHandler: illegal " + configTag + " parameter"}
886         );
887     }
888 
889     /**
890      * Hook to allow subclasses to load their Signature creation Crypto however they see
891      * fit.
892      *
893      * @param requestData the RequestData object
894      * @return a Crypto instance to use for Signature creation
895      */
896     public Crypto loadSignatureCrypto(RequestData requestData) throws WSSecurityException {
897         return
898             loadCrypto(
899                 WSHandlerConstants.SIG_PROP_FILE,
900                 WSHandlerConstants.SIG_PROP_REF_ID,
901                 requestData
902             );
903     }
904 
905     /**
906      * Hook to allow subclasses to load their Signature verification Crypto however they see
907      * fit.
908      *
909      * @param requestData the RequestData object
910      * @return a Crypto instance to use for Signature verification
911      */
912     public Crypto loadSignatureVerificationCrypto(RequestData requestData)
913         throws WSSecurityException {
914         return
915             loadCrypto(
916                 WSHandlerConstants.SIG_VER_PROP_FILE,
917                 WSHandlerConstants.SIG_VER_PROP_REF_ID,
918                 requestData
919             );
920     }
921 
922     /**
923      * Hook to allow subclasses to load their Decryption Crypto however they see
924      * fit.
925      *
926      * @param requestData the RequestData object
927      * @return a Crypto instance to use for Decryption creation/verification
928      */
929     protected Crypto loadDecryptionCrypto(RequestData requestData) throws WSSecurityException {
930         return
931             loadCrypto(
932                 WSHandlerConstants.DEC_PROP_FILE,
933                 WSHandlerConstants.DEC_PROP_REF_ID,
934                 requestData
935             );
936     }
937 
938     /**
939      * Hook to allow subclasses to load their Encryption Crypto however they see
940      * fit.
941      *
942      * @param requestData the RequestData object
943      * @return a Crypto instance to use for Encryption creation/verification
944      */
945     protected Crypto loadEncryptionCrypto(RequestData requestData) throws WSSecurityException {
946         return
947             loadCrypto(
948                 WSHandlerConstants.ENC_PROP_FILE,
949                 WSHandlerConstants.ENC_PROP_REF_ID,
950                 requestData
951             );
952     }
953 
954     /**
955      * Load a Crypto instance. Firstly, it tries to use the cryptoPropertyRefId tag to retrieve
956      * a Crypto object via a custom reference Id. Failing this, it tries to load the crypto
957      * instance via the cryptoPropertyFile tag.
958      *
959      * @param requestData the RequestData object
960      * @return a Crypto instance to use for Encryption creation/verification
961      */
962     protected Crypto loadCrypto(
963         String cryptoPropertyFile,
964         String cryptoPropertyRefId,
965         RequestData requestData
966     ) throws WSSecurityException {
967         Object mc = requestData.getMsgContext();
968         Crypto crypto = null;
969 
970         //
971         // Try the Property Ref Id first
972         //
973         String refId = getString(cryptoPropertyRefId, mc);
974         if (refId != null) {
975             crypto = cryptos.get(refId);
976             if (crypto == null) {
977                 Object obj = getProperty(mc, refId);
978                 if (obj instanceof Properties) {
979                     crypto = CryptoFactory.getInstance((Properties)obj,
980                                                        Loader.getClassLoader(CryptoFactory.class),
981                                                        getPasswordEncryptor(requestData));
982                     cryptos.put(refId, crypto);
983                 } else if (obj instanceof Crypto) {
984                     // No need to cache this as it's already loaded
985                     crypto = (Crypto)obj;
986                 }
987             }
988             if (crypto == null) {
989                 LOG.warn("The Crypto reference " + refId + " specified by "
990                     + cryptoPropertyRefId + " could not be loaded"
991                 );
992             }
993         }
994 
995         //
996         // Now try loading the properties file
997         //
998         if (crypto == null) {
999             String propFile = getString(cryptoPropertyFile, mc);
1000             if (propFile != null) {
1001                 crypto = cryptos.get(propFile);
1002                 if (crypto == null) {
1003                     crypto = loadCryptoFromPropertiesFile(propFile, requestData);
1004                     cryptos.put(propFile, crypto);
1005                 }
1006                 if (crypto == null) {
1007                     LOG.warn(
1008                          "The Crypto properties file " + propFile + " specified by "
1009                          + cryptoPropertyFile + " could not be loaded or found"
1010                     );
1011                 }
1012             }
1013         }
1014 
1015         return crypto;
1016     }
1017 
1018     /**
1019      * A hook to allow subclass to load Crypto instances from property files in a different
1020      * way.
1021      * @param propFilename The property file name
1022      * @param reqData The RequestData object
1023      * @return A Crypto instance that has been loaded
1024      */
1025     protected Crypto loadCryptoFromPropertiesFile(
1026         String propFilename,
1027         RequestData reqData
1028     ) throws WSSecurityException {
1029         ClassLoader classLoader = this.getClassLoader();
1030         Properties properties = CryptoFactory.getProperties(propFilename, classLoader);
1031         return
1032             CryptoFactory.getInstance(
1033                 properties, classLoader, getPasswordEncryptor(reqData)
1034             );
1035     }
1036 
1037     /**
1038      * Get a CallbackHandler instance. First try to get an instance via the
1039      * callbackHandlerRef on the message context. Failing that, try to load a new
1040      * instance of the CallbackHandler via the callbackHandlerClass argument.
1041      *
1042      * @param callbackHandlerClass The class name of the CallbackHandler instance
1043      * @param callbackHandlerRef The reference name of the CallbackHandler instance
1044      * @param requestData The RequestData which supplies the message context
1045      * @return a CallbackHandler instance
1046      * @throws WSSecurityException
1047      */
1048     public CallbackHandler getCallbackHandler(
1049         String callbackHandlerClass,
1050         String callbackHandlerRef,
1051         RequestData requestData
1052     ) throws WSSecurityException {
1053         Object mc = requestData.getMsgContext();
1054         CallbackHandler cbHandler = (CallbackHandler) getOption(callbackHandlerRef);
1055         if (cbHandler == null) {
1056             cbHandler = (CallbackHandler) getProperty(mc, callbackHandlerRef);
1057         }
1058         if (cbHandler == null) {
1059             String callback = getString(callbackHandlerClass, mc);
1060             if (callback != null) {
1061                 cbHandler = loadCallbackHandler(callback);
1062             }
1063         }
1064         return cbHandler;
1065     }
1066 
1067     /**
1068      * Get a CallbackHandler instance to obtain passwords.
1069      * @param reqData The RequestData which supplies the message context
1070      * @return the CallbackHandler instance to obtain passwords.
1071      * @throws WSSecurityException
1072      */
1073     public CallbackHandler getPasswordCallbackHandler(RequestData reqData)
1074         throws WSSecurityException {
1075         return
1076             getCallbackHandler(
1077                 WSHandlerConstants.PW_CALLBACK_CLASS,
1078                 WSHandlerConstants.PW_CALLBACK_REF,
1079                 reqData
1080             );
1081     }
1082 
1083     /**
1084      * Load a CallbackHandler instance.
1085      * @param callbackHandlerClass The class name of the CallbackHandler instance
1086      * @return a CallbackHandler instance
1087      * @throws WSSecurityException
1088      */
1089     private CallbackHandler loadCallbackHandler(
1090         String callbackHandlerClass
1091     ) throws WSSecurityException {
1092 
1093         Class<? extends CallbackHandler> cbClass = null;
1094         CallbackHandler cbHandler = null;
1095         try {
1096             cbClass =
1097                 Loader.loadClass(getClassLoader(),
1098                                  callbackHandlerClass,
1099                                  CallbackHandler.class);
1100         } catch (ClassNotFoundException e) {
1101             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
1102                     "empty",
1103                     new Object[] {"WSHandler: cannot load callback handler class: "
1104                     + callbackHandlerClass}
1105             );
1106         }
1107         try {
1108             cbHandler = cbClass.getDeclaredConstructor().newInstance();
1109         } catch (Exception e) {
1110             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
1111                     "empty",
1112                     new Object[] {"WSHandler: cannot create instance of callback handler: "
1113                     + callbackHandlerClass
1114                     }
1115             );
1116         }
1117         return cbHandler;
1118     }
1119 
1120     protected PasswordEncryptor getPasswordEncryptor(RequestData requestData) {
1121         PasswordEncryptor passwordEncryptor = requestData.getPasswordEncryptor();
1122         if (passwordEncryptor == null) {
1123             Object o = getOption(WSHandlerConstants.PASSWORD_ENCRYPTOR_INSTANCE);
1124             if (o instanceof PasswordEncryptor) {
1125                 passwordEncryptor = (PasswordEncryptor) o;
1126             }
1127         }
1128         if (passwordEncryptor == null) {
1129             Object mc = requestData.getMsgContext();
1130             Object o = getProperty(mc, WSHandlerConstants.PASSWORD_ENCRYPTOR_INSTANCE);
1131             if (o instanceof PasswordEncryptor) {
1132                 passwordEncryptor = (PasswordEncryptor) o;
1133             }
1134         }
1135         if (passwordEncryptor == null) {
1136             CallbackHandler callbackHandler = requestData.getCallbackHandler();
1137             if (callbackHandler != null) {
1138                 passwordEncryptor = new JasyptPasswordEncryptor(callbackHandler);
1139             }
1140         }
1141 
1142         return passwordEncryptor;
1143     }
1144 
1145     /**
1146      * Get a password callback (WSPasswordCallback object) from a CallbackHandler instance
1147      * @param username The username to supply to the CallbackHandler
1148      * @param doAction The action to perform
1149      * @param callbackHandler The CallbackHandler instance
1150      * @param requestData The RequestData which supplies the message context
1151      * @return the WSPasswordCallback object containing the password
1152      * @throws WSSecurityException
1153      */
1154     public WSPasswordCallback getPasswordCB(
1155          String username,
1156          int doAction,
1157          CallbackHandler callbackHandler,
1158          RequestData requestData
1159     ) throws WSSecurityException {
1160 
1161         if (callbackHandler != null) {
1162             return performPasswordCallback(callbackHandler, username, doAction);
1163         } else {
1164             //
1165             // If a callback isn't configured then try to get the password
1166             // from the message context
1167             //
1168             String password = getPassword(requestData.getMsgContext());
1169             if (password == null) {
1170                 String err = "provided null or empty password";
1171                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
1172                         "empty",
1173                         new Object[] {"WSHandler: application " + err});
1174             }
1175             WSPasswordCallback pwCb = constructPasswordCallback(username, doAction);
1176             pwCb.setPassword(password);
1177             return pwCb;
1178         }
1179     }
1180 
1181     /**
1182      * Perform a callback on a CallbackHandler instance
1183      * @param cbHandler the CallbackHandler instance
1184      * @param username The username to supply to the CallbackHandler
1185      * @param doAction The action to perform
1186      * @return a WSPasswordCallback instance
1187      * @throws WSSecurityException
1188      */
1189     private WSPasswordCallback performPasswordCallback(
1190         CallbackHandler cbHandler,
1191         String username,
1192         int doAction
1193     ) throws WSSecurityException {
1194 
1195         WSPasswordCallback pwCb = constructPasswordCallback(username, doAction);
1196         Callback[] callbacks = new Callback[1];
1197         callbacks[0] = pwCb;
1198         //
1199         // Call back the application to get the password
1200         //
1201         try {
1202             cbHandler.handle(callbacks);
1203         } catch (Exception e) {
1204             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
1205                     "empty", new Object[] {"WSHandler: password callback failed"});
1206         }
1207         return pwCb;
1208     }
1209 
1210     /**
1211      * Construct a WSPasswordCallback instance
1212      * @param username The username
1213      * @param doAction The action to perform
1214      * @return a WSPasswordCallback instance
1215      * @throws WSSecurityException
1216      */
1217     private WSPasswordCallback constructPasswordCallback(
1218         String username,
1219         int doAction
1220     ) throws WSSecurityException {
1221 
1222         int reason;
1223 
1224         switch (doAction) {
1225         case WSConstants.UT:
1226         case WSConstants.UT_SIGN:
1227             reason = WSPasswordCallback.USERNAME_TOKEN;
1228             break;
1229         case WSConstants.SIGN:
1230             reason = WSPasswordCallback.SIGNATURE;
1231             break;
1232         case WSConstants.DKT_SIGN:
1233             reason = WSPasswordCallback.SECRET_KEY;
1234             break;
1235         case WSConstants.ENCR:
1236             reason = WSPasswordCallback.SECRET_KEY;
1237             break;
1238         case WSConstants.DKT_ENCR:
1239             reason = WSPasswordCallback.SECRET_KEY;
1240             break;
1241         default:
1242             reason = WSPasswordCallback.UNKNOWN;
1243             break;
1244         }
1245         return new WSPasswordCallback(username, reason);
1246     }
1247 
1248     private void splitEncParts(boolean required, String tmpS,
1249                                List<WSEncryptionPart> parts, RequestData reqData)
1250         throws WSSecurityException {
1251         WSEncryptionPart encPart = null;
1252         String[] rawParts = tmpS.split(";");
1253 
1254         for (String rawPart : rawParts) {
1255             String[] partDef = rawPart.split("}");
1256 
1257             if (partDef.length == 1) {
1258                 LOG.debug("single partDef: '{}'", partDef[0]);
1259                 encPart =
1260                     new WSEncryptionPart(partDef[0].trim(),
1261                             reqData.getSoapConstants().getEnvelopeURI(),
1262                             "Content");
1263             } else if (partDef.length == 2) {
1264                 String mode = partDef[0].trim().substring(1);
1265                 String element = partDef[1].trim();
1266                 encPart = new WSEncryptionPart(element, mode);
1267             } else if (partDef.length == 3) {
1268                 String mode = partDef[0].trim();
1269                 if (mode.length() <= 1) {
1270                     mode = "Content";
1271                 } else {
1272                     mode = mode.substring(1);
1273                 }
1274                 String nmSpace = partDef[1].trim();
1275                 if (nmSpace.length() <= 1) {
1276                     nmSpace = reqData.getSoapConstants().getEnvelopeURI();
1277                 } else {
1278                     nmSpace = nmSpace.substring(1);
1279                     if (nmSpace.equals(WSConstants.NULL_NS)) {
1280                         nmSpace = null;
1281                     }
1282                 }
1283                 String element = partDef[2].trim();
1284                 if (LOG.isDebugEnabled()) {
1285                     LOG.debug(
1286                         "partDefs: '" + mode + "' ,'" + nmSpace + "' ,'" + element + "'"
1287                     );
1288                 }
1289                 encPart = new WSEncryptionPart(element, nmSpace, mode);
1290             } else {
1291                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
1292                         "empty",
1293                         new Object[] {"WSHandler: wrong part definition: " + tmpS});
1294             }
1295             encPart.setRequired(required);
1296             parts.add(encPart);
1297         }
1298     }
1299 
1300     @SuppressWarnings("unchecked")
1301     private void handleSpecialUser(RequestData reqData) {
1302         EncryptionActionToken actionToken = reqData.getEncryptionToken();
1303         if (actionToken == null
1304             || !WSHandlerConstants.USE_REQ_SIG_CERT.equals(actionToken.getUser())) {
1305             return;
1306         }
1307         List<WSHandlerResult> results =
1308             (List<WSHandlerResult>) getProperty(
1309                 reqData.getMsgContext(), WSHandlerConstants.RECV_RESULTS
1310             );
1311         if (results == null) {
1312             return;
1313         }
1314         /*
1315          * Scan the results for a matching actor. Use results only if the
1316          * receiving Actor and the sending Actor match.
1317          */
1318         for (WSHandlerResult rResult : results) {
1319             String hActor = rResult.getActor();
1320             if (!WSSecurityUtil.isActorEqual(reqData.getActor(), hActor)) {
1321                 continue;
1322             }
1323             List<WSSecurityEngineResult> wsSecEngineResults = rResult.getResults();
1324             /*
1325              * Scan the results for the first Signature action. Use the
1326              * certificate of this Signature to set the certificate for the
1327              * encryption action :-).
1328              */
1329             for (WSSecurityEngineResult wser : wsSecEngineResults) {
1330                 Integer wserAction = (Integer) wser.get(WSSecurityEngineResult.TAG_ACTION);
1331                 if (wserAction != null && wserAction.intValue() == WSConstants.SIGN) {
1332                     X509Certificate cert =
1333                         (X509Certificate)wser.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
1334                     actionToken.setCertificate(cert);
1335                     return;
1336                 }
1337             }
1338         }
1339     }
1340 
1341     @SuppressWarnings("deprecation")
1342     protected void decodeSignatureParameter2(RequestData reqData)
1343         throws WSSecurityException {
1344         if (reqData.getSigVerCrypto() == null) {
1345             reqData.setSigVerCrypto(loadSignatureVerificationCrypto(reqData));
1346         }
1347         if (reqData.getSigVerCrypto() == null) {
1348             reqData.setSigVerCrypto(loadSignatureCrypto(reqData));
1349         }
1350         boolean enableRevocation =
1351             decodeBooleanConfigValue(
1352                 reqData.getMsgContext(), WSHandlerConstants.ENABLE_REVOCATION, false
1353             );
1354         reqData.setEnableRevocation(enableRevocation);
1355 
1356         String certConstraints =
1357             getString(WSHandlerConstants.SIG_SUBJECT_CERT_CONSTRAINTS, reqData.getMsgContext());
1358         if (certConstraints != null) {
1359             String certConstraintsSeparator =
1360                 getString(WSHandlerConstants.SIG_CERT_CONSTRAINTS_SEPARATOR, reqData.getMsgContext());
1361             if (certConstraintsSeparator == null || certConstraintsSeparator.isEmpty()) {
1362                 certConstraintsSeparator = ",";
1363             }
1364             Collection<Pattern> subjectCertConstraints = getCertConstraints(certConstraints, certConstraintsSeparator);
1365             reqData.setSubjectCertConstraints(subjectCertConstraints);
1366         }
1367         String issuerCertConstraintsStringValue =
1368             getString(WSHandlerConstants.SIG_ISSUER_CERT_CONSTRAINTS, reqData.getMsgContext());
1369         if (issuerCertConstraintsStringValue != null) {
1370             String certConstraintsSeparator =
1371                 getString(WSHandlerConstants.SIG_CERT_CONSTRAINTS_SEPARATOR, reqData.getMsgContext());
1372             if (certConstraintsSeparator == null || certConstraintsSeparator.isEmpty()) {
1373                 certConstraintsSeparator = ",";
1374             }
1375             Collection<Pattern> issuerCertConstraints =
1376                 getCertConstraints(issuerCertConstraintsStringValue, certConstraintsSeparator);
1377             reqData.setIssuerDNPatterns(issuerCertConstraints);
1378         }
1379 
1380         String value = getString(WSHandlerConstants.EXPAND_XOP_INCLUDE_FOR_SIGNATURE, reqData.getMsgContext());
1381         boolean expandXOP = false;
1382         if (value != null) {
1383             expandXOP =
1384                 decodeBooleanConfigValue(
1385                     reqData.getMsgContext(), WSHandlerConstants.EXPAND_XOP_INCLUDE_FOR_SIGNATURE, true
1386                 );
1387         } else {
1388             expandXOP =
1389                 decodeBooleanConfigValue(
1390                     reqData.getMsgContext(), WSHandlerConstants.EXPAND_XOP_INCLUDE, true
1391             );
1392         }
1393         reqData.setExpandXopInclude(expandXOP);
1394     }
1395 
1396     private Collection<Pattern> getCertConstraints(String certConstraints, String separator) throws WSSecurityException {
1397         String[] certConstraintsList = certConstraints.split(separator);
1398         if (certConstraintsList != null && certConstraintsList.length > 0) {
1399             Collection<Pattern> certConstraintsCollection =
1400                 new ArrayList<>(certConstraintsList.length);
1401             for (String certConstraint : certConstraintsList) {
1402                 try {
1403                     certConstraintsCollection.add(Pattern.compile(certConstraint.trim()));
1404                 } catch (PatternSyntaxException ex) {
1405                     LOG.debug(ex.getMessage(), ex);
1406                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
1407                 }
1408             }
1409 
1410             return certConstraintsCollection;
1411         }
1412         return Collections.emptyList();
1413     }
1414 
1415     /*
1416      * Set and check the decryption specific parameters, if necessary
1417      * take over signature crypto instance.
1418      */
1419     protected void decodeDecryptionParameter(RequestData reqData)
1420         throws WSSecurityException {
1421         if (reqData.getDecCrypto() == null) {
1422             reqData.setDecCrypto(loadDecryptionCrypto(reqData));
1423         }
1424 
1425         boolean allowRsa15 =
1426             decodeBooleanConfigValue(
1427                 reqData.getMsgContext(), WSHandlerConstants.ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM,
1428                 false
1429             );
1430         reqData.setAllowRSA15KeyTransportAlgorithm(allowRsa15);
1431     }
1432 
1433     /**
1434      * Looks up key first via {@link #getOption(String)} and if not found
1435      * there, via {@link #getProperty(Object, String)}
1436      *
1437      * @param key the key to search for. May not be null.
1438      * @param mc the message context to search.
1439      * @return the value found.
1440      * @throws IllegalArgumentException if <code>key</code> is null.
1441      */
1442     public String getString(String key, Object mc) {
1443         if (key == null) {
1444             throw new IllegalArgumentException("Key cannot be null");
1445         }
1446         String s = getStringOption(key);
1447         if (s != null) {
1448             return s;
1449         }
1450         if (mc == null) {
1451             throw new IllegalArgumentException("Message context cannot be null");
1452         }
1453         return (String) getProperty(mc, key);
1454     }
1455 
1456 
1457     /**
1458      * Returns the option on <code>name</code>.
1459      *
1460      * @param key the non-null key of the option.
1461      * @return the option on <code>key</code> if <code>key</code>
1462      *  exists and is of type java.lang.String; otherwise null.
1463      */
1464     public String getStringOption(String key) {
1465         Object o = getOption(key);
1466         if (o instanceof String) {
1467             return (String) o;
1468         } else {
1469             return null;
1470         }
1471     }
1472 
1473     /**
1474      * Returns the classloader to be used for loading the callback class
1475      * @return class loader
1476      */
1477     public ClassLoader getClassLoader() {
1478         try {
1479             return Loader.getTCL();
1480         } catch (Exception ex) {
1481             return null;
1482         }
1483     }
1484 
1485     public abstract Object getOption(String key);
1486     public abstract Object getProperty(Object msgContext, String key);
1487 
1488     public abstract void setProperty(Object msgContext, String key,
1489             Object value);
1490 
1491 
1492     public abstract String getPassword(Object msgContext);
1493 
1494     public abstract void setPassword(Object msgContext, String password);
1495 }