/**
* 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.cxf.ws.security.wss4j.policyhandlers;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.callback.CallbackHandler;
import javax.xml.crypto.dsig.Reference;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.cxf.attachment.AttachmentUtil;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.saaj.SAAJUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.helpers.MapNamespaceContext;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.rt.security.utils.SecurityUtils;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.cxf.staxutils.W3CDOMStreamWriter;
import org.apache.cxf.ws.policy.AssertionInfo;
import org.apache.cxf.ws.policy.AssertionInfoMap;
import org.apache.cxf.ws.policy.PolicyConstants;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.policy.PolicyUtils;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.tokenstore.TokenStore;
import org.apache.cxf.ws.security.tokenstore.TokenStoreUtils;
import org.apache.cxf.ws.security.wss4j.AttachmentCallbackHandler;
import org.apache.cxf.ws.security.wss4j.CXFCallbackLookup;
import org.apache.cxf.ws.security.wss4j.WSS4JUtils;
import org.apache.cxf.wsdl.WSDLConstants;
import org.apache.neethi.Assertion;
import org.apache.wss4j.common.WSEncryptionPart;
import org.apache.wss4j.common.bsp.BSPEnforcer;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoFactory;
import org.apache.wss4j.common.crypto.CryptoType;
import org.apache.wss4j.common.derivedKey.ConversationConstants;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.principal.UsernameTokenPrincipal;
import org.apache.wss4j.common.saml.SAMLCallback;
import org.apache.wss4j.common.saml.SAMLUtil;
import org.apache.wss4j.common.saml.SamlAssertionWrapper;
import org.apache.wss4j.common.saml.bean.Version;
import org.apache.wss4j.common.token.BinarySecurity;
import org.apache.wss4j.common.token.SecurityTokenReference;
import org.apache.wss4j.common.token.X509Security;
import org.apache.wss4j.common.util.Loader;
import org.apache.wss4j.common.util.XMLUtils;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.WSDocInfo;
import org.apache.wss4j.dom.callback.CallbackLookup;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.apache.wss4j.dom.handler.WSHandlerResult;
import org.apache.wss4j.dom.message.WSSecBase;
import org.apache.wss4j.dom.message.WSSecDKSign;
import org.apache.wss4j.dom.message.WSSecEncryptedKey;
import org.apache.wss4j.dom.message.WSSecHeader;
import org.apache.wss4j.dom.message.WSSecSignature;
import org.apache.wss4j.dom.message.WSSecSignatureConfirmation;
import org.apache.wss4j.dom.message.WSSecTimestamp;
import org.apache.wss4j.dom.message.WSSecUsernameToken;
import org.apache.wss4j.dom.util.WSSecurityUtil;
import org.apache.wss4j.policy.SPConstants;
import org.apache.wss4j.policy.SPConstants.IncludeTokenType;
import org.apache.wss4j.policy.model.AbstractBinding;
import org.apache.wss4j.policy.model.AbstractSecurityAssertion;
import org.apache.wss4j.policy.model.AbstractSymmetricAsymmetricBinding;
import org.apache.wss4j.policy.model.AbstractSymmetricAsymmetricBinding.ProtectionOrder;
import org.apache.wss4j.policy.model.AbstractToken;
import org.apache.wss4j.policy.model.AbstractToken.DerivedKeys;
import org.apache.wss4j.policy.model.AlgorithmSuite.AlgorithmSuiteType;
import org.apache.wss4j.policy.model.AsymmetricBinding;
import org.apache.wss4j.policy.model.Attachments;
import org.apache.wss4j.policy.model.ContentEncryptedElements;
import org.apache.wss4j.policy.model.EncryptedElements;
import org.apache.wss4j.policy.model.EncryptedParts;
import org.apache.wss4j.policy.model.Header;
import org.apache.wss4j.policy.model.IssuedToken;
import org.apache.wss4j.policy.model.KerberosToken;
import org.apache.wss4j.policy.model.KeyValueToken;
import org.apache.wss4j.policy.model.Layout.LayoutType;
import org.apache.wss4j.policy.model.SamlToken;
import org.apache.wss4j.policy.model.SamlToken.SamlTokenType;
import org.apache.wss4j.policy.model.SecureConversationToken;
import org.apache.wss4j.policy.model.SecurityContextToken;
import org.apache.wss4j.policy.model.SignedElements;
import org.apache.wss4j.policy.model.SignedParts;
import org.apache.wss4j.policy.model.SpnegoContextToken;
import org.apache.wss4j.policy.model.SupportingTokens;
import org.apache.wss4j.policy.model.SymmetricBinding;
import org.apache.wss4j.policy.model.UsernameToken;
import org.apache.wss4j.policy.model.Wss10;
import org.apache.wss4j.policy.model.Wss11;
import org.apache.wss4j.policy.model.X509Token;
import org.apache.wss4j.policy.model.X509Token.TokenType;
/**
*
*/
public abstract class AbstractBindingBuilder extends AbstractCommonBindingHandler {
public static final String CRYPTO_CACHE = "ws-security.crypto.cache";
protected static final Logger LOG = LogUtils.getL7dLogger(AbstractBindingBuilder.class);
protected AbstractSymmetricAsymmetricBinding.ProtectionOrder protectionOrder =
AbstractSymmetricAsymmetricBinding.ProtectionOrder.SignBeforeEncrypting;
protected final WSSConfig wssConfig;
protected SOAPMessage saaj;
protected WSSecHeader secHeader;
protected AssertionInfoMap aim;
protected AbstractBinding binding;
protected WSSecTimestamp timestampEl;
protected String mainSigId;
protected List<WSEncryptionPart> sigConfList;
protected Set<WSEncryptionPart> encryptedTokensList = new HashSet<>();
protected Set<Integer> signatures = new HashSet<>();
protected Element bottomUpElement;
protected Element topDownElement;
protected Element bstElement;
protected Element lastEncryptedKeyElement;
protected final CallbackLookup callbackLookup;
protected boolean storeBytesInAttachment;
protected WSDocInfo wsDocInfo;
private boolean expandXopInclude;
private Element lastSupportingTokenElement;
private Element lastDerivedKeyElement;
private List<AbstractSecurityAssertion> suppTokenParts = new ArrayList<>();
private List<SupportingToken> endSuppTokList;
private List<SupportingToken> sgndEndSuppTokList;
public AbstractBindingBuilder(
WSSConfig config,
AbstractBinding binding,
SOAPMessage saaj,
WSSecHeader secHeader,
AssertionInfoMap aim,
SoapMessage message) throws SOAPException {
super(message);
this.wssConfig = config;
this.binding = binding;
this.aim = aim;
this.secHeader = secHeader;
this.saaj = saaj;
message.getExchange().put(WSHandlerConstants.SEND_SIGV, signatures);
boolean storeBytes =
MessageUtils.getContextualBoolean(
message, SecurityConstants.STORE_BYTES_IN_ATTACHMENT, true
);
boolean mtomEnabled = AttachmentUtil.isMtomEnabled(message);
if (storeBytes && mtomEnabled) {
storeBytesInAttachment = true;
if (binding instanceof AbstractSymmetricAsymmetricBinding
&& (ProtectionOrder.EncryptBeforeSigning
== ((AbstractSymmetricAsymmetricBinding)binding).getProtectionOrder()
|| ((AbstractSymmetricAsymmetricBinding)binding).isProtectTokens())) {
LOG.fine("Disabling SecurityConstants.STORE_BYTES_IN_ATTACHMENT due to "
+ "EncryptBeforeSigning or ProtectTokens policy.");
storeBytesInAttachment = false;
}
}
expandXopInclude = mtomEnabled;
wsDocInfo = new WSDocInfo(secHeader.getSecurityHeaderElement().getOwnerDocument());
Element soapBody = SAAJUtils.getBody(saaj);
if (soapBody != null) {
callbackLookup = new CXFCallbackLookup(soapBody.getOwnerDocument(), soapBody);
} else {
callbackLookup = null;
}
}
protected void insertAfter(Element child, Element sib) {
child = (Element)DOMUtils.getDomElement(child);
if (sib.getNextSibling() == null) {
secHeader.getSecurityHeaderElement().appendChild(child);
} else {
secHeader.getSecurityHeaderElement().insertBefore(child, sib.getNextSibling());
}
}
protected void addDerivedKeyElement(Element el) {
if (lastDerivedKeyElement != null) {
insertAfter(el, lastDerivedKeyElement);
} else if (lastEncryptedKeyElement != null) {
insertAfter(el, lastEncryptedKeyElement);
} else if (topDownElement != null) {
insertAfter(el, topDownElement);
} else if (secHeader.getSecurityHeaderElement().getFirstChild() != null) {
secHeader.getSecurityHeaderElement().insertBefore(
el, secHeader.getSecurityHeaderElement().getFirstChild()
);
} else {
secHeader.getSecurityHeaderElement().appendChild(el);
}
lastEncryptedKeyElement = el;
}
protected void addEncryptedKeyElement(Element el) {
if (lastEncryptedKeyElement != null) {
insertAfter(el, lastEncryptedKeyElement);
} else if (lastDerivedKeyElement != null) {
secHeader.getSecurityHeaderElement().insertBefore(el, lastDerivedKeyElement);
} else if (topDownElement != null) {
insertAfter(el, topDownElement);
} else if (secHeader.getSecurityHeaderElement().getFirstChild() != null) {
secHeader.getSecurityHeaderElement().insertBefore(
el, secHeader.getSecurityHeaderElement().getFirstChild()
);
} else {
secHeader.getSecurityHeaderElement().appendChild(el);
}
lastEncryptedKeyElement = el;
}
protected void addSupportingElement(Element el) {
el = (Element)DOMUtils.getDomElement(el);
if (lastSupportingTokenElement != null) {
insertAfter(el, lastSupportingTokenElement);
} else if (lastDerivedKeyElement != null) {
insertAfter(el, lastDerivedKeyElement);
} else if (lastEncryptedKeyElement != null) {
insertAfter(el, lastEncryptedKeyElement);
} else if (topDownElement != null) {
insertAfter(el, topDownElement);
} else if (bottomUpElement != null) {
secHeader.getSecurityHeaderElement().insertBefore(el, bottomUpElement);
} else {
secHeader.getSecurityHeaderElement().appendChild(el);
}
lastSupportingTokenElement = el;
}
protected void insertBeforeBottomUp(Element el) {
if (bottomUpElement == null) {
secHeader.getSecurityHeaderElement().appendChild(el);
} else {
secHeader.getSecurityHeaderElement().insertBefore(el, bottomUpElement);
}
bottomUpElement = el;
}
protected void addTopDownElement(Element el) {
if (topDownElement == null) {
if (secHeader.getSecurityHeaderElement().getFirstChild() == null) {
secHeader.getSecurityHeaderElement().appendChild(el);
} else {
secHeader.getSecurityHeaderElement().insertBefore(
el, secHeader.getSecurityHeaderElement().getFirstChild()
);
}
} else {
insertAfter(el, topDownElement);
}
topDownElement = el;
}
protected final Map<Object, Crypto> getCryptoCache() {
EndpointInfo info = message.getExchange().getEndpoint().getEndpointInfo();
synchronized (info) {
Map<Object, Crypto> o =
CastUtils.cast((Map<?, ?>)message.getContextualProperty(CRYPTO_CACHE));
if (o == null) {
o = CastUtils.cast((Map<?, ?>)info.getProperty(CRYPTO_CACHE));
}
if (o == null) {
o = new ConcurrentHashMap<>();
info.setProperty(CRYPTO_CACHE, o);
}
return o;
}
}
protected final TokenStore getTokenStore() {
return TokenStoreUtils.getTokenStore(message);
}
protected WSSecTimestamp createTimestamp() {
if (binding.isIncludeTimestamp()) {
Object o = message.getContextualProperty(SecurityConstants.TIMESTAMP_TTL);
int ttl = 300; //default is 300 seconds
if (o instanceof Number) {
ttl = ((Number)o).intValue();
} else if (o instanceof String) {
ttl = Integer.parseInt((String)o);
}
if (ttl <= 0) {
ttl = 300;
}
timestampEl = new WSSecTimestamp(secHeader);
timestampEl.setIdAllocator(wssConfig.getIdAllocator());
timestampEl.setWsTimeSource(wssConfig.getCurrentTime());
timestampEl.setTimeToLive(ttl);
timestampEl.prepare();
String namespace = binding.getName().getNamespaceURI();
PolicyUtils.assertPolicy(aim, new QName(namespace, SPConstants.INCLUDE_TIMESTAMP));
}
return timestampEl;
}
protected WSSecTimestamp handleLayout(WSSecTimestamp timestamp) {
if (binding.getLayout() != null) {
AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.LAYOUT);
if (binding.getLayout().getLayoutType() == LayoutType.LaxTsLast) {
if (timestamp == null) {
ai.setNotAsserted(SPConstants.LAYOUT_LAX_TIMESTAMP_LAST + " requires a timestamp");
} else {
ai.setAsserted(true);
assertPolicy(
new QName(binding.getLayout().getName().getNamespaceURI(),
SPConstants.LAYOUT_LAX_TIMESTAMP_LAST));
Element el = timestamp.getElement();
secHeader.getSecurityHeaderElement().appendChild(el);
if (bottomUpElement == null) {
bottomUpElement = el;
}
}
} else if (binding.getLayout().getLayoutType() == LayoutType.LaxTsFirst) {
if (timestamp == null) {
ai.setNotAsserted(SPConstants.LAYOUT_LAX_TIMESTAMP_FIRST + " requires a timestamp");
} else {
addTopDownElement(timestampEl.getElement());
ai.setAsserted(true);
assertPolicy(
new QName(binding.getLayout().getName().getNamespaceURI(),
SPConstants.LAYOUT_LAX_TIMESTAMP_FIRST));
}
} else if (timestampEl != null) {
if (ai != null) {
ai.setAsserted(true);
}
addTopDownElement(timestampEl.getElement());
} else if (ai != null) {
ai.setAsserted(true);
}
assertPolicy(
new QName(binding.getLayout().getName().getNamespaceURI(), SPConstants.LAYOUT_LAX));
assertPolicy(
new QName(binding.getLayout().getName().getNamespaceURI(), SPConstants.LAYOUT_STRICT));
} else if (timestampEl != null) {
addTopDownElement(timestampEl.getElement());
}
return timestamp;
}
protected void reshuffleTimestamp() {
// Make sure that the Timestamp is in first place, if that is what the policy requires
if (binding.getLayout() != null && timestampEl != null) {
if (binding.getLayout().getLayoutType() == LayoutType.LaxTsFirst
&& secHeader.getSecurityHeaderElement().getFirstChild() != timestampEl.getElement()) {
Node firstChild = secHeader.getSecurityHeaderElement().getFirstChild();
while (firstChild != null && firstChild.getNodeType() != Node.ELEMENT_NODE) {
firstChild = firstChild.getNextSibling();
}
if (firstChild != null && firstChild != timestampEl.getElement()) {
secHeader.getSecurityHeaderElement().insertBefore(timestampEl.getElement(), firstChild);
}
} else if (binding.getLayout().getLayoutType() == LayoutType.LaxTsLast
&& secHeader.getSecurityHeaderElement().getLastChild() != timestampEl.getElement()) {
secHeader.getSecurityHeaderElement().appendChild(timestampEl.getElement());
}
}
}
private List<SupportingToken> handleSupportingTokens(
Collection<AssertionInfo> tokensInfos,
boolean endorse
) throws WSSecurityException {
List<SupportingToken> ret = new ArrayList<>();
if (tokensInfos != null) {
for (AssertionInfo assertionInfo : tokensInfos) {
if (assertionInfo.getAssertion() instanceof SupportingTokens) {
assertionInfo.setAsserted(true);
try {
handleSupportingTokens((SupportingTokens)assertionInfo.getAssertion(), endorse, ret);
} catch (SOAPException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
}
}
}
}
return ret;
}
protected List<SupportingToken> handleSupportingTokens(
SupportingTokens suppTokens,
boolean endorse,
List<SupportingToken> ret
) throws WSSecurityException, SOAPException {
if (suppTokens == null) {
return ret;
}
for (AbstractToken token : suppTokens.getTokens()) {
assertToken(token);
if (!isTokenRequired(token.getIncludeTokenType())) {
// Check for any SignedParts so as *not* to sign them
getSignedParts(suppTokens);
continue;
}
if (token instanceof UsernameToken) {
handleUsernameTokenSupportingToken(
(UsernameToken)token, endorse, suppTokens.isEncryptedToken(), ret
);
} else if (token instanceof IssuedToken
|| token instanceof SecureConversationToken
|| token instanceof SecurityContextToken
|| token instanceof KerberosToken
|| token instanceof SpnegoContextToken) {
//ws-trust/ws-sc stuff.......
SecurityToken secToken = getSecurityToken();
if (secToken == null) {
unassertPolicy(token, "Could not find IssuedToken");
}
Element clone = cloneElement(secToken.getToken());
secToken.setToken(clone);
addSupportingElement(clone);
String id = XMLUtils.getIDFromReference(secToken.getId());
if (suppTokens.isEncryptedToken()) {
WSEncryptionPart part = new WSEncryptionPart(id, "Element");
part.setElement(clone);
encryptedTokensList.add(part);
}
if (secToken.getX509Certificate() == null) {
ret.add(
new SupportingToken(token, new WSSecurityTokenHolder(secToken, secHeader),
getSignedParts(suppTokens))
);
} else {
ret.add(signSupportingToken(secToken, id, token, suppTokens));
}
} else if (token instanceof X509Token) {
//We have to use a cert. Prepare X509 signature
WSSecSignature sig = getSignatureBuilder(token, false, endorse);
assertPolicy(suppTokens);
Element bstElem = sig.getBinarySecurityTokenElement();
if (bstElem != null) {
if (lastEncryptedKeyElement != null) {
if (lastEncryptedKeyElement.getNextSibling() != null) {
secHeader.getSecurityHeaderElement().insertBefore(bstElem,
lastEncryptedKeyElement.getNextSibling());
} else {
secHeader.getSecurityHeaderElement().appendChild(bstElem);
}
} else {
sig.prependBSTElementToHeader();
}
if (suppTokens.isEncryptedToken()) {
WSEncryptionPart part = new WSEncryptionPart(sig.getBSTTokenId(), "Element");
part.setElement(bstElem);
encryptedTokensList.add(part);
}
}
ret.add(new SupportingToken(token, sig, getSignedParts(suppTokens)));
} else if (token instanceof KeyValueToken) {
WSSecSignature sig = getSignatureBuilder(token, false, endorse);
assertPolicy(suppTokens);
if (suppTokens.isEncryptedToken()) {
WSEncryptionPart part = new WSEncryptionPart(sig.getBSTTokenId(), "Element");
encryptedTokensList.add(part);
}
ret.add(new SupportingToken(token, sig, getSignedParts(suppTokens)));
} else if (token instanceof SamlToken) {
SamlAssertionWrapper assertionWrapper = addSamlToken((SamlToken)token);
if (assertionWrapper != null) {
Element envelope = saaj.getSOAPPart().getEnvelope();
envelope = (Element)DOMUtils.getDomElement(envelope);
Element assertionElement = assertionWrapper.toDOM(envelope.getOwnerDocument());
addSupportingElement(assertionElement);
ret.add(new SupportingToken(token, assertionWrapper, getSignedParts(suppTokens)));
if (suppTokens.isEncryptedToken()) {
WSEncryptionPart part = new WSEncryptionPart(assertionWrapper.getId(), "Element");
part.setElement(assertionElement);
encryptedTokensList.add(part);
}
}
}
}
return ret;
}
private SupportingToken signSupportingToken(SecurityToken secToken, String id,
AbstractToken token, SupportingTokens suppTokens)
throws SOAPException {
WSSecSignature sig = new WSSecSignature(secHeader);
sig.setIdAllocator(wssConfig.getIdAllocator());
sig.setCallbackLookup(callbackLookup);
sig.setX509Certificate(secToken.getX509Certificate());
sig.setCustomTokenId(id);
sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER);
sig.setWsDocInfo(wsDocInfo);
sig.setExpandXopInclude(isExpandXopInclude());
sig.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message));
sig.setStoreBytesInAttachment(storeBytesInAttachment);
String tokenType = secToken.getTokenType();
if (WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
} else if (WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML2_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML2_KI_VALUE_TYPE);
} else if (tokenType != null) {
sig.setCustomTokenValueType(tokenType);
} else {
sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
}
sig.setSignatureAlgorithm(binding.getAlgorithmSuite().getAsymmetricSignature());
sig.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue());
Crypto crypto = secToken.getCrypto();
String uname = null;
try {
uname = crypto.getX509Identifier(secToken.getX509Certificate());
} catch (WSSecurityException e1) {
LOG.log(Level.FINE, e1.getMessage(), e1);
throw new Fault(e1);
}
String password = getPassword(uname, token, WSPasswordCallback.SIGNATURE);
sig.setUserInfo(uname, password);
try {
sig.prepare(secToken.getCrypto());
} catch (WSSecurityException e) {
LOG.log(Level.FINE, e.getMessage(), e);
throw new Fault(e);
}
return new SupportingToken(token, sig, getSignedParts(suppTokens));
}
protected void handleUsernameTokenSupportingToken(
UsernameToken token, boolean endorse, boolean encryptedToken, List<SupportingToken> ret
) throws WSSecurityException {
if (endorse) {
WSSecUsernameToken utBuilder = addDKUsernameToken(token, true);
if (utBuilder != null) {
utBuilder.prepare();
addSupportingElement(utBuilder.getUsernameTokenElement());
ret.add(new SupportingToken(token, utBuilder, null));
if (encryptedToken) {
WSEncryptionPart part = new WSEncryptionPart(utBuilder.getId(), "Element");
part.setElement(utBuilder.getUsernameTokenElement());
encryptedTokensList.add(part);
}
}
} else {
WSSecUsernameToken utBuilder = addUsernameToken(token);
if (utBuilder != null) {
utBuilder.prepare();
addSupportingElement(utBuilder.getUsernameTokenElement());
ret.add(new SupportingToken(token, utBuilder, null));
//WebLogic and WCF always encrypt these
//See: http://e-docs.bea.com/wls/docs103/webserv_intro/interop.html
//encryptedTokensIdList.add(utBuilder.getId());
if (encryptedToken
|| MessageUtils.getContextualBoolean(message,
SecurityConstants.ALWAYS_ENCRYPT_UT,
true)) {
WSEncryptionPart part = new WSEncryptionPart(utBuilder.getId(), "Element");
part.setElement(utBuilder.getUsernameTokenElement());
encryptedTokensList.add(part);
}
}
}
}
protected Element cloneElement(Element el) {
Document doc = secHeader.getSecurityHeaderElement().getOwnerDocument();
if (!doc.equals(el.getOwnerDocument())) {
XMLStreamReader reader = StaxUtils.createXMLStreamReader(el);
DocumentFragment fragment = doc.createDocumentFragment();
W3CDOMStreamWriter writer = new W3CDOMStreamWriter(fragment);
try {
StaxUtils.copy(reader, writer);
return (Element)fragment.getFirstChild();
} catch (XMLStreamException ex) {
LOG.log(Level.FINE, "Error cloning security element", ex);
}
}
return el;
}
protected void addSignatureParts(List<SupportingToken> tokenList, List<WSEncryptionPart> sigParts) {
boolean useSTRTransform =
MessageUtils.getContextualBoolean(
message, SecurityConstants.USE_STR_TRANSFORM, true
);
for (SupportingToken supportingToken : tokenList) {
Object tempTok = supportingToken.getTokenImplementation();
WSEncryptionPart part = null;
if (tempTok instanceof WSSecSignature) {
WSSecSignature tempSig = (WSSecSignature) tempTok;
SecurityTokenReference secRef = tempSig.getSecurityTokenReference();
if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())
|| WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) {
Element secRefElement = cloneElement(secRef.getElement());
addSupportingElement(secRefElement);
part = new WSEncryptionPart("STRTransform", null, "Element");
part.setId(tempSig.getSecurityTokenReferenceURI());
part.setElement(secRefElement);
} else {
if (tempSig.getBSTTokenId() != null) {
part = new WSEncryptionPart(tempSig.getBSTTokenId());
part.setElement(tempSig.getBinarySecurityTokenElement());
}
}
} else if (tempTok instanceof WSSecUsernameToken) {
WSSecUsernameToken unt = (WSSecUsernameToken)tempTok;
part = new WSEncryptionPart(unt.getId());
part.setElement(unt.getUsernameTokenElement());
} else if (tempTok instanceof BinarySecurity) {
BinarySecurity bst = (BinarySecurity)tempTok;
part = new WSEncryptionPart(bst.getID());
part.setElement(bst.getElement());
} else if (tempTok instanceof SamlAssertionWrapper) {
SamlAssertionWrapper assertionWrapper = (SamlAssertionWrapper)tempTok;
Document doc = assertionWrapper.getElement().getOwnerDocument();
boolean saml1 = assertionWrapper.getSaml1() != null;
if (useSTRTransform) {
// TODO We only support using a KeyIdentifier for the moment
SecurityTokenReference secRef =
createSTRForSamlAssertion(doc, assertionWrapper.getId(), saml1, false);
Element clone = cloneElement(secRef.getElement());
addSupportingElement(clone);
part = new WSEncryptionPart("STRTransform", null, "Element");
part.setId(secRef.getID());
part.setElement(clone);
} else {
part = new WSEncryptionPart(assertionWrapper.getId());
part.setElement(assertionWrapper.getElement());
}
} else if (tempTok instanceof WSSecurityTokenHolder) {
SecurityToken token = ((WSSecurityTokenHolder)tempTok).getToken();
String tokenType = token.getTokenType();
if (WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML_NS.equals(tokenType)
|| WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML2_NS.equals(tokenType)) {
Document doc = token.getToken().getOwnerDocument();
boolean saml1 = WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML_NS.equals(tokenType);
String id = token.getId();
if (id == null || "".equals(id)) {
if (saml1) {
id = token.getToken().getAttributeNS(null, "AssertionID");
} else {
id = token.getToken().getAttributeNS(null, "ID");
}
}
if (useSTRTransform) {
SecurityTokenReference secRef =
createSTRForSamlAssertion(doc, id, saml1, false);
Element clone = cloneElement(secRef.getElement());
addSupportingElement(clone);
part = new WSEncryptionPart("STRTransform", null, "Element");
part.setId(secRef.getID());
part.setElement(clone);
} else {
part = new WSEncryptionPart(id);
part.setElement(token.getToken());
}
} else {
String id = XMLUtils.getIDFromReference(token.getId());
part = new WSEncryptionPart(id);
part.setElement(token.getToken());
}
} else {
unassertPolicy(supportingToken.getToken(),
"UnsupportedTokenInSupportingToken: " + tempTok);
}
if (part != null) {
sigParts.add(part);
}
}
}
/**
* Create a SecurityTokenReference to point to a SAML Assertion
* @param doc The owner Document instance
* @param id The Assertion ID
* @param saml1 Whether the Assertion is a SAML1 or SAML2 Assertion
* @param useDirectReferenceToAssertion whether to refer directly to the assertion or not
* @return a SecurityTokenReference to a SAML Assertion
*/
private SecurityTokenReference createSTRForSamlAssertion(
Document doc,
String id,
boolean saml1,
boolean useDirectReferenceToAssertion
) {
SecurityTokenReference secRefSaml = new SecurityTokenReference(doc);
String secRefID = wssConfig.getIdAllocator().createSecureId("STR-", secRefSaml);
secRefSaml.setID(secRefID);
if (useDirectReferenceToAssertion) {
org.apache.wss4j.common.token.Reference ref =
new org.apache.wss4j.common.token.Reference(doc);
ref.setURI("#" + id);
if (saml1) {
ref.setValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
} else {
secRefSaml.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
}
secRefSaml.setReference(ref);
} else {
Element keyId = doc.createElementNS(WSConstants.WSSE_NS, "wsse:KeyIdentifier");
String valueType = null;
if (saml1) {
valueType = WSConstants.WSS_SAML_KI_VALUE_TYPE;
secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
} else {
valueType = WSConstants.WSS_SAML2_KI_VALUE_TYPE;
secRefSaml.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
}
keyId.setAttributeNS(
null, "ValueType", valueType
);
keyId.appendChild(doc.createTextNode(id));
Element elem = secRefSaml.getElement();
elem.appendChild(keyId);
}
return secRefSaml;
}
protected WSSecUsernameToken addUsernameToken(UsernameToken token) {
assertToken(token);
if (!isTokenRequired(token.getIncludeTokenType())) {
return null;
}
String userName = (String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.USERNAME, message);
if (!StringUtils.isEmpty(userName)) {
WSSecUsernameToken utBuilder = new WSSecUsernameToken(secHeader);
utBuilder.setIdAllocator(wssConfig.getIdAllocator());
utBuilder.setWsTimeSource(wssConfig.getCurrentTime());
// If NoPassword property is set we don't need to set the password
if (token.getPasswordType() == UsernameToken.PasswordType.NoPassword) {
utBuilder.setUserInfo(userName, null);
utBuilder.setPasswordType(null);
} else {
String password =
(String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.PASSWORD, message);
if (StringUtils.isEmpty(password)) {
password = getPassword(userName, token, WSPasswordCallback.USERNAME_TOKEN);
}
if (password != null) {
// If the password is available then build the token
if (token.getPasswordType() == UsernameToken.PasswordType.HashPassword) {
utBuilder.setPasswordType(WSConstants.PASSWORD_DIGEST);
} else {
utBuilder.setPasswordType(WSConstants.PASSWORD_TEXT);
}
utBuilder.setUserInfo(userName, password);
} else {
unassertPolicy(token, "No password available");
return null;
}
}
if (token.isCreated() && token.getPasswordType() != UsernameToken.PasswordType.HashPassword) {
utBuilder.addCreated();
}
if (token.isNonce() && token.getPasswordType() != UsernameToken.PasswordType.HashPassword) {
utBuilder.addNonce();
}
return utBuilder;
} else {
unassertPolicy(token, "No username available");
return null;
}
}
protected WSSecUsernameToken addDKUsernameToken(UsernameToken token, boolean useMac) {
assertToken(token);
if (!isTokenRequired(token.getIncludeTokenType())) {
return null;
}
String userName = (String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.USERNAME, message);
if (!StringUtils.isEmpty(userName)) {
WSSecUsernameToken utBuilder = new WSSecUsernameToken(secHeader);
utBuilder.setIdAllocator(wssConfig.getIdAllocator());
utBuilder.setWsTimeSource(wssConfig.getCurrentTime());
String password =
(String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.PASSWORD, message);
if (StringUtils.isEmpty(password)) {
password = getPassword(userName, token, WSPasswordCallback.USERNAME_TOKEN);
}
if (!StringUtils.isEmpty(password)) {
// If the password is available then build the token
utBuilder.setUserInfo(userName, password);
utBuilder.addDerivedKey(useMac, null, 1000);
utBuilder.prepare();
} else {
unassertPolicy(token, "No password available");
return null;
}
return utBuilder;
} else {
unassertPolicy(token, "No username available");
return null;
}
}
protected SamlAssertionWrapper addSamlToken(SamlToken token) throws WSSecurityException {
assertToken(token);
if (!isTokenRequired(token.getIncludeTokenType())) {
return null;
}
//
// Get the SAML CallbackHandler
//
Object o = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SAML_CALLBACK_HANDLER, message);
if (o == null) {
SecurityToken securityToken = getSecurityToken();
if (securityToken != null) {
Element tokenElement = (Element)securityToken.getToken();
String namespace = tokenElement.getNamespaceURI();
String localname = tokenElement.getLocalName();
SamlTokenType tokenType = token.getSamlTokenType();
if ((tokenType == SamlTokenType.WssSamlV11Token10 || tokenType == SamlTokenType.WssSamlV11Token11)
&& WSConstants.SAML_NS.equals(namespace) && "Assertion".equals(localname)) {
return new SamlAssertionWrapper(tokenElement);
} else if (tokenType == SamlTokenType.WssSamlV20Token11
&& WSConstants.SAML2_NS.equals(namespace) && "Assertion".equals(localname)) {
return new SamlAssertionWrapper(tokenElement);
}
}
}
SAMLCallback samlCallback = new SAMLCallback();
SamlTokenType tokenType = token.getSamlTokenType();
if (tokenType == SamlTokenType.WssSamlV11Token10 || tokenType == SamlTokenType.WssSamlV11Token11) {
samlCallback.setSamlVersion(Version.SAML_11);
} else if (tokenType == SamlTokenType.WssSamlV20Token11) {
samlCallback.setSamlVersion(Version.SAML_20);
}
try {
CallbackHandler handler = SecurityUtils.getCallbackHandler(o);
if (handler == null) {
unassertPolicy(token, "No SAML CallbackHandler available");
return null;
}
SAMLUtil.doSAMLCallback(handler, samlCallback);
} catch (Exception ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
}
SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
if (samlCallback.isSignAssertion()) {
String issuerName = samlCallback.getIssuerKeyName();
if (issuerName == null) {
String userNameKey = SecurityConstants.SIGNATURE_USERNAME;
issuerName = (String)SecurityUtils.getSecurityPropertyValue(userNameKey, message);
}
String password = samlCallback.getIssuerKeyPassword();
if (password == null) {
password = getPassword(issuerName, token, WSPasswordCallback.SIGNATURE);
}
Crypto crypto = samlCallback.getIssuerCrypto();
if (crypto == null) {
crypto = getSignatureCrypto();
}
assertion.signAssertion(
issuerName,
password,
crypto,
samlCallback.isSendKeyValue(),
samlCallback.getCanonicalizationAlgorithm(),
samlCallback.getSignatureAlgorithm(),
samlCallback.getSignatureDigestAlgorithm()
);
}
return assertion;
}
/**
* Store a SAML Assertion as a SecurityToken
*/
protected void storeAssertionAsSecurityToken(SamlAssertionWrapper assertion) {
String id = findIDFromSamlToken(assertion.getElement());
if (id == null) {
return;
}
SecurityToken secToken = new SecurityToken(id);
if (assertion.getSaml2() != null) {
secToken.setTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
} else {
secToken.setTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
}
secToken.setToken(assertion.getElement());
getTokenStore().add(secToken);
message.put(SecurityConstants.TOKEN_ID, secToken.getId());
}
protected String findIDFromSamlToken(Element samlToken) {
String id = null;
if (samlToken != null) {
QName elName = DOMUtils.getElementQName(samlToken);
if (elName.equals(new QName(WSConstants.SAML_NS, "Assertion"))
&& samlToken.hasAttributeNS(null, "AssertionID")) {
id = samlToken.getAttributeNS(null, "AssertionID");
} else if (elName.equals(new QName(WSConstants.SAML2_NS, "Assertion"))
&& samlToken.hasAttributeNS(null, "ID")) {
id = samlToken.getAttributeNS(null, "ID");
}
if (id == null) {
id = samlToken.getAttributeNS(WSConstants.WSU_NS, "Id");
}
}
return id;
}
protected String getPassword(String userName, Assertion info, int usage) {
//Then try to get the password from the given callback handler
Object o = SecurityUtils.getSecurityPropertyValue(SecurityConstants.CALLBACK_HANDLER, message);
CallbackHandler handler = null;
try {
handler = SecurityUtils.getCallbackHandler(o);
if (handler == null) {
// Don't unassert for signature as we might get the password from the crypto properties
if (usage == WSPasswordCallback.SIGNATURE) {
LOG.info("No CallbackHandler available to retrieve a password. We will now try the crypto "
+ "properties file for a private password");
} else {
unassertPolicy(info, "No callback handler and no password available");
}
return null;
}
} catch (Exception ex) {
// Don't unassert for signature as we might get the password from the crypto properties
if (usage == WSPasswordCallback.SIGNATURE) {
LOG.info("No CallbackHandler available to retrieve a password. We will now try the crypto "
+ "properties file for a private password");
} else {
unassertPolicy(info, "No callback handler and no password available");
}
return null;
}
WSPasswordCallback[] cb = {new WSPasswordCallback(userName, usage)};
try {
handler.handle(cb);
} catch (Exception e) {
unassertPolicy(info, e);
}
//get the password
return cb[0].getPassword();
}
/**
* Generates a wsu:Id attribute for the provided {@code Element} and returns the attribute value
* or finds and returns the value of the attribute if it already exists.
*
* @param element the {@code Element} to check/create the attribute on
*
* @return the generated or discovered wsu:Id attribute value
*/
public String addWsuIdToElement(Element elem) {
String id;
//first try to get the Id attr
Attr idAttr = elem.getAttributeNodeNS(null, "Id");
if (idAttr == null) {
//then try the wsu:Id value
idAttr = elem.getAttributeNodeNS(PolicyConstants.WSU_NAMESPACE_URI, "Id");
}
if (idAttr != null) {
id = idAttr.getValue();
} else {
//Add an id
id = wssConfig.getIdAllocator().createId("_", elem);
String pfx = null;
try {
pfx = elem.lookupPrefix(PolicyConstants.WSU_NAMESPACE_URI);
} catch (Throwable t) {
pfx = DOMUtils.getPrefixRecursive(elem, PolicyConstants.WSU_NAMESPACE_URI);
}
boolean found = !StringUtils.isEmpty(pfx);
int cnt = 0;
while (StringUtils.isEmpty(pfx)) {
pfx = "wsu" + (cnt == 0 ? "" : cnt);
String ns;
try {
ns = elem.lookupNamespaceURI(pfx);
} catch (Throwable t) {
ns = DOMUtils.getNamespace(elem, pfx);
}
if (!StringUtils.isEmpty(ns)) {
pfx = null;
cnt++;
}
}
if (!found) {
idAttr = elem.getOwnerDocument().createAttributeNS(WSDLConstants.NS_XMLNS, "xmlns:" + pfx);
idAttr.setValue(PolicyConstants.WSU_NAMESPACE_URI);
elem.setAttributeNodeNS(idAttr);
}
idAttr = elem.getOwnerDocument().createAttributeNS(PolicyConstants.WSU_NAMESPACE_URI,
pfx + ":Id");
idAttr.setValue(id);
elem.setAttributeNodeNS(idAttr);
}
return id;
}
public List<WSEncryptionPart> getEncryptedParts()
throws SOAPException {
EncryptedParts parts = null;
EncryptedElements elements = null;
ContentEncryptedElements celements = null;
Collection<AssertionInfo> ais = getAllAssertionsByLocalname(SPConstants.ENCRYPTED_PARTS);
if (!ais.isEmpty()) {
for (AssertionInfo ai : ais) {
parts = (EncryptedParts)ai.getAssertion();
ai.setAsserted(true);
}
}
ais = getAllAssertionsByLocalname(SPConstants.ENCRYPTED_ELEMENTS);
if (!ais.isEmpty()) {
for (AssertionInfo ai : ais) {
elements = (EncryptedElements)ai.getAssertion();
ai.setAsserted(true);
}
}
ais = getAllAssertionsByLocalname(SPConstants.CONTENT_ENCRYPTED_ELEMENTS);
if (!ais.isEmpty()) {
for (AssertionInfo ai : ais) {
celements = (ContentEncryptedElements)ai.getAssertion();
ai.setAsserted(true);
}
}
if (parts == null && elements == null && celements == null) {
return new ArrayList<>();
}
List<WSEncryptionPart> securedParts = new ArrayList<>();
boolean isBody = false;
if (parts != null) {
isBody = parts.isBody();
for (Header head : parts.getHeaders()) {
WSEncryptionPart wep = new WSEncryptionPart(head.getName(),
head.getNamespace(),
"Header");
securedParts.add(wep);
}
Attachments attachments = parts.getAttachments();
if (attachments != null) {
String encModifier = "Element";
if (MessageUtils.getContextualBoolean(
message, SecurityConstants.USE_ATTACHMENT_ENCRYPTION_CONTENT_ONLY_TRANSFORM, false)) {
encModifier = "Content";
}
WSEncryptionPart wep = new WSEncryptionPart("cid:Attachments", encModifier);
securedParts.add(wep);
}
}
// REVISIT consider catching exceptions and unassert failed assertions or
// to process and assert them one at a time. Additionally, a found list
// should be applied to all operations that involve adding anything to
// the encrypted list to prevent duplication / errors in encryption.
return getPartsAndElements(false,
isBody,
securedParts,
elements == null ? null : elements.getXPaths(),
celements == null ? null : celements.getXPaths());
}
public List<WSEncryptionPart> getSignedParts(SupportingTokens supportingToken)
throws SOAPException {
boolean isSignBody = false;
SignedParts parts = null;
SignedElements elements = null;
if (supportingToken != null && supportingToken.isEndorsing()) {
parts = supportingToken.getSignedParts();
elements = supportingToken.getSignedElements();
// Store them so that the main Signature doesn't sign them
if (parts != null) {
suppTokenParts.add(parts);
this.assertPolicy(parts.getName());
}
if (elements != null) {
suppTokenParts.add(elements);
this.assertPolicy(elements.getName());
}
} else {
Collection<AssertionInfo> ais = getAllAssertionsByLocalname(SPConstants.SIGNED_PARTS);
if (!ais.isEmpty()) {
for (AssertionInfo ai : ais) {
SignedParts signedParts = (SignedParts)ai.getAssertion();
ai.setAsserted(true);
if (!suppTokenParts.contains(signedParts)) {
parts = signedParts;
}
}
}
ais = getAllAssertionsByLocalname(SPConstants.SIGNED_ELEMENTS);
if (!ais.isEmpty()) {
for (AssertionInfo ai : ais) {
SignedElements signedElements = (SignedElements)ai.getAssertion();
ai.setAsserted(true);
if (!suppTokenParts.contains(signedElements)) {
elements = signedElements;
}
}
}
}
if (parts == null && elements == null) {
return new ArrayList<>();
}
List<WSEncryptionPart> signedParts = new ArrayList<>();
if (parts != null) {
isSignBody = parts.isBody();
for (Header head : parts.getHeaders()) {
WSEncryptionPart wep = new WSEncryptionPart(head.getName(),
head.getNamespace(),
"Header");
signedParts.add(wep);
}
Attachments attachments = parts.getAttachments();
if (attachments != null) {
String modifier = "Element";
if (attachments.isContentSignatureTransform()) {
modifier = "Content";
}
WSEncryptionPart wep = new WSEncryptionPart("cid:Attachments", modifier);
signedParts.add(wep);
}
}
return getPartsAndElements(true,
isSignBody,
signedParts,
elements == null ? null : elements.getXPaths(),
null);
}
/**
* Identifies the portions of the message to be signed/encrypted.
*
* @param sign
* whether the matches are to be signed or encrypted
* @param includeBody
* if the body should be included in the signature/encryption
* @param parts
* any {@code WSEncryptionPart}s to match for signature or
* encryption as specified by WS-SP signed parts or encrypted
* parts. Parts without a name match all elements with the
* provided namespace.
* @param xpaths
* any XPath expressions to sign/encrypt matches
* @param contentXpaths
* any XPath expressions to content encrypt
* @return a configured list of {@code WSEncryptionPart}s suitable for
* processing by WSS4J
* @throws SOAPException
* if there is an error extracting SOAP content from the SAAJ
* model
*/
public List<WSEncryptionPart> getPartsAndElements(boolean sign,
boolean includeBody,
List<WSEncryptionPart> parts,
List<org.apache.wss4j.policy.model.XPath> xpaths,
List<org.apache.wss4j.policy.model.XPath> contentXpaths)
throws SOAPException {
List<WSEncryptionPart> result = new ArrayList<>();
List<Element> found = new ArrayList<>();
// Handle sign/enc parts
result.addAll(this.getParts(sign, includeBody, parts, found));
// Handle sign/enc elements
result.addAll(this.getElements("Header", xpaths, found, sign));
if (!sign) {
// Handle content encrypted elements
result.addAll(this.getElements("Content", contentXpaths, found, sign));
}
return result;
}
/**
* Identifies the portions of the message to be signed/encrypted.
*
* @param sign
* whether the matches are to be signed or encrypted
* @param includeBody
* if the body should be included in the signature/encryption
* @param parts
* any {@code WSEncryptionPart}s to match for signature or
* encryption as specified by WS-SP signed parts or encrypted
* parts. Parts without a name match all elements with the
* provided namespace.
* @param found
* a list of elements that have previously been tagged for
* signing/encryption. Populated with additional matches found by
* this method and used to prevent including the same element
* twice under the same operation.
* @return a configured list of {@code WSEncryptionPart}s suitable for
* processing by WSS4J
* @throws SOAPException
* if there is an error extracting SOAP content from the SAAJ
* model
*/
protected List<WSEncryptionPart> getParts(boolean sign,
boolean includeBody, List<WSEncryptionPart> parts,
List<Element> found) throws SOAPException {
List<WSEncryptionPart> result = new ArrayList<>();
Element soapBody = SAAJUtils.getBody(this.saaj);
soapBody = (Element)DOMUtils.getDomElement(soapBody);
if (includeBody && !found.contains(soapBody)) {
found.add(soapBody);
final String id = this.addWsuIdToElement(soapBody);
if (sign) {
WSEncryptionPart bodyPart = new WSEncryptionPart(id, "Element");
bodyPart.setElement(soapBody);
result.add(bodyPart);
} else {
WSEncryptionPart bodyPart = new WSEncryptionPart(id, "Content");
bodyPart.setElement(soapBody);
result.add(bodyPart);
}
}
final SOAPHeader header = SAAJUtils.getHeader(saaj);
// Handle sign/enc parts
for (WSEncryptionPart part : parts) {
if (part.getId() != null && part.getId().startsWith("cid:")) {
// Attachments are handled inside WSS4J via a CallbackHandler
result.add(part);
continue;
}
final List<Element> elements;
if (StringUtils.isEmpty(part.getName())) {
// An entire namespace
elements =
DOMUtils.getChildrenWithNamespace(header, part.getNamespace());
} else {
// All elements with a given name and namespace
elements =
DOMUtils.getChildrenWithName(header, part.getNamespace(), part.getName());
}
for (Element el : elements) {
if (!found.contains(el)) {
found.add(el);
// Generate an ID for the element and use this ID or else
// WSS4J will only ever sign/encrypt the first matching
// element with the same name and namespace as that in the
// WSEncryptionPart
final String id = this.addWsuIdToElement(el);
WSEncryptionPart elPart =
new WSEncryptionPart(id, part.getEncModifier());
elPart.setElement(el);
result.add(elPart);
}
}
}
return result;
}
/**
* Identifies the portions of the message to be signed/encrypted.
*
* @param encryptionModifier
* indicates the scope of the crypto operation over matched
* elements. Either "Content" or "Element".
* @param xpaths
* any XPath expressions to sign/encrypt matches
* @param found
* a list of elements that have previously been tagged for
* signing/encryption. Populated with additional matches found by
* this method and used to prevent including the same element
* twice under the same operation.
* @param forceId
* force adding a wsu:Id onto the elements. Recommended for signatures.
* @return a configured list of {@code WSEncryptionPart}s suitable for
* processing by WSS4J
* @throws XPathExpressionException
* if a provided XPath is invalid
* @throws SOAPException
* if there is an error extracting SOAP content from the SAAJ
* model
*/
protected List<WSEncryptionPart> getElements(String encryptionModifier,
List<org.apache.wss4j.policy.model.XPath> xpaths,
List<Element> found,
boolean forceId) throws SOAPException {
List<WSEncryptionPart> result = new ArrayList<>();
if (xpaths != null && !xpaths.isEmpty()) {
XPathFactory factory = XPathFactory.newInstance();
for (org.apache.wss4j.policy.model.XPath xPath : xpaths) {
XPath xpath = factory.newXPath();
if (xPath.getPrefixNamespaceMap() != null) {
xpath.setNamespaceContext(new MapNamespaceContext(xPath.getPrefixNamespaceMap()));
}
NodeList list = null;
try {
Element envelope = saaj.getSOAPPart().getEnvelope();
envelope = (Element)DOMUtils.getDomElement(envelope);
list = (NodeList)xpath.evaluate(xPath.getXPath(), envelope, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
LOG.log(Level.WARNING, "Failure in evaluating an XPath expression", e);
}
if (list != null) {
for (int x = 0; x < list.getLength(); x++) {
Element el = (Element)list.item(x);
if (!found.contains(el)) {
found.add(el);
String id = setIdOnElement(el, forceId);
WSEncryptionPart part =
new WSEncryptionPart(id, encryptionModifier);
part.setElement(el);
part.setXpath(xPath.getXPath());
result.add(part);
}
}
}
}
}
return result;
}
private String setIdOnElement(Element element, boolean forceId) {
if (forceId) {
return this.addWsuIdToElement(element);
}
//not forcing an ID on this. Use one if there is one
//there already, but don't force one
Attr idAttr = element.getAttributeNodeNS(null, "Id");
if (idAttr == null) {
//then try the wsu:Id value
idAttr = element.getAttributeNodeNS(PolicyConstants.WSU_NAMESPACE_URI, "Id");
}
if (idAttr != null) {
return idAttr.getValue();
}
return null;
}
protected WSSecEncryptedKey getEncryptedKeyBuilder(AbstractToken token) throws WSSecurityException {
WSSecEncryptedKey encrKey = new WSSecEncryptedKey(secHeader);
encrKey.setIdAllocator(wssConfig.getIdAllocator());
encrKey.setCallbackLookup(callbackLookup);
encrKey.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message));
encrKey.setStoreBytesInAttachment(storeBytesInAttachment);
Crypto crypto = getEncryptionCrypto();
message.getExchange().put(SecurityConstants.ENCRYPT_CRYPTO, crypto);
setKeyIdentifierType(encrKey, token);
boolean alsoIncludeToken = false;
// Find out do we also need to include the token as per the Inclusion requirement
if (token instanceof X509Token
&& token.getIncludeTokenType() != IncludeTokenType.INCLUDE_TOKEN_NEVER
&& encrKey.getKeyIdentifierType() != WSConstants.BST_DIRECT_REFERENCE) {
alsoIncludeToken = true;
}
String encrUser = setEncryptionUser(encrKey, token, false, crypto);
AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType();
encrKey.setSymmetricEncAlgorithm(algType.getEncryption());
encrKey.setKeyEncAlgo(algType.getAsymmetricKeyWrap());
encrKey.setMGFAlgorithm(algType.getMGFAlgo());
encrKey.prepare(crypto);
if (alsoIncludeToken) {
X509Certificate encCert = getEncryptCert(crypto, encrUser);
BinarySecurity bstToken = new X509Security(saaj.getSOAPPart());
((X509Security)bstToken).setX509Certificate(encCert);
bstToken.addWSUNamespace();
bstToken.setID(wssConfig.getIdAllocator().createSecureId("X509-", encCert));
WSSecurityUtil.prependChildElement(
secHeader.getSecurityHeaderElement(), bstToken.getElement()
);
bstElement = bstToken.getElement();
}
return encrKey;
}
private X509Certificate getEncryptCert(Crypto crypto, String encrUser) throws WSSecurityException {
// Check for prepared encryption certificate
X509Certificate encrCert =
(X509Certificate)SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_CERT, message);
if (encrCert != null) {
return encrCert;
}
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
cryptoType.setAlias(encrUser);
X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
if (certs != null && certs.length > 0) {
return certs[0];
}
return null;
}
public Crypto getSignatureCrypto() throws WSSecurityException {
return getCrypto(SecurityConstants.SIGNATURE_CRYPTO, SecurityConstants.SIGNATURE_PROPERTIES);
}
public Crypto getEncryptionCrypto() throws WSSecurityException {
Crypto crypto =
getCrypto(SecurityConstants.ENCRYPT_CRYPTO, SecurityConstants.ENCRYPT_PROPERTIES);
boolean enableRevocation = false;
String enableRevStr =
(String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENABLE_REVOCATION, message);
if (enableRevStr != null) {
enableRevocation = Boolean.parseBoolean(enableRevStr);
}
if (enableRevocation && crypto != null) {
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
String encrUser =
(String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_USERNAME, message);
if (encrUser == null) {
try {
encrUser = crypto.getDefaultX509Identifier();
} catch (WSSecurityException e1) {
throw new Fault(e1);
}
}
cryptoType.setAlias(encrUser);
X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
if (certs != null && certs.length > 0) {
crypto.verifyTrust(certs, enableRevocation, null, null);
}
}
if (crypto != null) {
this.message.getExchange().put(SecurityConstants.ENCRYPT_CRYPTO, crypto);
}
return crypto;
}
protected Crypto getCrypto(
String cryptoKey,
String propKey
) throws WSSecurityException {
Crypto crypto = (Crypto)SecurityUtils.getSecurityPropertyValue(cryptoKey, message);
if (crypto != null) {
return crypto;
}
Object o = SecurityUtils.getSecurityPropertyValue(propKey, message);
if (o == null) {
return null;
}
crypto = getCryptoCache().get(o);
if (crypto != null) {
return crypto;
}
URL propsURL = SecurityUtils.loadResource(message, o);
Properties properties = WSS4JUtils.getProps(o, propsURL);
if (properties != null) {
crypto = CryptoFactory.getInstance(properties,
Loader.getClassLoader(CryptoFactory.class),
WSS4JUtils.getPasswordEncryptor(message));
getCryptoCache().put(o, crypto);
}
return crypto;
}
public void setKeyIdentifierType(WSSecBase secBase, AbstractToken token) {
boolean tokenTypeSet = false;
if (token instanceof X509Token) {
X509Token x509Token = (X509Token)token;
if (x509Token.isRequireIssuerSerialReference()) {
secBase.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
tokenTypeSet = true;
} else if (x509Token.isRequireKeyIdentifierReference()) {
secBase.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
tokenTypeSet = true;
} else if (x509Token.isRequireThumbprintReference()) {
secBase.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
tokenTypeSet = true;
}
} else if (token instanceof KeyValueToken) {
secBase.setKeyIdentifierType(WSConstants.KEY_VALUE);
tokenTypeSet = true;
}
assertToken(token);
if (!tokenTypeSet) {
boolean requestor = isRequestor();
if (token.getIncludeTokenType() == IncludeTokenType.INCLUDE_TOKEN_NEVER
|| token instanceof X509Token
&& ((token.getIncludeTokenType() == IncludeTokenType.INCLUDE_TOKEN_ALWAYS_TO_RECIPIENT
&& !requestor)
|| (token.getIncludeTokenType() == IncludeTokenType.INCLUDE_TOKEN_ALWAYS_TO_INITIATOR
&& requestor))) {
Wss10 wss = getWss10();
assertPolicy(wss);
if (wss == null || wss.isMustSupportRefKeyIdentifier()) {
secBase.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
} else if (wss.isMustSupportRefIssuerSerial()) {
secBase.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
} else if (wss instanceof Wss11 && ((Wss11) wss).isMustSupportRefThumbprint()) {
secBase.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
} else {
secBase.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
}
} else {
secBase.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
}
}
}
public String setEncryptionUser(WSSecEncryptedKey encrKeyBuilder, AbstractToken token,
boolean sign, Crypto crypto) {
// Check for prepared certificate property
X509Certificate encrCert =
(X509Certificate)SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_CERT, message);
if (encrCert != null) {
encrKeyBuilder.setUseThisCert(encrCert);
return null;
}
String key = sign ? SecurityConstants.SIGNATURE_USERNAME : SecurityConstants.ENCRYPT_USERNAME;
String encrUser = (String)SecurityUtils.getSecurityPropertyValue(key, message);
if (crypto != null && (encrUser == null || "".equals(encrUser))) {
try {
encrUser = crypto.getDefaultX509Identifier();
} catch (WSSecurityException e1) {
throw new Fault(e1);
}
}
if (encrUser == null || "".equals(encrUser)) {
unassertPolicy(token, "A " + (sign ? "signature" : "encryption") + " username needs to be declared.");
}
if (WSHandlerConstants.USE_REQ_SIG_CERT.equals(encrUser)) {
List<WSHandlerResult> results =
CastUtils.cast((List<?>)
message.getExchange().getInMessage().get(WSHandlerConstants.RECV_RESULTS));
if (results != null) {
encrKeyBuilder.setUseThisCert(WSS4JUtils.getReqSigCert(results));
//TODO This is a hack, this should not come under USE_REQ_SIG_CERT
if (encrKeyBuilder.isCertSet()) {
encrKeyBuilder.setUserInfo(getUsername(results));
}
} else {
unassertPolicy(token, "No security results in incoming message");
}
} else {
encrKeyBuilder.setUserInfo(encrUser);
}
return encrUser;
}
/**
* Scan through <code>WSHandlerResult<code> list for a Username token and return
* the username if a Username Token found
* @param results
* @return
*/
public static String getUsername(List<WSHandlerResult> results) {
/*
* Scan the results for a matching actor. Use results only if the
* receiving Actor and the sending Actor match.
*/
for (WSHandlerResult rResult : results) {
List<WSSecurityEngineResult> wsSecEngineResults = rResult.getResults();
/*
* Scan the results for a username token. Use the username
* of this token to set the alias for the encryption user
*/
for (WSSecurityEngineResult wser : wsSecEngineResults) {
Integer actInt = (Integer)wser.get(WSSecurityEngineResult.TAG_ACTION);
if (actInt.intValue() == WSConstants.UT) {
UsernameTokenPrincipal principal
= (UsernameTokenPrincipal)wser.get(WSSecurityEngineResult.TAG_PRINCIPAL);
return principal.getName();
}
}
}
return null;
}
protected WSSecurityEngineResult getEncryptedKeyResult() {
List<WSHandlerResult> results = CastUtils.cast((List<?>)message.getExchange().getInMessage()
.get(WSHandlerConstants.RECV_RESULTS));
for (WSHandlerResult rResult : results) {
List<WSSecurityEngineResult> encryptedResults = rResult.getActionResults().get(WSConstants.ENCR);
if (encryptedResults != null) {
for (WSSecurityEngineResult wser : encryptedResults) {
String encryptedKeyID = (String)wser.get(WSSecurityEngineResult.TAG_ID);
if (encryptedKeyID != null && encryptedKeyID.length() != 0) {
return wser;
}
}
}
}
return null;
}
private void checkForX509PkiPath(WSSecSignature sig, AbstractToken token) {
if (token instanceof X509Token) {
X509Token x509Token = (X509Token) token;
TokenType tokenType = x509Token.getTokenType();
if (tokenType == TokenType.WssX509PkiPathV1Token10
|| tokenType == TokenType.WssX509PkiPathV1Token11) {
sig.setUseSingleCertificate(false);
}
}
}
protected WSSecSignature getSignatureBuilder(
AbstractToken token, boolean attached, boolean endorse
) throws WSSecurityException {
WSSecSignature sig = new WSSecSignature(secHeader);
sig.setIdAllocator(wssConfig.getIdAllocator());
sig.setCallbackLookup(callbackLookup);
sig.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message));
sig.setStoreBytesInAttachment(storeBytesInAttachment);
sig.setExpandXopInclude(isExpandXopInclude());
sig.setWsDocInfo(wsDocInfo);
checkForX509PkiPath(sig, token);
if (token instanceof IssuedToken || token instanceof SamlToken) {
assertToken(token);
SecurityToken securityToken = getSecurityToken();
String tokenType = securityToken.getTokenType();
Element ref;
if (attached) {
ref = securityToken.getAttachedReference();
} else {
ref = securityToken.getUnattachedReference();
}
if (ref != null) {
SecurityTokenReference secRef =
new SecurityTokenReference(cloneElement(ref), new BSPEnforcer());
sig.setSecurityTokenReference(secRef);
sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER);
} else {
int type = attached ? WSConstants.CUSTOM_SYMM_SIGNING
: WSConstants.CUSTOM_SYMM_SIGNING_DIRECT;
if (WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER);
} else if (WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML2_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML2_KI_VALUE_TYPE);
sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER);
} else {
sig.setCustomTokenValueType(tokenType);
sig.setKeyIdentifierType(type);
}
}
String sigTokId;
if (attached) {
sigTokId = securityToken.getWsuId();
if (sigTokId == null) {
sigTokId = securityToken.getId();
}
if (sigTokId.startsWith("#")) {
sigTokId = sigTokId.substring(1);
}
} else {
sigTokId = securityToken.getId();
}
sig.setCustomTokenId(sigTokId);
} else {
setKeyIdentifierType(sig, token);
// Find out do we also need to include the token as per the Inclusion requirement
if (token instanceof X509Token
&& token.getIncludeTokenType() != IncludeTokenType.INCLUDE_TOKEN_NEVER
&& (sig.getKeyIdentifierType() != WSConstants.BST_DIRECT_REFERENCE
&& sig.getKeyIdentifierType() != WSConstants.KEY_VALUE)) {
sig.setIncludeSignatureToken(true);
}
}
boolean encryptCrypto = false;
String userNameKey = SecurityConstants.SIGNATURE_USERNAME;
String type = "signature";
if (binding instanceof SymmetricBinding && !endorse) {
encryptCrypto = ((SymmetricBinding)binding).getProtectionToken() != null;
userNameKey = SecurityConstants.ENCRYPT_USERNAME;
}
Crypto crypto = encryptCrypto ? getEncryptionCrypto() : getSignatureCrypto();
if (endorse && crypto == null && binding instanceof SymmetricBinding) {
type = "encryption";
userNameKey = SecurityConstants.ENCRYPT_USERNAME;
crypto = getEncryptionCrypto();
}
if (!encryptCrypto) {
message.getExchange().put(SecurityConstants.SIGNATURE_CRYPTO, crypto);
}
String user = (String)SecurityUtils.getSecurityPropertyValue(userNameKey, message);
if (StringUtils.isEmpty(user)) {
if (crypto != null) {
try {
user = crypto.getDefaultX509Identifier();
if (StringUtils.isEmpty(user)) {
unassertPolicy(token, "No configured " + type + " username detected");
return null;
}
} catch (WSSecurityException e1) {
LOG.log(Level.FINE, e1.getMessage(), e1);
throw new Fault(e1);
}
} else {
unassertPolicy(token, "Security configuration could not be detected. "
+ "Potential cause: Make sure jaxws:client element with name "
+ "attribute value matching endpoint port is defined as well as a "
+ SecurityConstants.SIGNATURE_PROPERTIES + " element within it.");
return null;
}
}
String password = getPassword(user, token, WSPasswordCallback.SIGNATURE);
sig.setUserInfo(user, password);
sig.setSignatureAlgorithm(binding.getAlgorithmSuite().getAsymmetricSignature());
AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType();
sig.setDigestAlgo(algType.getDigest());
sig.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue());
boolean includePrefixes =
MessageUtils.getContextualBoolean(
message, SecurityConstants.ADD_INCLUSIVE_PREFIXES, true
);
sig.setAddInclusivePrefixes(includePrefixes);
try {
sig.prepare(crypto);
} catch (WSSecurityException e) {
LOG.log(Level.FINE, e.getMessage(), e);
unassertPolicy(token, e);
}
return sig;
}
protected void doEndorsedSignatures(List<SupportingToken> tokenList,
boolean isTokenProtection,
boolean isSigProtect) {
for (SupportingToken supportingToken : tokenList) {
Object tempTok = supportingToken.getTokenImplementation();
List<WSEncryptionPart> sigParts = new ArrayList<>();
WSEncryptionPart sigPart = new WSEncryptionPart(mainSigId);
sigPart.setElement(bottomUpElement);
sigParts.add(sigPart);
if (supportingToken.getSignedParts() != null) {
for (WSEncryptionPart signedPart : supportingToken.getSignedParts()) {
sigParts.add(signedPart);
}
}
if (tempTok instanceof WSSecSignature) {
WSSecSignature sig = (WSSecSignature)tempTok;
if (isTokenProtection && sig.getBSTTokenId() != null) {
WSEncryptionPart bstPart =
new WSEncryptionPart(sig.getBSTTokenId());
bstPart.setElement(sig.getBinarySecurityTokenElement());
sigParts.add(bstPart);
}
try {
List<Reference> referenceList = sig.addReferencesToSign(sigParts);
sig.computeSignature(referenceList, false, null);
addSig(sig.getSignatureValue());
if (isSigProtect) {
WSEncryptionPart part = new WSEncryptionPart(sig.getId(), "Element");
encryptedTokensList.add(part);
}
} catch (WSSecurityException e) {
unassertPolicy(supportingToken.getToken(), e);
}
} else if (tempTok instanceof WSSecurityTokenHolder) {
SecurityToken token = ((WSSecurityTokenHolder)tempTok).getToken();
if (isTokenProtection) {
sigParts.add(new WSEncryptionPart(token.getId()));
}
try {
if (supportingToken.getToken().getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
doSymmSignatureDerived(supportingToken.getToken(), token, sigParts,
isTokenProtection, isSigProtect);
} else {
doSymmSignature(supportingToken.getToken(), token, sigParts,
isTokenProtection, isSigProtect);
}
} catch (Exception e) {
LOG.log(Level.FINE, e.getMessage(), e);
}
} else if (tempTok instanceof WSSecUsernameToken) {
WSSecUsernameToken utBuilder = (WSSecUsernameToken)tempTok;
String id = utBuilder.getId();
Instant created = Instant.now();
Instant expires = created.plusSeconds(WSS4JUtils.getSecurityTokenLifetime(message) / 1000L);
SecurityToken secToken = new SecurityToken(id,
utBuilder.getUsernameTokenElement(),
created,
expires);
if (isTokenProtection) {
sigParts.add(new WSEncryptionPart(secToken.getId()));
}
try {
byte[] secret = utBuilder.getDerivedKey();
secToken.setSecret(secret);
if (supportingToken.getToken().getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
doSymmSignatureDerived(supportingToken.getToken(), secToken, sigParts,
isTokenProtection, isSigProtect);
} else {
doSymmSignature(supportingToken.getToken(), secToken, sigParts,
isTokenProtection, isSigProtect);
}
} catch (Exception e) {
LOG.log(Level.FINE, e.getMessage(), e);
}
}
}
}
private void doSymmSignatureDerived(AbstractToken policyToken, SecurityToken tok,
List<WSEncryptionPart> sigParts, boolean isTokenProtection,
boolean isSigProtect)
throws WSSecurityException {
Document doc = saaj.getSOAPPart();
WSSecDKSign dkSign = new WSSecDKSign(secHeader);
dkSign.setIdAllocator(wssConfig.getIdAllocator());
dkSign.setCallbackLookup(callbackLookup);
dkSign.setStoreBytesInAttachment(storeBytesInAttachment);
dkSign.setExpandXopInclude(isExpandXopInclude());
//Check whether it is security policy 1.2 and use the secure conversation accordingly
if (policyToken.getVersion() == SPConstants.SPVersion.SP11) {
dkSign.setWscVersion(ConversationConstants.VERSION_05_02);
}
//Check for whether the token is attached in the message or not
boolean attached = false;
if (isTokenRequired(policyToken.getIncludeTokenType())) {
attached = true;
}
// Setting the AttachedReference or the UnattachedReference according to the flag
Element ref;
if (attached) {
ref = tok.getAttachedReference();
} else {
ref = tok.getUnattachedReference();
}
if (ref != null) {
ref = cloneElement(ref);
dkSign.setExternalKey(tok.getSecret(), ref);
} else if (!isRequestor() && policyToken.getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
// If the Encrypted key used to create the derived key is not
// attached use key identifier as defined in WSS1.1 section
// 7.7 Encrypted Key reference
SecurityTokenReference tokenRef
= new SecurityTokenReference(doc);
if (tok.getSHA1() != null) {
tokenRef.setKeyIdentifierEncKeySHA1(tok.getSHA1());
tokenRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
}
dkSign.setExternalKey(tok.getSecret(), tokenRef.getElement());
} else {
dkSign.setExternalKey(tok.getSecret(), tok.getId());
}
//Set the algo info
dkSign.setSignatureAlgorithm(binding.getAlgorithmSuite().getSymmetricSignature());
dkSign.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue());
AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType();
dkSign.setDerivedKeyLength(algType.getSignatureDerivedKeyLength() / 8);
if (tok.getSHA1() != null) {
//Set the value type of the reference
dkSign.setCustomValueType(WSConstants.SOAPMESSAGE_NS11 + "#"
+ WSConstants.ENC_KEY_VALUE_TYPE);
} else if (policyToken instanceof UsernameToken) {
dkSign.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
}
dkSign.prepare();
if (isTokenProtection) {
String sigTokId = XMLUtils.getIDFromReference(tok.getId());
sigParts.add(new WSEncryptionPart(sigTokId));
}
dkSign.getParts().addAll(sigParts);
List<Reference> referenceList = dkSign.addReferencesToSign(sigParts);
//Add elements to header
addSupportingElement(dkSign.getdktElement());
//Do signature
dkSign.computeSignature(referenceList, false, null);
if (isSigProtect) {
WSEncryptionPart part = new WSEncryptionPart(dkSign.getSignatureId(), "Element");
encryptedTokensList.add(part);
}
addSig(dkSign.getSignatureValue());
}
private void doSymmSignature(AbstractToken policyToken, SecurityToken tok,
List<WSEncryptionPart> sigParts, boolean isTokenProtection,
boolean isSigProtect)
throws WSSecurityException {
WSSecSignature sig = new WSSecSignature(secHeader);
sig.setIdAllocator(wssConfig.getIdAllocator());
sig.setCallbackLookup(callbackLookup);
sig.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message));
sig.setStoreBytesInAttachment(storeBytesInAttachment);
sig.setExpandXopInclude(isExpandXopInclude());
sig.setWsDocInfo(wsDocInfo);
// If a EncryptedKeyToken is used, set the correct value type to
// be used in the wsse:Reference in ds:KeyInfo
if (policyToken instanceof X509Token) {
if (isRequestor()) {
// TODO Add support for SAML2 here
sig.setCustomTokenValueType(
WSConstants.SOAPMESSAGE_NS11 + "#" + WSConstants.ENC_KEY_VALUE_TYPE
);
sig.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
} else {
//the tok has to be an EncryptedKey token
sig.setEncrKeySha1value(tok.getSHA1());
sig.setKeyIdentifierType(WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER);
}
} else {
String tokenType = tok.getTokenType();
if (WSConstants.WSS_SAML_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
} else if (WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
|| WSConstants.SAML2_NS.equals(tokenType)) {
sig.setCustomTokenValueType(WSConstants.WSS_SAML2_KI_VALUE_TYPE);
} else if (tokenType != null) {
sig.setCustomTokenValueType(tokenType);
} else if (policyToken instanceof UsernameToken) {
sig.setCustomTokenValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE);
} else {
sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
}
sig.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING);
}
String sigTokId = tok.getWsuId();
if (sigTokId == null) {
sigTokId = tok.getId();
}
sigTokId = XMLUtils.getIDFromReference(sigTokId);
sig.setCustomTokenId(sigTokId);
sig.setSecretKey(tok.getSecret());
sig.setSignatureAlgorithm(binding.getAlgorithmSuite().getSymmetricSignature());
AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType();
sig.setDigestAlgo(algType.getDigest());
sig.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue());
sig.prepare(getSignatureCrypto());
sig.getParts().addAll(sigParts);
List<Reference> referenceList = sig.addReferencesToSign(sigParts);
//Do signature
sig.computeSignature(referenceList, false, null);
if (isSigProtect) {
WSEncryptionPart part = new WSEncryptionPart(sig.getId(), "Element");
encryptedTokensList.add(part);
}
addSig(sig.getSignatureValue());
}
protected void addSupportingTokens(List<WSEncryptionPart> sigs) throws WSSecurityException {
Collection<AssertionInfo> sgndSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.SIGNED_SUPPORTING_TOKENS);
List<SupportingToken> sigSuppTokList = this.handleSupportingTokens(sgndSuppTokens, false);
Collection<AssertionInfo> endSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.ENDORSING_SUPPORTING_TOKENS);
endSuppTokList = this.handleSupportingTokens(endSuppTokens, true);
Collection<AssertionInfo> sgndEndSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.SIGNED_ENDORSING_SUPPORTING_TOKENS);
sgndEndSuppTokList = this.handleSupportingTokens(sgndEndSuppTokens, true);
Collection<AssertionInfo> sgndEncryptedSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.SIGNED_ENCRYPTED_SUPPORTING_TOKENS);
List<SupportingToken> sgndEncSuppTokList
= this.handleSupportingTokens(sgndEncryptedSuppTokens, false);
Collection<AssertionInfo> endorsingEncryptedSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.ENDORSING_ENCRYPTED_SUPPORTING_TOKENS);
endSuppTokList.addAll(this.handleSupportingTokens(endorsingEncryptedSuppTokens, true));
Collection<AssertionInfo> sgndEndEncSuppTokens =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.SIGNED_ENDORSING_ENCRYPTED_SUPPORTING_TOKENS);
sgndEndSuppTokList.addAll(this.handleSupportingTokens(sgndEndEncSuppTokens, true));
Collection<AssertionInfo> supportingToks =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.SUPPORTING_TOKENS);
this.handleSupportingTokens(supportingToks, false);
Collection<AssertionInfo> encryptedSupportingToks =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.ENCRYPTED_SUPPORTING_TOKENS);
this.handleSupportingTokens(encryptedSupportingToks, false);
//Setup signature parts
addSignatureParts(sigSuppTokList, sigs);
addSignatureParts(sgndEncSuppTokList, sigs);
addSignatureParts(sgndEndSuppTokList, sigs);
}
protected void doEndorse() {
boolean tokenProtect = false;
boolean sigProtect = false;
if (binding instanceof AsymmetricBinding) {
tokenProtect = ((AsymmetricBinding)binding).isProtectTokens();
sigProtect = ((AsymmetricBinding)binding).isEncryptSignature();
} else if (binding instanceof SymmetricBinding) {
tokenProtect = ((SymmetricBinding)binding).isProtectTokens();
sigProtect = ((SymmetricBinding)binding).isEncryptSignature();
}
// Do endorsed signatures
doEndorsedSignatures(endSuppTokList, tokenProtect, sigProtect);
// Do signed endorsing signatures
doEndorsedSignatures(sgndEndSuppTokList, tokenProtect, sigProtect);
}
protected void addSignatureConfirmation(List<WSEncryptionPart> sigParts) {
Wss10 wss10 = getWss10();
if (!(wss10 instanceof Wss11)
|| !((Wss11)wss10).isRequireSignatureConfirmation()) {
//If we don't require sig confirmation simply go back :-)
return;
}
List<WSHandlerResult> results =
CastUtils.cast((List<?>)
message.getExchange().getInMessage().get(WSHandlerConstants.RECV_RESULTS));
/*
* loop over all results gathered by all handlers in the chain. For each
* handler result get the various actions. After that loop we have all
* signature results in the signatureActions list
*/
List<WSSecurityEngineResult> signatureActions = new ArrayList<>();
for (WSHandlerResult wshResult : results) {
if (wshResult.getActionResults().containsKey(WSConstants.SIGN)) {
signatureActions.addAll(wshResult.getActionResults().get(WSConstants.SIGN));
}
if (wshResult.getActionResults().containsKey(WSConstants.UT_SIGN)) {
signatureActions.addAll(wshResult.getActionResults().get(WSConstants.UT_SIGN));
}
}
sigConfList = new ArrayList<>();
// prepare a SignatureConfirmation token
WSSecSignatureConfirmation wsc = new WSSecSignatureConfirmation(secHeader);
wsc.setIdAllocator(wssConfig.getIdAllocator());
if (!signatureActions.isEmpty()) {
for (WSSecurityEngineResult wsr : signatureActions) {
byte[] sigVal = (byte[]) wsr.get(WSSecurityEngineResult.TAG_SIGNATURE_VALUE);
wsc.setSignatureValue(sigVal);
wsc.prepare();
addSupportingElement(wsc.getSignatureConfirmationElement());
if (sigParts != null) {
WSEncryptionPart part = new WSEncryptionPart(wsc.getId(), "Element");
part.setElement(wsc.getSignatureConfirmationElement());
sigParts.add(part);
sigConfList.add(part);
}
}
} else {
//No Sig value
wsc.prepare();
addSupportingElement(wsc.getSignatureConfirmationElement());
if (sigParts != null) {
WSEncryptionPart part = new WSEncryptionPart(wsc.getId(), "Element");
part.setElement(wsc.getSignatureConfirmationElement());
sigParts.add(part);
sigConfList.add(part);
}
}
assertPolicy(
new QName(wss10.getName().getNamespaceURI(), SPConstants.REQUIRE_SIGNATURE_CONFIRMATION));
}
/**
* Processes the parts to be signed and reconfigures those parts that have
* already been encrypted.
*
* @param encryptedParts
* the parts that have been encrypted
* @param signedParts
* the parts that are to be signed
*
* @throws IllegalArgumentException
* if an element in {@code signedParts} contains a {@code
* WSEncryptionPart} with a {@code null} {@code id} value
* and the {@code WSEncryptionPart} {@code name} value is not
* "Token"
*/
public void handleEncryptedSignedHeaders(List<WSEncryptionPart> encryptedParts,
List<WSEncryptionPart> signedParts) {
final List<WSEncryptionPart> signedEncryptedParts = new ArrayList<>();
for (WSEncryptionPart encryptedPart : encryptedParts) {
final Iterator<WSEncryptionPart> signedPartsIt = signedParts.iterator();
while (signedPartsIt.hasNext()) {
WSEncryptionPart signedPart = signedPartsIt.next();
// Everything has to be ID based except for the case of a part
// indicating "Token" as the element name. This name is a flag
// for WSS4J to sign the initiator token used in the signature.
// Since the encryption happened before the signature creation,
// this element can't possibly be encrypted so we can safely ignore
// if it were ever to be set before this method is called.
if (signedPart.getId() == null && !"Token".equals(signedPart.getName())) {
throw new IllegalArgumentException(
"WSEncryptionPart must be ID based but no id was found.");
} else if (encryptedPart.getEncModifier().equals("Header")
&& signedPart.getId().equals(encryptedPart.getId())) {
// We are to sign something that has already been encrypted.
// We need to preserve the original aspects of signedPart but
// change the ID to the encrypted ID.
signedPartsIt.remove();
WSEncryptionPart part = new WSEncryptionPart(
encryptedPart.getEncId(),
encryptedPart.getEncModifier());
part.setElement(encryptedPart.getElement());
signedEncryptedParts.add(part);
}
}
}
signedParts.addAll(signedEncryptedParts);
}
/**
* Convert a DOM Element into a WSEncryptionPart, adding a (wsu:)Id if there is not
* one already.
* @param element The DOM Element to convert
* @return The WSEncryptionPart representing the DOM Element argument
*/
public WSEncryptionPart convertToEncryptionPart(Element element) {
String id = addWsuIdToElement(element);
WSEncryptionPart part = new WSEncryptionPart(id);
part.setElement(element);
return part;
}
static class SupportingToken {
private final AbstractToken token;
private final Object tokenImplementation;
private final List<WSEncryptionPart> signedParts;
SupportingToken(AbstractToken token, Object tokenImplementation,
List<WSEncryptionPart> signedParts) {
this.token = token;
this.tokenImplementation = tokenImplementation;
this.signedParts = signedParts;
}
public AbstractToken getToken() {
return token;
}
public Object getTokenImplementation() {
return tokenImplementation;
}
public List<WSEncryptionPart> getSignedParts() {
return signedParts;
}
}
protected void addSig(byte[] val) {
if (val != null && val.length > 0) {
signatures.add(Arrays.hashCode(val));
}
}
public boolean isExpandXopInclude() {
return expandXopInclude;
}
}