| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.wss4j.dom.engine; |
| |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.security.auth.callback.CallbackHandler; |
| import javax.xml.namespace.QName; |
| |
| import org.apache.wss4j.common.bsp.BSPRule; |
| import org.apache.wss4j.common.crypto.Crypto; |
| import org.apache.wss4j.common.ext.WSSecurityException; |
| import org.apache.wss4j.dom.WSConstants; |
| import org.apache.wss4j.dom.WSDocInfo; |
| import org.apache.wss4j.dom.callback.CallbackLookup; |
| import org.apache.wss4j.dom.callback.DOMCallbackLookup; |
| import org.apache.wss4j.dom.handler.RequestData; |
| import org.apache.wss4j.dom.handler.WSHandlerResult; |
| import org.apache.wss4j.dom.processor.Processor; |
| import org.apache.wss4j.dom.saml.DOMSAMLUtil; |
| import org.apache.wss4j.dom.util.WSSecurityUtil; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| /** |
| * WS-Security Engine. |
| */ |
| public class WSSecurityEngine { |
| private static final org.slf4j.Logger LOG = |
| org.slf4j.LoggerFactory.getLogger(WSSecurityEngine.class); |
| |
| /** |
| * The WSSConfig instance used by this SecurityEngine to |
| * find Processors for processing security headers |
| */ |
| private WSSConfig wssConfig; |
| private boolean doDebug; |
| private CallbackLookup callbackLookup; |
| |
| /** |
| * @return the WSSConfig object set on this instance |
| */ |
| public final WSSConfig |
| getWssConfig() { |
| if (wssConfig == null) { |
| wssConfig = WSSConfig.getNewInstance(); |
| } |
| return wssConfig; |
| } |
| |
| /** |
| * @param cfg the WSSConfig instance for this WSSecurityEngine to use |
| * |
| * @return the WSSConfig instance previously set on this |
| * WSSecurityEngine instance |
| */ |
| public final WSSConfig |
| setWssConfig(WSSConfig cfg) { |
| WSSConfig ret = wssConfig; |
| wssConfig = cfg; |
| return ret; |
| } |
| |
| /** |
| * Set the CallbackLookup object to use to locate elements |
| * @param callbackLookup the CallbackLookup object to use to locate elements |
| */ |
| public void setCallbackLookup(CallbackLookup callbackLookup) { |
| this.callbackLookup = callbackLookup; |
| } |
| |
| /** |
| * Get the CallbackLookup object to use to locate elements |
| * @return the CallbackLookup object to use to locate elements |
| */ |
| public CallbackLookup getCallbackLookup() { |
| return callbackLookup; |
| } |
| |
| /** |
| * Process the security header given the soap envelope as W3C document. |
| * <p/> |
| * This is the main entry point to verify or decrypt a SOAP envelope. |
| * First check if a <code>wsse:Security</code> is available with the |
| * defined actor. |
| * |
| * @param doc the SOAP envelope as {@link Document} |
| * @param actor the engine works on behalf of this <code>actor</code>. Refer |
| * to the SOAP specification about <code>actor</code> or <code>role |
| * </code> |
| * @param cb a callback hander to the caller to resolve passwords during |
| * encryption and UsernameToken handling |
| * @param crypto the object that implements the access to the keystore and the |
| * handling of certificates. |
| * @return a WSHandlerResult Object containing the results of processing the security header |
| * @throws WSSecurityException |
| * @see WSSecurityEngine#processSecurityHeader(Element securityHeader, CallbackHandler cb, |
| * Crypto sigVerCrypto, Crypto decCrypto) |
| */ |
| public WSHandlerResult processSecurityHeader( |
| Document doc, |
| String actor, |
| CallbackHandler cb, |
| Crypto crypto |
| ) throws WSSecurityException { |
| return processSecurityHeader(doc, actor, cb, crypto, crypto); |
| } |
| |
| /** |
| * Process the security header given the soap envelope as W3C document. |
| * <p/> |
| * This is the main entry point to verify or decrypt a SOAP envelope. |
| * First check if a <code>wsse:Security</code> is available with the |
| * defined actor. |
| * |
| * @param doc the SOAP envelope as {@link Document} |
| * @param actor the engine works on behalf of this <code>actor</code>. Refer |
| * to the SOAP specification about <code>actor</code> or <code>role |
| * </code> |
| * @param cb a callback hander to the caller to resolve passwords during |
| * encryption and UsernameToken handling |
| * @param sigVerCrypto the object that implements the access to the keystore and the |
| * handling of certificates for Signature verification |
| * @param decCrypto the object that implements the access to the keystore and the |
| * handling of certificates for Decryption |
| * @return a WSHandlerResult Object containing the results of processing the security header |
| * @throws WSSecurityException |
| * @see WSSecurityEngine#processSecurityHeader( |
| * Element securityHeader, CallbackHandler cb, Crypto sigVerCrypto, Crypto decCrypto) |
| */ |
| public WSHandlerResult processSecurityHeader( |
| Document doc, |
| String actor, |
| CallbackHandler cb, |
| Crypto sigVerCrypto, |
| Crypto decCrypto |
| ) throws WSSecurityException { |
| LOG.debug("enter processSecurityHeader()"); |
| |
| if (actor == null) { |
| actor = ""; |
| } |
| WSHandlerResult wsResult = null; |
| Element elem = WSSecurityUtil.getSecurityHeader(doc, actor); |
| if (elem != null) { |
| LOG.debug("Processing WS-Security header for '{}' actor.", actor); |
| wsResult = processSecurityHeader(elem, actor, cb, sigVerCrypto, decCrypto); |
| } |
| return wsResult; |
| } |
| |
| /** |
| * Process the security header given the <code>wsse:Security</code> DOM |
| * Element. |
| * |
| * This function loops over all direct child elements of the |
| * <code>wsse:Security</code> header. If it finds a known element, it |
| * transfers control to the appropriate handling function. The method |
| * processes the known child elements in the same order as they appear in |
| * the <code>wsse:Security</code> element. This is in accordance to the WS |
| * Security specification. <p/> |
| * |
| * Currently the functions can handle the following child elements: |
| * |
| * <ul> |
| * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li> |
| * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li> |
| * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li> |
| * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li> |
| * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li> |
| * </ul> |
| * |
| * Note that additional child elements can be processed if appropriate |
| * Processors have been registered with the WSSCondig instance set |
| * on this class. |
| * |
| * @param securityHeader the <code>wsse:Security</code> header element |
| * @param cb a callback hander to the caller to resolve passwords during |
| * encryption and UsernameToken handling |
| * @param sigVerCrypto the object that implements the access to the keystore and the |
| * handling of certificates used for Signature verification |
| * @param decCrypto the object that implements the access to the keystore and the |
| * handling of certificates used for Decryption |
| * @return a WSHandlerResult Object containing the results of processing the security header |
| * @throws WSSecurityException |
| */ |
| public WSHandlerResult processSecurityHeader( |
| Element securityHeader, |
| String actor, |
| CallbackHandler cb, |
| Crypto sigVerCrypto, |
| Crypto decCrypto |
| ) throws WSSecurityException { |
| RequestData data = new RequestData(); |
| data.setActor(actor); |
| data.setWssConfig(getWssConfig()); |
| data.setDecCrypto(decCrypto); |
| data.setSigVerCrypto(sigVerCrypto); |
| data.setCallbackHandler(cb); |
| return processSecurityHeader(securityHeader, data); |
| } |
| |
| /** |
| * Process the security header given the soap envelope as W3C document. |
| * <p/> |
| * This is the main entry point to verify or decrypt a SOAP envelope. |
| * First check if a <code>wsse:Security</code> is available with the |
| * defined actor. |
| * |
| * @param doc the SOAP envelope as {@link Document} |
| * @param requestData the RequestData associated with the request. It should |
| * be able to provide the callback handler, cryptos, etc... |
| * as needed by the processing |
| * @return a WSHandlerResult Object containing the results of processing the security header |
| * @throws WSSecurityException |
| */ |
| public WSHandlerResult processSecurityHeader( |
| Document doc, RequestData requestData |
| ) throws WSSecurityException { |
| if (requestData.getActor() == null) { |
| requestData.setActor(""); |
| } |
| String actor = requestData.getActor(); |
| WSHandlerResult wsResult = null; |
| Element elem = WSSecurityUtil.getSecurityHeader(doc, actor); |
| if (elem != null) { |
| if (doDebug) { |
| LOG.debug("Processing WS-Security header for '" + actor + "' actor."); |
| } |
| wsResult = processSecurityHeader(elem, requestData); |
| } |
| return wsResult; |
| } |
| |
| /** |
| * Process the security header given the <code>wsse:Security</code> DOM |
| * Element. |
| * |
| * This function loops over all direct child elements of the |
| * <code>wsse:Security</code> header. If it finds a known element, it |
| * transfers control to the appropriate handling function. The method |
| * processes the known child elements in the same order as they appear in |
| * the <code>wsse:Security</code> element. This is in accordance to the WS |
| * Security specification. <p/> |
| * |
| * Currently the functions can handle the following child elements: |
| * |
| * <ul> |
| * <li>{@link #SIGNATURE <code>ds:Signature</code>}</li> |
| * <li>{@link #ENCRYPTED_KEY <code>xenc:EncryptedKey</code>}</li> |
| * <li>{@link #REFERENCE_LIST <code>xenc:ReferenceList</code>}</li> |
| * <li>{@link #USERNAME_TOKEN <code>wsse:UsernameToken</code>}</li> |
| * <li>{@link #TIMESTAMP <code>wsu:Timestamp</code>}</li> |
| * </ul> |
| * |
| * Note that additional child elements can be processed if appropriate |
| * Processors have been registered with the WSSCondig instance set |
| * on this class. |
| * |
| * @param securityHeader the <code>wsse:Security</code> header element |
| * @param requestData the RequestData associated with the request. It should |
| * be able to provide the callback handler, cryptos, etc... |
| * as needed by the processing |
| * @return a WSHandlerResult Object containing the results of processing the security header |
| * @throws WSSecurityException |
| */ |
| public WSHandlerResult processSecurityHeader( |
| Element securityHeader, |
| RequestData requestData |
| ) throws WSSecurityException { |
| if (securityHeader == null) { |
| List<WSSecurityEngineResult> results = Collections.emptyList(); |
| Map<Integer, List<WSSecurityEngineResult>> actionResults = Collections.emptyMap(); |
| return new WSHandlerResult(null, results, actionResults); |
| } |
| |
| if (requestData.getWssConfig() == null) { |
| requestData.setWssConfig(getWssConfig()); |
| } |
| |
| // |
| // Gather some info about the document to process and store |
| // it for retrieval. Store the implementation of signature crypto |
| // (no need for encryption --- yet) |
| // |
| WSDocInfo wsDocInfo = new WSDocInfo(securityHeader.getOwnerDocument()); |
| CallbackLookup callbackLookupToUse = callbackLookup; |
| if (callbackLookupToUse == null) { |
| callbackLookupToUse = new DOMCallbackLookup(securityHeader.getOwnerDocument()); |
| } |
| wsDocInfo.setCallbackLookup(callbackLookupToUse); |
| wsDocInfo.setCrypto(requestData.getSigVerCrypto()); |
| wsDocInfo.setSecurityHeader(securityHeader); |
| requestData.setWsDocInfo(wsDocInfo); |
| |
| final WSSConfig cfg = getWssConfig(); |
| Node node = securityHeader.getFirstChild(); |
| |
| List<WSSecurityEngineResult> returnResults = new LinkedList<>(); |
| boolean foundTimestamp = false; |
| while (node != null) { |
| Node nextSibling = node.getNextSibling(); |
| if (Node.ELEMENT_NODE == node.getNodeType()) { |
| QName el = new QName(node.getNamespaceURI(), node.getLocalName()); |
| |
| // Check for multiple timestamps |
| if (foundTimestamp && el.equals(WSConstants.TIMESTAMP)) { |
| requestData.getBSPEnforcer().handleBSPRule(BSPRule.R3227); |
| } else if (el.equals(WSConstants.TIMESTAMP)) { |
| foundTimestamp = true; |
| } |
| // |
| // Call the processor for this token. After the processor returns, |
| // store it for later retrieval. The token processor may store some |
| // information about the processed token |
| // |
| Processor p = cfg.getProcessor(el); |
| if (p != null) { |
| List<WSSecurityEngineResult> results = p.handleToken((Element) node, requestData); |
| if (!results.isEmpty()) { |
| returnResults.addAll(0, results); |
| } |
| } else { |
| if (doDebug) { |
| LOG.debug( |
| "Unknown Element: " + node.getLocalName() + " " + node.getNamespaceURI() |
| ); |
| } |
| } |
| } |
| // |
| // If the next sibling is null and the stored next sibling is not null, then we have |
| // encountered an EncryptedData element which was decrypted, and so the next sibling |
| // of the current node is null. In that case, go on to the previously stored next |
| // sibling |
| // |
| if (node.getNextSibling() == null && nextSibling != null |
| && nextSibling.getParentNode() != null) { |
| node = nextSibling; |
| } else { |
| node = node.getNextSibling(); |
| } |
| } |
| |
| WSHandlerResult handlerResult = |
| new WSHandlerResult(requestData.getActor(), returnResults, wsDocInfo.getActionResults()); |
| |
| // Validate SAML Subject Confirmation requirements |
| if (requestData.isValidateSamlSubjectConfirmation()) { |
| Element bodyElement = callbackLookupToUse.getSOAPBody(); |
| DOMSAMLUtil.validateSAMLResults(handlerResult, requestData.getTlsCerts(), bodyElement); |
| } |
| |
| wsDocInfo.clear(); |
| |
| return handlerResult; |
| } |
| } |