/** * 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.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.logging.Level; import javax.xml.crypto.dsig.Reference; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import org.w3c.dom.Element; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.rt.security.utils.SecurityUtils; import org.apache.cxf.ws.policy.AssertionInfo; import org.apache.cxf.ws.policy.AssertionInfoMap; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.cxf.ws.security.wss4j.AttachmentCallbackHandler; import org.apache.cxf.ws.security.wss4j.WSS4JUtils; 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.derivedKey.ConversationConstants; import org.apache.wss4j.common.ext.WSPasswordCallback; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.saml.SamlAssertionWrapper; import org.apache.wss4j.common.token.SecurityTokenReference; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.dom.engine.WSSConfig; 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.WSSecTimestamp; import org.apache.wss4j.dom.message.WSSecUsernameToken; import org.apache.wss4j.policy.SP11Constants; import org.apache.wss4j.policy.SP12Constants; import org.apache.wss4j.policy.SPConstants; import org.apache.wss4j.policy.model.AbstractToken; import org.apache.wss4j.policy.model.AbstractToken.DerivedKeys; import org.apache.wss4j.policy.model.AlgorithmSuite; import org.apache.wss4j.policy.model.AlgorithmSuite.AlgorithmSuiteType; 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.SamlToken; 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.TransportBinding; import org.apache.wss4j.policy.model.TransportToken; import org.apache.wss4j.policy.model.UsernameToken; import org.apache.wss4j.policy.model.X509Token; /** * */ public class TransportBindingHandler extends AbstractBindingBuilder { TransportBinding tbinding; public TransportBindingHandler(WSSConfig config, TransportBinding binding, SOAPMessage saaj, WSSecHeader secHeader, AssertionInfoMap aim, SoapMessage message) throws SOAPException { super(config, binding, saaj, secHeader, aim, message); this.tbinding = binding; } private void addSignedSupportingTokens(SupportingTokens sgndSuppTokens) throws Exception { for (AbstractToken token : sgndSuppTokens.getTokens()) { assertToken(token); if (token != null && !isTokenRequired(token.getIncludeTokenType())) { continue; } if (token instanceof UsernameToken) { WSSecUsernameToken utBuilder = addUsernameToken((UsernameToken)token); if (utBuilder != null) { utBuilder.prepare(); utBuilder.appendToHeader(); } } else if (token instanceof IssuedToken || token instanceof KerberosToken || token instanceof SpnegoContextToken) { SecurityToken secTok = getSecurityToken(); if (isTokenRequired(token.getIncludeTokenType())) { //Add the token addEncryptedKeyElement(cloneElement(secTok.getToken())); } } else if (token instanceof SamlToken) { SamlAssertionWrapper assertionWrapper = addSamlToken((SamlToken)token); if (assertionWrapper != null) { Element envelope = saaj.getSOAPPart().getEnvelope(); envelope = (Element)DOMUtils.getDomElement(envelope); addSupportingElement(assertionWrapper.toDOM(envelope.getOwnerDocument())); } } else { //REVISIT - not supported for signed. Exception? } } } public void handleBinding() { WSSecTimestamp timestamp = createTimestamp(); handleLayout(timestamp); try { if (this.isRequestor()) { TransportToken transportTokenWrapper = tbinding.getTransportToken(); if (transportTokenWrapper != null) { AbstractToken transportToken = transportTokenWrapper.getToken(); if (transportToken instanceof IssuedToken) { SecurityToken secToken = getSecurityToken(); if (secToken == null) { unassertPolicy(transportToken, "No transport token id"); return; } else { assertPolicy(transportToken); } if (isTokenRequired(transportToken.getIncludeTokenType())) { Element el = secToken.getToken(); addEncryptedKeyElement(cloneElement(el)); } } assertToken(transportToken); assertTokenWrapper(transportTokenWrapper); } handleNonEndorsingSupportingTokens(); if (transportTokenWrapper != null) { handleEndorsingSupportingTokens(); } } else { handleNonEndorsingSupportingTokens(); if (tbinding != null && tbinding.getTransportToken() != null) { assertTokenWrapper(tbinding.getTransportToken()); assertToken(tbinding.getTransportToken().getToken()); handleEndorsingSupportingTokens(); } addSignatureConfirmation(null); } } catch (Exception e) { LOG.log(Level.FINE, e.getMessage(), e); throw new Fault(e); } if (tbinding != null) { assertPolicy(tbinding.getName()); assertAlgorithmSuite(tbinding.getAlgorithmSuite()); assertWSSProperties(tbinding.getName().getNamespaceURI()); assertTrustProperties(tbinding.getName().getNamespaceURI()); } assertPolicy(SP12Constants.SIGNED_PARTS); assertPolicy(SP11Constants.SIGNED_PARTS); assertPolicy(SP12Constants.ENCRYPTED_PARTS); assertPolicy(SP11Constants.ENCRYPTED_PARTS); } /** * Handle the non-endorsing supporting tokens */ private void handleNonEndorsingSupportingTokens() throws Exception { Collection<AssertionInfo> ais; ais = getAllAssertionsByLocalname(SPConstants.SIGNED_SUPPORTING_TOKENS); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { SupportingTokens sgndSuppTokens = (SupportingTokens)ai.getAssertion(); if (sgndSuppTokens != null) { addSignedSupportingTokens(sgndSuppTokens); } ai.setAsserted(true); } } ais = getAllAssertionsByLocalname(SPConstants.SIGNED_ENCRYPTED_SUPPORTING_TOKENS); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { SupportingTokens sgndSuppTokens = (SupportingTokens)ai.getAssertion(); if (sgndSuppTokens != null) { addSignedSupportingTokens(sgndSuppTokens); } ai.setAsserted(true); } } ais = getAllAssertionsByLocalname(SPConstants.ENCRYPTED_SUPPORTING_TOKENS); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { SupportingTokens encrSuppTokens = (SupportingTokens)ai.getAssertion(); if (encrSuppTokens != null) { addSignedSupportingTokens(encrSuppTokens); } ai.setAsserted(true); } } ais = getAllAssertionsByLocalname(SPConstants.SUPPORTING_TOKENS); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { SupportingTokens suppTokens = (SupportingTokens)ai.getAssertion(); if (suppTokens != null && suppTokens.getTokens() != null && suppTokens.getTokens().size() > 0) { handleSupportingTokens(suppTokens, false, new ArrayList<>()); } ai.setAsserted(true); } } } /** * Handle the endorsing supporting tokens */ private void handleEndorsingSupportingTokens() throws Exception { Collection<AssertionInfo> ais; ais = getAllAssertionsByLocalname(SPConstants.SIGNED_ENDORSING_SUPPORTING_TOKENS); if (!ais.isEmpty()) { SupportingTokens sgndSuppTokens = null; for (AssertionInfo ai : ais) { sgndSuppTokens = (SupportingTokens)ai.getAssertion(); ai.setAsserted(true); } if (sgndSuppTokens != null) { for (AbstractToken token : sgndSuppTokens.getTokens()) { handleEndorsingToken(token, sgndSuppTokens); } } } ais = getAllAssertionsByLocalname(SPConstants.ENDORSING_SUPPORTING_TOKENS); if (!ais.isEmpty()) { SupportingTokens endSuppTokens = null; for (AssertionInfo ai : ais) { endSuppTokens = (SupportingTokens)ai.getAssertion(); ai.setAsserted(true); } if (endSuppTokens != null) { for (AbstractToken token : endSuppTokens.getTokens()) { handleEndorsingToken(token, endSuppTokens); } } } ais = getAllAssertionsByLocalname(SPConstants.ENDORSING_ENCRYPTED_SUPPORTING_TOKENS); if (!ais.isEmpty()) { SupportingTokens endSuppTokens = null; for (AssertionInfo ai : ais) { endSuppTokens = (SupportingTokens)ai.getAssertion(); ai.setAsserted(true); } if (endSuppTokens != null) { for (AbstractToken token : endSuppTokens.getTokens()) { handleEndorsingToken(token, endSuppTokens); } } } ais = getAllAssertionsByLocalname(SPConstants.SIGNED_ENDORSING_ENCRYPTED_SUPPORTING_TOKENS); if (!ais.isEmpty()) { SupportingTokens endSuppTokens = null; for (AssertionInfo ai : ais) { endSuppTokens = (SupportingTokens)ai.getAssertion(); ai.setAsserted(true); } if (endSuppTokens != null) { for (AbstractToken token : endSuppTokens.getTokens()) { handleEndorsingToken(token, endSuppTokens); } } } } private void handleEndorsingToken( AbstractToken token, SupportingTokens wrapper ) throws Exception { assertToken(token); if (token != null && !isTokenRequired(token.getIncludeTokenType())) { return; } if (token instanceof IssuedToken || token instanceof SecureConversationToken || token instanceof SecurityContextToken || token instanceof KerberosToken || token instanceof SpnegoContextToken) { addSig(doIssuedTokenSignature(token, wrapper)); } else if (token instanceof X509Token || token instanceof KeyValueToken) { addSig(doX509TokenSignature(token, wrapper)); } else if (token instanceof SamlToken) { SamlAssertionWrapper assertionWrapper = addSamlToken((SamlToken)token); Element envelope = saaj.getSOAPPart().getEnvelope(); envelope = (Element)DOMUtils.getDomElement(envelope); assertionWrapper.toDOM(envelope.getOwnerDocument()); storeAssertionAsSecurityToken(assertionWrapper); addSig(doIssuedTokenSignature(token, wrapper)); } else if (token instanceof UsernameToken) { // Create a UsernameToken object for derived keys and store the security token WSSecUsernameToken usernameToken = addDKUsernameToken((UsernameToken)token, true); String id = usernameToken.getId(); byte[] secret = usernameToken.getDerivedKey(); Instant created = Instant.now(); Instant expires = created.plusSeconds(WSS4JUtils.getSecurityTokenLifetime(message) / 1000L); SecurityToken tempTok = new SecurityToken(id, usernameToken.getUsernameTokenElement(), created, expires); tempTok.setSecret(secret); getTokenStore().add(tempTok); message.put(SecurityConstants.TOKEN_ID, tempTok.getId()); addSig(doIssuedTokenSignature(token, wrapper)); } } private byte[] doX509TokenSignature(AbstractToken token, SupportingTokens wrapper) throws Exception { List<WSEncryptionPart> sigParts = signPartsAndElements(wrapper.getSignedParts(), wrapper.getSignedElements()); if (token.getDerivedKeys() == DerivedKeys.RequireDerivedKeys) { WSSecEncryptedKey encrKey = getEncryptedKeyBuilder(token); assertPolicy(wrapper); Element bstElem = encrKey.getBinarySecurityTokenElement(); if (bstElem != null) { addTopDownElement(bstElem); } encrKey.appendToHeader(); WSSecDKSign dkSig = new WSSecDKSign(secHeader); dkSig.setIdAllocator(wssConfig.getIdAllocator()); dkSig.setCallbackLookup(callbackLookup); if (token.getVersion() == SPConstants.SPVersion.SP11) { dkSig.setWscVersion(ConversationConstants.VERSION_05_02); } dkSig.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue()); dkSig.setSignatureAlgorithm(binding.getAlgorithmSuite().getSymmetricSignature()); dkSig.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message)); dkSig.setStoreBytesInAttachment(storeBytesInAttachment); dkSig.setExpandXopInclude(isExpandXopInclude()); dkSig.setWsDocInfo(wsDocInfo); AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType(); dkSig.setDerivedKeyLength(algType.getSignatureDerivedKeyLength() / 8); dkSig.setExternalKey(encrKey.getEphemeralKey(), encrKey.getId()); dkSig.prepare(); dkSig.getParts().addAll(sigParts); List<Reference> referenceList = dkSig.addReferencesToSign(sigParts); //Do signature dkSig.appendDKElementToHeader(); dkSig.computeSignature(referenceList, false, null); return dkSig.getSignatureValue(); } else { WSSecSignature sig = getSignatureBuilder(token, false, false); assertPolicy(wrapper); if (sig != null) { sig.prependBSTElementToHeader(); List<Reference> referenceList = sig.addReferencesToSign(sigParts); if (bottomUpElement == null) { sig.computeSignature(referenceList, false, null); } else { sig.computeSignature(referenceList, true, bottomUpElement); } bottomUpElement = sig.getSignatureElement(); mainSigId = sig.getId(); return sig.getSignatureValue(); } else { return new byte[0]; } } } private byte[] doIssuedTokenSignature( AbstractToken token, SupportingTokens wrapper ) throws Exception { boolean tokenIncluded = false; // Get the issued token SecurityToken secTok = getSecurityToken(); if (secTok == null) { LOG.fine("The retrieved SecurityToken was null"); Exception ex = new Exception("The retrieved SecurityToken was null"); throw new WSSecurityException( WSSecurityException.ErrorCode.FAILURE, ex ); } if (isTokenRequired(token.getIncludeTokenType())) { //Add the token Element el = cloneElement(secTok.getToken()); //if (securityTok != null) { //do we need to sign this as well? //String id = addWsuIdToElement(el); //sigParts.add(new WSEncryptionPart(id)); //} addEncryptedKeyElement(el); tokenIncluded = true; } List<WSEncryptionPart> sigParts = signPartsAndElements(wrapper.getSignedParts(), wrapper.getSignedElements()); if (token.getDerivedKeys() == DerivedKeys.RequireDerivedKeys) { return doDerivedKeySignature(tokenIncluded, secTok, token, sigParts); } else { return doSignature(tokenIncluded, secTok, token, wrapper, sigParts); } } private byte[] doDerivedKeySignature( boolean tokenIncluded, SecurityToken secTok, AbstractToken token, List<WSEncryptionPart> sigParts ) throws Exception { //Do Signature with derived keys WSSecDKSign dkSign = new WSSecDKSign(secHeader); dkSign.setIdAllocator(wssConfig.getIdAllocator()); dkSign.setCallbackLookup(callbackLookup); dkSign.setAttachmentCallbackHandler(new AttachmentCallbackHandler(message)); dkSign.setStoreBytesInAttachment(storeBytesInAttachment); dkSign.setExpandXopInclude(isExpandXopInclude()); dkSign.setWsDocInfo(wsDocInfo); AlgorithmSuite algorithmSuite = tbinding.getAlgorithmSuite(); //Setting the AttachedReference or the UnattachedReference according to the flag Element ref; if (tokenIncluded) { ref = secTok.getAttachedReference(); } else { ref = secTok.getUnattachedReference(); } if (ref != null) { dkSign.setExternalKey(secTok.getSecret(), cloneElement(ref)); } else { dkSign.setExternalKey(secTok.getSecret(), secTok.getId()); } if (token instanceof UsernameToken) { dkSign.setCustomValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE); } // Set the algo info dkSign.setSignatureAlgorithm(algorithmSuite.getSymmetricSignature()); AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType(); dkSign.setDerivedKeyLength(algType.getSignatureDerivedKeyLength() / 8); if (token.getVersion() == SPConstants.SPVersion.SP11) { dkSign.setWscVersion(ConversationConstants.VERSION_05_02); } dkSign.prepare(); addDerivedKeyElement(dkSign.getdktElement()); dkSign.getParts().addAll(sigParts); List<Reference> referenceList = dkSign.addReferencesToSign(sigParts); //Do signature dkSign.computeSignature(referenceList, false, null); return dkSign.getSignatureValue(); } private byte[] doSignature( boolean tokenIncluded, SecurityToken secTok, AbstractToken token, SupportingTokens wrapper, List<WSEncryptionPart> sigParts ) throws Exception { 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); //Setting the AttachedReference or the UnattachedReference according to the flag Element ref; if (tokenIncluded) { ref = secTok.getAttachedReference(); } else { ref = secTok.getUnattachedReference(); } if (ref != null) { SecurityTokenReference secRef = new SecurityTokenReference(cloneElement(ref), new BSPEnforcer()); sig.setSecurityTokenReference(secRef); sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER); } else if (token instanceof UsernameToken) { sig.setCustomTokenId(secTok.getId()); sig.setCustomTokenValueType(WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE); int type = tokenIncluded ? WSConstants.CUSTOM_SYMM_SIGNING : WSConstants.CUSTOM_SYMM_SIGNING_DIRECT; sig.setKeyIdentifierType(type); } else if (secTok.getTokenType() == null) { sig.setCustomTokenValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE); sig.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER); } else { String id = secTok.getWsuId(); if (id == null) { sig.setCustomTokenId(secTok.getId()); sig.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING_DIRECT); } else { sig.setCustomTokenId(secTok.getWsuId()); sig.setKeyIdentifierType(WSConstants.CUSTOM_SYMM_SIGNING); } String tokenType = secTok.getTokenType(); 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); } } Crypto crypto = null; if (secTok.getSecret() == null) { sig.setX509Certificate(secTok.getX509Certificate()); crypto = secTok.getCrypto(); if (crypto == null) { crypto = getSignatureCrypto(); } if (crypto == null) { LOG.fine("No signature Crypto properties are available"); Exception ex = new Exception("No signature Crypto properties are available"); throw new WSSecurityException( WSSecurityException.ErrorCode.FAILURE, ex ); } String uname = crypto.getX509Identifier(secTok.getX509Certificate()); if (uname == null) { String userNameKey = SecurityConstants.SIGNATURE_USERNAME; uname = (String)SecurityUtils.getSecurityPropertyValue(userNameKey, message); } String password = getPassword(uname, token, WSPasswordCallback.SIGNATURE); sig.setUserInfo(uname, password); sig.setSignatureAlgorithm(binding.getAlgorithmSuite().getAsymmetricSignature()); } else { crypto = getSignatureCrypto(); sig.setSecretKey(secTok.getSecret()); sig.setSignatureAlgorithm(binding.getAlgorithmSuite().getSymmetricSignature()); } sig.setSigCanonicalization(binding.getAlgorithmSuite().getC14n().getValue()); AlgorithmSuiteType algType = binding.getAlgorithmSuite().getAlgorithmSuiteType(); sig.setDigestAlgo(algType.getDigest()); sig.prepare(crypto); sig.getParts().addAll(sigParts); List<Reference> referenceList = sig.addReferencesToSign(sigParts); //Do signature if (bottomUpElement == null) { sig.computeSignature(referenceList, false, null); } else { sig.computeSignature(referenceList, true, bottomUpElement); } bottomUpElement = sig.getSignatureElement(); mainSigId = sig.getId(); return sig.getSignatureValue(); } /** * Identifies the portions of the message to be signed/encrypted. */ private List<WSEncryptionPart> signPartsAndElements( SignedParts signedParts, SignedElements signedElements ) throws SOAPException { List<WSEncryptionPart> result = new ArrayList<>(); List<Element> found = new ArrayList<>(); // Add timestamp if (timestampEl != null) { WSEncryptionPart timestampPart = new WSEncryptionPart("Timestamp", WSConstants.WSU_NS, "Element"); String id = addWsuIdToElement(timestampEl.getElement()); timestampPart.setId(id); timestampPart.setElement(timestampEl.getElement()); found.add(timestampPart.getElement()); result.add(timestampPart); } // Add SignedParts if (signedParts != null) { List<WSEncryptionPart> parts = new ArrayList<>(); boolean isSignBody = signedParts.isBody(); for (Header head : signedParts.getHeaders()) { WSEncryptionPart wep = new WSEncryptionPart(head.getName(), head.getNamespace(), "Element"); parts.add(wep); } // Handle sign/enc parts result.addAll(this.getParts(true, isSignBody, parts, found)); } if (signedElements != null) { // Handle SignedElements result.addAll( this.getElements("Element", signedElements.getXPaths(), found, true) ); } return result; } }