View Javadoc

1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.soap.security.wss4j;
18  
19  import java.io.IOException;
20  import java.security.Principal;
21  import java.security.cert.X509Certificate;
22  import java.util.ArrayList;
23  import java.util.List;
24  import javax.security.auth.callback.Callback;
25  import javax.security.auth.callback.CallbackHandler;
26  import javax.security.auth.callback.UnsupportedCallbackException;
27  
28  import org.apache.ws.security.WSConstants;
29  import org.apache.ws.security.WSSConfig;
30  import org.apache.ws.security.WSSecurityEngine;
31  import org.apache.ws.security.WSSecurityEngineResult;
32  import org.apache.ws.security.WSSecurityException;
33  import org.apache.ws.security.WSUsernameTokenPrincipal;
34  import org.apache.ws.security.components.crypto.Crypto;
35  import org.apache.ws.security.handler.RequestData;
36  import org.apache.ws.security.handler.WSHandlerConstants;
37  import org.apache.ws.security.handler.WSHandlerResult;
38  import org.apache.ws.security.message.token.Timestamp;
39  import org.apache.ws.security.util.WSSecurityUtil;
40  import org.apache.ws.security.validate.Credential;
41  import org.apache.ws.security.validate.SignatureTrustValidator;
42  import org.apache.ws.security.validate.TimestampValidator;
43  import org.w3c.dom.Document;
44  
45  import org.springframework.beans.factory.InitializingBean;
46  import org.springframework.util.Assert;
47  import org.springframework.util.CollectionUtils;
48  import org.springframework.util.StringUtils;
49  import org.springframework.ws.context.MessageContext;
50  import org.springframework.ws.soap.SoapMessage;
51  import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
52  import org.springframework.ws.soap.security.WsSecuritySecurementException;
53  import org.springframework.ws.soap.security.WsSecurityValidationException;
54  import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
55  import org.springframework.ws.soap.security.callback.CleanupCallback;
56  import org.springframework.ws.soap.security.wss4j.callback.UsernameTokenPrincipalCallback;
57  
58  /**
59   * A WS-Security endpoint interceptor based on Apache's WSS4J. This interceptor supports messages created by the [email protected]
60   * org.springframework.ws.soap.axiom.AxiomSoapMessageFactory} and the [email protected] org.springframework.ws.soap.saaj.SaajSoapMessageFactory}.
61   * <p/>
62   * The validation and securement actions executed by this interceptor are configured via <code>validationActions</code>
63   * and <code>securementActions</code> properties, respectively. Actions should be passed as a space-separated strings.
64   * <p/>
65   * Valid <strong>validation</strong> actions are:
66   * <p/>
67   * <blockquote><table> <tr><th>Validation action</th><th>Description</th></tr> <tr><td><code>UsernameToken</code></td><td>Validates
68   * username token</td></tr> <tr><td><code>Timestamp</code></td><td>Validates the timestamp</td></tr>
69   * <tr><td><code>Encrypt</code></td><td>Decrypts the message</td></tr> <tr><td><code>Signature</code></td><td>Validates
70   * the signature</td></tr> <tr><td><code>NoSecurity</code></td><td>No action performed</td></tr> </table></blockquote>
71   * <p/>
72   * <strong>Securement</strong> actions are: <blockquote><table> <tr><th>Securement action</th><th>Description</th></tr>
73   * <tr><td><code>UsernameToken</td></code><td>Adds a username token</td></tr> <tr><td><code>UsernameTokenSignature</td></code><td>Adds
74   * a username token and a signature username token secret key</td></tr> <tr><td><code>Timestamp</td></code><td>Adds a
75   * timestamp</td></tr> <tr><td><code>Encrypt</td></code><td>Encrypts the response</td></tr>
76   * <tr><td><code>Signature</td></code><td>Signs the response</td></tr> <tr><td><code>NoSecurity</td></code><td>No action
77   * performed</td></tr> </table></blockquote>
78   * <p/>
79   * The order of the actions that the client performed to secure the messages is significant and is enforced by the
80   * interceptor.
81   *
82   * @author Tareq Abed Rabbo
83   * @author Arjen Poutsma
84   * @see <a href="http://ws.apache.org/wss4j/">Apache WSS4J</a>
85   * @since 1.5.0
86   */
87  public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor implements InitializingBean {
88  
89      public static final String SECUREMENT_USER_PROPERTY_NAME = "Wss4jSecurityInterceptor.securementUser";
90  
91      private int securementAction;
92  
93      private String securementActions;
94  
95      private List<Integer> securementActionsVector;
96  
97      private String securementUsername;
98  
99      private CallbackHandler validationCallbackHandler;
100 
101     private int validationAction;
102 
103     private String validationActions;
104 
105     private List<Integer> validationActionsVector;
106 
107     private String validationActor;
108 
109     private Crypto validationDecryptionCrypto;
110 
111     private Crypto validationSignatureCrypto;
112 
113     private boolean timestampStrict = true;
114 
115     private boolean enableSignatureConfirmation;
116 
117     private int validationTimeToLive = 300;
118 
119     private int securementTimeToLive = 300;
120     
121     private WSSConfig wssConfig;
122 
123     private final Wss4jHandler handler = new Wss4jHandler();
124 
125     private final WSSecurityEngine securityEngine = new WSSecurityEngine();
126 
127     private boolean enableRevocation;
128 
129     private boolean bspCompliant;
130 
131     private boolean securementUseDerivedKey;
132 
133     public void setSecurementActions(String securementActions) {
134         this.securementActions = securementActions;
135         securementActionsVector = new ArrayList<Integer>();
136         try {
137             securementAction = WSSecurityUtil.decodeAction(securementActions, securementActionsVector);
138         }
139         catch (WSSecurityException ex) {
140             throw new IllegalArgumentException(ex);
141         }
142     }
143 
144     /**
145      * The actor name of the <code>wsse:Security</code> header.
146      * <p/>
147      * If this parameter is omitted, the actor name is not set.
148      * <p/>
149      * The value of the actor or role has to match the receiver's setting or may contain standard values.
150      */
151     public void setSecurementActor(String securementActor) {
152         handler.setOption(WSHandlerConstants.ACTOR, securementActor);
153     }
154 
155     public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
156         handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
157     }
158 
159     /** Sets the key name that needs to be sent for encryption. */
160     public void setSecurementEncryptionEmbeddedKeyName(String securementEncryptionEmbeddedKeyName) {
161         handler.setOption(WSHandlerConstants.ENC_KEY_NAME, securementEncryptionEmbeddedKeyName);
162     }
163 
164     /**
165      * Defines which key identifier type to use. The WS-Security specifications recommends to use the identifier type
166      * <code>IssuerSerial</code>. For possible encryption key identifier types refer to [email protected]
167      * org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For encryption <code>IssuerSerial</code>,
168      * <code>X509KeyIdentifier</code>,  <code>DirectReference</code>, <code>Thumbprint</code>,
169      * <code>SKIKeyIdentifier</code>, and <code>EmbeddedKeyName</code> are valid only.
170      */
171     public void setSecurementEncryptionKeyIdentifier(String securementEncryptionKeyIdentifier) {
172         handler.setOption(WSHandlerConstants.ENC_KEY_ID, securementEncryptionKeyIdentifier);
173     }
174 
175     /**
176      * Defines which algorithm to use to encrypt the generated symmetric key. Currently WSS4J supports [email protected]
177      * WSConstants#KEYTRANSPORT_RSA15} and [email protected] WSConstants#KEYTRANSPORT_RSAOEP}.
178      */
179     public void setSecurementEncryptionKeyTransportAlgorithm(String securementEncryptionKeyTransportAlgorithm) {
180         handler.setOption(WSHandlerConstants.ENC_KEY_TRANSPORT, securementEncryptionKeyTransportAlgorithm);
181     }
182 
183     /**
184      * Property to define which parts of the request shall be encrypted.
185      * <p/>
186      * The value of this property is a list of semi-colon separated element names that identify the elements to encrypt.
187      * An encryption mode specifier and a namespace identification, each inside a pair of curly brackets, may precede
188      * each element name.
189      * <p/>
190      * The encryption mode specifier is either <code>{Content}</code> or <code>{Element}</code>. Please refer to the W3C
191      * XML Encryption specification about the differences between Element and Content encryption. The encryption mode
192      * defaults to <code>Content</code> if it is omitted. Example of a list:
193      * <pre>
194      * &lt;property name="securementEncryptionParts"
195      *   value="{Content}{http://example.org/paymentv2}CreditCard;
196      *             {Element}{}UserName" />
197      * </pre>
198      * The the first entry of the list identifies the element <code>CreditCard</code> in the namespace
199      * <code>http://example.org/paymentv2</code>, and will encrypt its content. Be aware that the element name, the
200      * namespace identifier, and the encryption modifier are case sensitive.
201      * <p/>
202      * The encryption modifier and the namespace identifier can be omitted. In this case the encryption mode defaults to
203      * <code>Content</code> and the namespace is set to the SOAP namespace.
204      * <p/>
205      * An empty encryption mode defaults to <code>Content</code>, an empty namespace identifier defaults to the SOAP
206      * namespace. The second line of the example defines <code>Element</code> as encryption mode for an
207      * <code>UserName</code> element in the SOAP namespace.
208      * <p/>
209      * To specify an element without a namespace use the string <code>Null</code> as the namespace name (this is a case
210      * sensitive string)
211      * <p/>
212      * If no list is specified, the handler encrypts the SOAP Body in <code>Content</code> mode by default.
213      */
214     public void setSecurementEncryptionParts(String securementEncryptionParts) {
215         handler.setOption(WSHandlerConstants.ENCRYPTION_PARTS, securementEncryptionParts);
216     }
217 
218     /**
219      * Defines which symmetric encryption algorithm to use. WSS4J supports the following alorithms: [email protected]
220      * WSConstants#TRIPLE_DES}, [email protected] WSConstants#AES_128}, [email protected] WSConstants#AES_256}, and [email protected]
221      * WSConstants#AES_192}. Except for AES 192 all of these algorithms are required by the XML Encryption
222      * specification.
223      */
224     public void setSecurementEncryptionSymAlgorithm(String securementEncryptionSymAlgorithm) {
225         this.handler.setOption(WSHandlerConstants.ENC_SYM_ALGO, securementEncryptionSymAlgorithm);
226     }
227 
228     /**
229      * The user's name for encryption.
230      * <p/>
231      * The encryption functions uses the public key of this user's certificate to encrypt the generated symmetric key.
232      * <p/>
233      * If this parameter is not set, then the encryption function falls back to the [email protected]
234      * org.apache.ws.security.handler.WSHandlerConstants#USER} parameter to get the certificate.
235      * <p/>
236      * If <b>only</b> encryption of the SOAP body data is requested, it is recommended to use this parameter to define
237      * the username. The application can then use the standard user and password functions (see example at [email protected]
238      * org.apache.ws.security.handler.WSHandlerConstants#USER} to enable HTTP authentication functions.
239      * <p/>
240      * Encryption only does not authenticate a user / sender, therefore it does not need a password.
241      * <p/>
242      * Placing the username of the encryption certificate in the configuration file is not a security risk, because the
243      * public key of that certificate is used only.
244      * <p/>
245      */
246     public void setSecurementEncryptionUser(String securementEncryptionUser) {
247         handler.setOption(WSHandlerConstants.ENCRYPTION_USER, securementEncryptionUser);
248     }
249 
250     public void setSecurementPassword(String securementPassword) {
251         this.handler.setSecurementPassword(securementPassword);
252     }
253 
254     /**
255      * Specific parameter for UsernameToken action to define the encoding of the passowrd.
256      * <p/>
257      * The parameter can be set to either [email protected] WSConstants#PW_DIGEST} or to [email protected] WSConstants#PW_TEXT}.
258      * <p/>
259      * The default setting is PW_DIGEST.
260      */
261     public void setSecurementPasswordType(String securementUsernameTokenPasswordType) {
262         handler.setOption(WSHandlerConstants.PASSWORD_TYPE, securementUsernameTokenPasswordType);
263     }
264 
265     /**
266      * Defines which signature algorithm to use.
267      * @see WSConstants#RSA
268      * @see WSConstants#DSA
269      */
270     public void setSecurementSignatureAlgorithm(String securementSignatureAlgorithm) {
271         handler.setOption(WSHandlerConstants.SIG_ALGO, securementSignatureAlgorithm);
272     }
273 
274     /**
275      * Defines which signature digest algorithm to use.
276      */
277     public void setSecurementSignatureDigestAlgorithm(String digestAlgorithm) {
278         handler.setOption(WSHandlerConstants.SIG_DIGEST_ALGO, digestAlgorithm);
279     }
280 
281     public void setSecurementSignatureCrypto(Crypto securementSignatureCrypto) {
282         handler.setSecurementSignatureCrypto(securementSignatureCrypto);
283     }
284 
285     /**
286      * Defines which key identifier type to use. The WS-Security specifications recommends to use the identifier type
287      * <code>IssuerSerial</code>. For possible signature key identifier types refer to [email protected]
288      * org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For signature <code>IssuerSerial</code> and
289      * <code>DirectReference</code> are valid only.
290      */
291     public void setSecurementSignatureKeyIdentifier(String securementSignatureKeyIdentifier) {
292         handler.setOption(WSHandlerConstants.SIG_KEY_ID, securementSignatureKeyIdentifier);
293     }
294 
295     /**
296      * Property to define which parts of the request shall be signed.
297      * <p/>
298      * Refer to [email protected] #setSecurementEncryptionParts(String)} for a detailed description of the format of the value
299      * string.
300      * <p/>
301      * If this property is not specified the handler signs the SOAP Body by default.
302      * <p/>
303      * The WS Security specifications define several formats to transfer the signature tokens (certificates) or
304      * references to these tokens. Thus, the plain element name <code>Token</code> signs the token and takes care of the
305      * different formats.
306      * <p/>
307      * To sign the SOAP body <b>and</b> the signature token the value of this parameter must contain:
308      * <pre>
309      * &lt;property name="securementSignatureParts"
310      *   value="{}{http://schemas.xmlsoap.org/soap/envelope/}Body; Token" />
311      * </pre>
312      * To specify an element without a namespace use the string <code>Null</code> as the namespace name (this is a case
313      * sensitive string)
314      * <p/>
315      * If there is no other element in the request with a local name of <code>Body</code> then the SOAP namespace
316      * identifier can be empty (<code>{}</code>).
317      */
318     public void setSecurementSignatureParts(String securementSignatureParts) {
319         handler.setOption(WSHandlerConstants.SIGNATURE_PARTS, securementSignatureParts);
320     }
321 
322     /**
323      * The user's name for signature.
324      * <p/>
325      * This name is used as the alias name in the keystore to get user's
326      * certificate and private key to perform signing.
327      * <p/>
328      * If this parameter is not set, then the signature
329      * function falls back to the alias specified by [email protected] #setSecurementUsername(String)}.
330      * <p/>
331      */
332     public void setSecurementSignatureUser(String securementSignatureUser) {
333         handler.setOption(WSHandlerConstants.SIGNATURE_USER, securementSignatureUser);
334     }
335 
336     /** Sets the username for securement username token or/and the alias of the private key for securement signature */
337     public void setSecurementUsername(String securementUsername) {
338         this.securementUsername = securementUsername;
339     }
340 
341     /** Sets the time to live on the outgoing message */
342     public void setSecurementTimeToLive(int securementTimeToLive) {
343         if (securementTimeToLive <= 0) {
344             throw new IllegalArgumentException("timeToLive must be positive");
345         }
346         this.securementTimeToLive = securementTimeToLive;
347     }
348 
349     /**
350      * Enables the derivation of keys as per the UsernameTokenProfile 1.1 spec. Default is [email protected] true}.
351      */
352     public void setSecurementUseDerivedKey(boolean securementUseDerivedKey) {
353         this.securementUseDerivedKey = securementUseDerivedKey;
354     }
355 
356     /** Sets the server-side time to live */
357     public void setValidationTimeToLive(int validationTimeToLive) {
358         if (validationTimeToLive <= 0) {
359             throw new IllegalArgumentException("timeToLive must be positive");
360         }
361         this.validationTimeToLive = validationTimeToLive;
362     }
363 
364     /** Sets the validation actions to be executed by the interceptor. */
365     public void setValidationActions(String actions) {
366         this.validationActions = actions;
367         try {
368             validationActionsVector = new ArrayList<Integer>();
369             validationAction = WSSecurityUtil.decodeAction(actions, validationActionsVector);
370         }
371         catch (WSSecurityException ex) {
372             throw new IllegalArgumentException(ex);
373         }
374     }
375 
376     public void setValidationActor(String validationActor) {
377         this.validationActor = validationActor;
378     }
379 
380     /**
381      * Sets the [email protected] org.apache.ws.security.WSPasswordCallback} handler to use when validating messages.
382      *
383      * @see #setValidationCallbackHandlers(CallbackHandler[])
384      */
385     public void setValidationCallbackHandler(CallbackHandler callbackHandler) {
386         this.validationCallbackHandler = callbackHandler;
387     }
388 
389     /**
390      * Sets the [email protected] org.apache.ws.security.WSPasswordCallback} handlers to use when validating messages.
391      *
392      * @see #setValidationCallbackHandler(CallbackHandler)
393      */
394     public void setValidationCallbackHandlers(CallbackHandler[] callbackHandler) {
395         this.validationCallbackHandler = new CallbackHandlerChain(callbackHandler);
396     }
397 
398     /** Sets the Crypto to use to decrypt incoming messages */
399     public void setValidationDecryptionCrypto(Crypto decryptionCrypto) {
400         this.validationDecryptionCrypto = decryptionCrypto;
401     }
402 
403     /** Sets the Crypto to use to verify the signature of incoming messages */
404     public void setValidationSignatureCrypto(Crypto signatureCrypto) {
405         this.validationSignatureCrypto = signatureCrypto;
406     }
407 
408     /** Whether to enable signatureConfirmation or not. By default signatureConfirmation is enabled */
409     public void setEnableSignatureConfirmation(boolean enableSignatureConfirmation) {
410         handler.setOption(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, enableSignatureConfirmation);
411         this.enableSignatureConfirmation = enableSignatureConfirmation;
412     }
413 
414     /** Sets if the generated timestamp header's precision is in milliseconds. */
415     public void setTimestampPrecisionInMilliseconds(boolean timestampPrecisionInMilliseconds) {
416         handler.setOption(WSHandlerConstants.TIMESTAMP_PRECISION, timestampPrecisionInMilliseconds);
417     }
418 
419     /** Sets whether or not timestamp verification is done with the server-side time to live */
420     public void setTimestampStrict(boolean timestampStrict) {
421         this.timestampStrict = timestampStrict;
422     }
423 
424     /**
425      * Enables the <code>mustUnderstand</code> attribute on WS-Security headers on outgoing messages. Default is
426      * <code>true</code>.
427      */
428     public void setSecurementMustUnderstand(boolean securementMustUnderstand) {
429         handler.setOption(WSHandlerConstants.MUST_UNDERSTAND, securementMustUnderstand);
430     }
431 
432     /**
433      * Sets the additional elements in <code>UsernameToken</code>s.
434      * <p/>
435      * The value of this parameter is a list of element names that are added to the UsernameToken. The names of the list
436      * a separated by spaces.
437      * <p/>
438      * The list may contain the names <code>Nonce</code> and <code>Created</code> only (case sensitive). Use this option
439      * if the password type is <code>passwordText</code> and the handler shall add the <code>Nonce</code> and/or
440      * <code>Created</code> elements.
441      */
442     public void setSecurementUsernameTokenElements(String securementUsernameTokenElements) {
443         handler.setOption(WSHandlerConstants.ADD_UT_ELEMENTS, securementUsernameTokenElements);
444     }
445     
446     /**
447      * Sets the web service specification settings.
448      * <p>
449      * The default settings follow the latest OASIS and changing anything might violate the OASIS specs.
450      *  
451      * @param config web service security configuration or [email protected] null} to use default settings
452      */
453     public void setWssConfig(WSSConfig config) {
454     	securityEngine.setWssConfig(config);
455     	wssConfig = config;
456     }
457 
458     /**
459      * Set whether to enable CRL checking or not when verifying trust in a certificate.
460      */
461     public void setEnableRevocation(boolean enableRevocation) {
462         this.enableRevocation = enableRevocation;
463     }
464 
465     /**
466      * Set the WS-I Basic Security Profile compliance mode. Default is [email protected] true}.
467      */
468     public void setBspCompliant(boolean bspCompliant) {
469 	    this.handler.setOption(WSHandlerConstants.IS_BSP_COMPLIANT, bspCompliant);
470         this.bspCompliant = bspCompliant;
471     }
472 
473     /**
474      * Sets the location of the SAML properties file. The file should be available on the classpath.
475      */
476     public void setSamlProperties(String location) {
477         handler.setOption(WSHandlerConstants.SAML_PROP_FILE, location);
478     }
479 
480     public void afterPropertiesSet() throws Exception {
481         Assert.isTrue(validationActions != null || securementActions != null,
482                 "validationActions or securementActions are required");
483         if (validationActions != null) {
484             if ((validationAction & WSConstants.UT) != 0) {
485                 Assert.notNull(validationCallbackHandler, "validationCallbackHandler is required");
486             }
487 
488             if ((validationAction & WSConstants.SIGN) != 0) {
489                 Assert.notNull(validationSignatureCrypto, "validationSignatureCrypto is required");
490             }
491         }
492         // securement actions are not to be validated at start up as they could
493         // be configured dynamically via the message context
494 
495         // allow for qualified password types for .Net interoperability
496         securityEngine.getWssConfig().setAllowNamespaceQualifiedPasswordTypes(true);
497         securityEngine.getWssConfig().setWsiBSPCompliant(bspCompliant);
498     }
499 
500     @Override
501     protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
502             throws WsSecuritySecurementException {
503         if (securementAction == WSConstants.NO_SECURITY && !enableSignatureConfirmation) {
504             return;
505         }
506         if (logger.isDebugEnabled()) {
507             logger.debug("Securing message [" + soapMessage + "] with actions [" + securementActions + "]");
508         }
509         RequestData requestData = initializeRequestData(messageContext);
510 
511         Document envelopeAsDocument = soapMessage.getDocument();
512         try {
513             // In case on signature confirmation with no other securement
514             // action, we need to pass an empty securementActionsVector to avoid
515             // NPE
516             if (securementAction == WSConstants.NO_SECURITY) {
517                 securementActionsVector = new ArrayList<Integer>(0);
518             }
519 
520             handler.doSenderAction(securementAction, envelopeAsDocument, requestData, securementActionsVector, false);
521         }
522         catch (WSSecurityException ex) {
523             throw new Wss4jSecuritySecurementException(ex.getMessage(), ex);
524         }
525 
526         soapMessage.setDocument(envelopeAsDocument);
527     }
528 
529     /**
530      * Creates and initializes a request data for the given message context.
531      *
532      * @param messageContext the message context
533      * @return the request data
534      */
535     protected RequestData initializeRequestData(MessageContext messageContext) {
536         RequestData requestData = new RequestData();
537         requestData.setMsgContext(messageContext);
538 
539         // reads securementUsername first from the context then from the property
540         String contextUsername = (String) messageContext.getProperty(SECUREMENT_USER_PROPERTY_NAME);
541         if (StringUtils.hasLength(contextUsername)) {
542             requestData.setUsername(contextUsername);
543         }
544         else {
545             requestData.setUsername(securementUsername);
546         }
547 
548         requestData.setTimeToLive(securementTimeToLive);
549 
550         requestData.setUseDerivedKey(securementUseDerivedKey);
551         
552         requestData.setWssConfig(wssConfig);
553 
554         return requestData;
555     }
556 
557     @Override
558     @SuppressWarnings("unchecked")
559     protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
560             throws WsSecurityValidationException {
561         if (logger.isDebugEnabled()) {
562             logger.debug("Validating message [" + soapMessage + "] with actions [" + validationActions + "]");
563         }
564 
565         if (validationAction == WSConstants.NO_SECURITY) {
566             return;
567         }
568 
569         Document envelopeAsDocument = soapMessage.getDocument();
570 
571         // Header processing
572 
573         try {
574             List<WSSecurityEngineResult> results = securityEngine
575                     .processSecurityHeader(envelopeAsDocument, validationActor, validationCallbackHandler,
576                             validationSignatureCrypto, validationDecryptionCrypto);
577 
578             // Results verification
579             if (CollectionUtils.isEmpty(results)) {
580                 throw new Wss4jSecurityValidationException("No WS-Security header found");
581             }
582 
583             checkResults(results, validationActionsVector);
584 
585             // puts the results in the context
586             // useful for Signature Confirmation
587             updateContextWithResults(messageContext, results);
588 
589             verifyCertificateTrust(results);
590 
591             verifyTimestamp(results);
592 
593             processPrincipal(results);
594         }
595         catch (WSSecurityException ex) {
596             throw new Wss4jSecurityValidationException(ex.getMessage(), ex);
597         }
598 
599         soapMessage.setDocument(envelopeAsDocument);
600 
601         soapMessage.getEnvelope().getHeader().removeHeaderElement(WS_SECURITY_NAME);
602     }
603 
604     /**
605      * Checks whether the received headers match the configured validation actions. Subclasses could override this method
606      * for custom verification behavior.
607      *
608      *
609      * @param results the results of the validation function
610      * @param validationActions the decoded validation actions
611      * @throws Wss4jSecurityValidationException if the results are deemed invalid
612      */
613     protected void checkResults(List<WSSecurityEngineResult> results, List<Integer> validationActions)
614             throws Wss4jSecurityValidationException {
615         if (!handler.checkReceiverResultsAnyOrder(results, validationActions)) {
616             throw new Wss4jSecurityValidationException("Security processing failed (actions mismatch)");
617         }
618     }
619 
620     /**
621      * Puts the results of WS-Security headers processing in the message context. Some actions like Signature
622      * Confirmation require this.
623      */
624     @SuppressWarnings("unchecked")
625     private void updateContextWithResults(MessageContext messageContext, List<WSSecurityEngineResult> results) {
626         List<WSHandlerResult> handlerResults;
627         if ((handlerResults = (List<WSHandlerResult>) messageContext.getProperty(WSHandlerConstants.RECV_RESULTS)) == null) {
628             handlerResults = new ArrayList<WSHandlerResult>();
629             messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
630         }
631         WSHandlerResult rResult = new WSHandlerResult(validationActor, results);
632         handlerResults.add(0, rResult);
633         messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
634     }
635 
636     /** Verifies the trust of a certificate. */
637     protected void verifyCertificateTrust(List<WSSecurityEngineResult> results) throws WSSecurityException {
638         WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
639 
640         if (actionResult != null) {
641             X509Certificate returnCert =
642                     (X509Certificate) actionResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
643             Credential credential = new Credential();
644             credential.setCertificates(new X509Certificate[] { returnCert});
645 
646             RequestData requestData = new RequestData();
647             requestData.setSigCrypto(validationSignatureCrypto);
648             requestData.setEnableRevocation(enableRevocation);
649 
650             SignatureTrustValidator validator = new SignatureTrustValidator();
651             validator.validate(credential, requestData);
652         }
653     }
654 
655     /** Verifies the timestamp. */
656     protected void verifyTimestamp(List<WSSecurityEngineResult> results) throws WSSecurityException {
657         WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.TS);
658 
659         if (actionResult != null) {
660             Timestamp timestamp = (Timestamp) actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP);
661             if (timestamp != null && timestampStrict) {
662                 Credential credential = new Credential();
663                 credential.setTimestamp(timestamp);
664 
665                 RequestData requestData = new RequestData();
666                 WSSConfig config = new WSSConfig();
667                 config.setTimeStampTTL(validationTimeToLive);
668                 config.setTimeStampStrict(timestampStrict);
669                 requestData.setWssConfig(config);
670 
671                 TimestampValidator validator = new TimestampValidator();
672                 validator.validate(credential, requestData);
673             }
674         }
675     }
676 
677     private void processPrincipal(List<WSSecurityEngineResult> results) {
678         WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.UT);
679 
680         if (actionResult != null) {
681             Principal principal = (Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
682             if (principal != null && principal instanceof WSUsernameTokenPrincipal) {
683                 WSUsernameTokenPrincipal usernameTokenPrincipal = (WSUsernameTokenPrincipal) principal;
684                 UsernameTokenPrincipalCallback callback = new UsernameTokenPrincipalCallback(usernameTokenPrincipal);
685                 try {
686                     validationCallbackHandler.handle(new Callback[]{callback});
687                 }
688                 catch (IOException ex) {
689                     logger.warn("Principal callback resulted in IOException", ex);
690                 }
691                 catch (UnsupportedCallbackException ex) {
692                     // ignore
693                 }
694             }
695         }
696     }
697 
698     @Override
699     protected void cleanUp() {
700         if (validationCallbackHandler != null) {
701             try {
702                 CleanupCallback cleanupCallback = new CleanupCallback();
703                 validationCallbackHandler.handle(new Callback[]{cleanupCallback});
704             }
705             catch (IOException ex) {
706                 logger.warn("Cleanup callback resulted in IOException", ex);
707             }
708             catch (UnsupportedCallbackException ex) {
709                 // ignore
710             }
711         }
712     }
713 }