/**
* 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.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.rt.security.utils.SecurityUtils;
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.tokenstore.TokenStoreUtils;
import org.apache.cxf.ws.security.wss4j.TokenStoreCallbackHandler;
import org.apache.wss4j.common.ConfigurationConstants;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.policy.SPConstants;
import org.apache.wss4j.policy.SPConstants.IncludeTokenType;
import org.apache.wss4j.policy.model.AbstractSymmetricAsymmetricBinding;
import org.apache.wss4j.policy.model.AbstractToken;
import org.apache.wss4j.policy.model.AbstractToken.DerivedKeys;
import org.apache.wss4j.policy.model.AbstractTokenWrapper;
import org.apache.wss4j.policy.model.AlgorithmSuite;
import org.apache.wss4j.policy.model.AsymmetricBinding;
import org.apache.wss4j.policy.model.IssuedToken;
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.SpnegoContextToken;
import org.apache.wss4j.policy.model.X509Token;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.ext.WSSSecurityProperties;
import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
import org.apache.xml.security.stax.ext.OutboundSecurityContext;
import org.apache.xml.security.stax.ext.SecurePart;
import org.apache.xml.security.stax.ext.SecurePart.Modifier;
/**
*
*/
public class StaxAsymmetricBindingHandler extends AbstractStaxBindingHandler {
private static final Logger LOG = LogUtils.getL7dLogger(StaxAsymmetricBindingHandler.class);
private AsymmetricBinding abinding;
private SoapMessage message;
public StaxAsymmetricBindingHandler(
WSSSecurityProperties properties,
SoapMessage msg,
AsymmetricBinding abinding,
OutboundSecurityContext outboundSecurityContext
) {
super(properties, msg, abinding, outboundSecurityContext);
this.message = msg;
this.abinding = abinding;
}
public void handleBinding() {
AssertionInfoMap aim = getMessage().get(AssertionInfoMap.class);
configureTimestamp(aim);
assertPolicy(abinding.getName());
String asymSignatureAlgorithm =
(String)getMessage().getContextualProperty(SecurityConstants.ASYMMETRIC_SIGNATURE_ALGORITHM);
if (asymSignatureAlgorithm != null && abinding.getAlgorithmSuite() != null) {
abinding.getAlgorithmSuite().setAsymmetricSignature(asymSignatureAlgorithm);
}
String symSignatureAlgorithm =
(String)getMessage().getContextualProperty(SecurityConstants.SYMMETRIC_SIGNATURE_ALGORITHM);
if (symSignatureAlgorithm != null && abinding.getAlgorithmSuite() != null) {
abinding.getAlgorithmSuite().setSymmetricSignature(symSignatureAlgorithm);
}
if (abinding.getProtectionOrder()
== AbstractSymmetricAsymmetricBinding.ProtectionOrder.EncryptBeforeSigning) {
doEncryptBeforeSign();
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.ENCRYPT_BEFORE_SIGNING));
} else {
doSignBeforeEncrypt();
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.SIGN_BEFORE_ENCRYPTING));
}
configureLayout(aim);
assertAlgorithmSuite(abinding.getAlgorithmSuite());
assertWSSProperties(abinding.getName().getNamespaceURI());
assertTrustProperties(abinding.getName().getNamespaceURI());
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.ONLY_SIGN_ENTIRE_HEADERS_AND_BODY));
if (abinding.isProtectTokens()) {
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.PROTECT_TOKENS));
}
}
private void doSignBeforeEncrypt() {
try {
AbstractTokenWrapper initiatorWrapper = abinding.getInitiatorSignatureToken();
if (initiatorWrapper == null) {
initiatorWrapper = abinding.getInitiatorToken();
}
if (initiatorWrapper != null) {
assertTokenWrapper(initiatorWrapper);
AbstractToken initiatorToken = initiatorWrapper.getToken();
if (initiatorToken instanceof IssuedToken) {
SecurityToken sigTok = getSecurityToken();
addIssuedToken((IssuedToken)initiatorToken, sigTok, false, true);
if (sigTok != null) {
storeSecurityToken(initiatorToken, sigTok);
outboundSecurityContext.remove(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION);
}
// Set up CallbackHandler which wraps the configured Handler
WSSSecurityProperties properties = getProperties();
TokenStoreCallbackHandler callbackHandler =
new TokenStoreCallbackHandler(
properties.getCallbackHandler(), TokenStoreUtils.getTokenStore(message)
);
properties.setCallbackHandler(callbackHandler);
} else if (initiatorToken instanceof SamlToken) {
addSamlToken((SamlToken)initiatorToken, false, true);
}
assertToken(initiatorToken);
}
// Add timestamp
List<SecurePart> sigs = new ArrayList<>();
if (timestampAdded) {
SecurePart part =
new SecurePart(new QName(WSSConstants.NS_WSU10, "Timestamp"), Modifier.Element);
sigs.add(part);
}
sigs.addAll(this.getSignedParts());
if (isRequestor() && initiatorWrapper != null) {
doSignature(initiatorWrapper, sigs);
} else if (!isRequestor()) {
//confirm sig
addSignatureConfirmation(sigs);
AbstractTokenWrapper recipientSignatureToken = abinding.getRecipientSignatureToken();
if (recipientSignatureToken == null) {
recipientSignatureToken = abinding.getRecipientToken();
}
if (recipientSignatureToken != null) {
assertTokenWrapper(recipientSignatureToken);
assertToken(recipientSignatureToken.getToken());
}
if (recipientSignatureToken != null && !sigs.isEmpty()) {
doSignature(recipientSignatureToken, sigs);
}
}
addSupportingTokens();
removeSignatureIfSignedSAML();
prependSignatureToSC();
List<SecurePart> enc = getEncryptedParts();
//Check for signature protection
if (abinding.isEncryptSignature()) {
SecurePart part =
new SecurePart(new QName(WSSConstants.NS_DSIG, "Signature"), Modifier.Element);
enc.add(part);
if (signatureConfirmationAdded) {
SecurePart securePart =
new SecurePart(WSSConstants.TAG_WSSE11_SIG_CONF, Modifier.Element);
enc.add(securePart);
}
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.ENCRYPT_SIGNATURE));
}
//Do encryption
AbstractTokenWrapper encToken;
if (isRequestor()) {
enc.addAll(encryptedTokensList);
encToken = abinding.getRecipientEncryptionToken();
if (encToken == null) {
encToken = abinding.getRecipientToken();
}
} else {
encToken = abinding.getInitiatorEncryptionToken();
if (encToken == null) {
encToken = abinding.getInitiatorToken();
}
}
if (encToken != null) {
assertTokenWrapper(encToken);
assertToken(encToken.getToken());
}
doEncryption(encToken, enc, false);
putCustomTokenAfterSignature();
} catch (Exception e) {
String reason = e.getMessage();
LOG.log(Level.WARNING, "Sign before encryption failed due to : " + reason);
throw new Fault(e);
}
}
private void doEncryptBeforeSign() {
try {
AbstractTokenWrapper wrapper;
AbstractToken encryptionToken = null;
if (isRequestor()) {
wrapper = abinding.getRecipientEncryptionToken();
if (wrapper == null) {
wrapper = abinding.getRecipientToken();
}
} else {
wrapper = abinding.getInitiatorEncryptionToken();
if (wrapper == null) {
wrapper = abinding.getInitiatorToken();
}
}
assertTokenWrapper(wrapper);
if (wrapper != null) {
encryptionToken = wrapper.getToken();
assertToken(encryptionToken);
}
AbstractTokenWrapper initiatorWrapper = abinding.getInitiatorSignatureToken();
if (initiatorWrapper == null) {
initiatorWrapper = abinding.getInitiatorToken();
}
if (initiatorWrapper != null) {
assertTokenWrapper(initiatorWrapper);
AbstractToken initiatorToken = initiatorWrapper.getToken();
if (initiatorToken instanceof IssuedToken) {
SecurityToken sigTok = getSecurityToken();
addIssuedToken((IssuedToken)initiatorToken, sigTok, false, true);
if (sigTok != null) {
storeSecurityToken(initiatorToken, sigTok);
outboundSecurityContext.remove(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION);
}
// Set up CallbackHandler which wraps the configured Handler
WSSSecurityProperties properties = getProperties();
TokenStoreCallbackHandler callbackHandler =
new TokenStoreCallbackHandler(
properties.getCallbackHandler(), TokenStoreUtils.getTokenStore(message)
);
properties.setCallbackHandler(callbackHandler);
} else if (initiatorToken instanceof SamlToken) {
addSamlToken((SamlToken)initiatorToken, false, true);
}
}
List<SecurePart> encrParts = null;
List<SecurePart> sigParts = null;
try {
encrParts = getEncryptedParts();
//Signed parts are determined before encryption because encrypted signed headers
//will not be included otherwise
sigParts = getSignedParts();
} catch (SOAPException ex) {
throw new Fault(ex);
}
addSupportingTokens();
if (encryptionToken != null && !encrParts.isEmpty()) {
if (isRequestor()) {
encrParts.addAll(encryptedTokensList);
} else {
addSignatureConfirmation(sigParts);
}
//Check for signature protection
if (abinding.isEncryptSignature()) {
SecurePart part =
new SecurePart(new QName(WSSConstants.NS_DSIG, "Signature"), Modifier.Element);
encrParts.add(part);
if (signatureConfirmationAdded) {
SecurePart securePart =
new SecurePart(WSSConstants.TAG_WSSE11_SIG_CONF, Modifier.Element);
encrParts.add(securePart);
}
assertPolicy(
new QName(abinding.getName().getNamespaceURI(), SPConstants.ENCRYPT_SIGNATURE));
}
doEncryption(wrapper, encrParts, true);
}
if (timestampAdded) {
SecurePart part =
new SecurePart(new QName(WSSConstants.NS_WSU10, "Timestamp"), Modifier.Element);
sigParts.add(part);
}
if (!sigParts.isEmpty()) {
if (initiatorWrapper != null && isRequestor()) {
doSignature(initiatorWrapper, sigParts);
} else if (!isRequestor()) {
AbstractTokenWrapper recipientSignatureToken = abinding.getRecipientSignatureToken();
if (recipientSignatureToken == null) {
recipientSignatureToken = abinding.getRecipientToken();
}
if (recipientSignatureToken != null) {
assertTokenWrapper(recipientSignatureToken);
assertToken(recipientSignatureToken.getToken());
doSignature(recipientSignatureToken, sigParts);
}
}
}
removeSignatureIfSignedSAML();
enforceEncryptBeforeSigningWithSignedSAML();
prependSignatureToSC();
putCustomTokenAfterSignature();
} catch (Exception e) {
String reason = e.getMessage();
LOG.log(Level.WARNING, "Encrypt before signing failed due to : " + reason);
throw new Fault(e);
}
}
private void doEncryption(AbstractTokenWrapper recToken,
List<SecurePart> encrParts,
boolean externalRef) throws SOAPException {
//Do encryption
if (recToken != null && recToken.getToken() != null && !encrParts.isEmpty()) {
AbstractToken encrToken = recToken.getToken();
AlgorithmSuite algorithmSuite = abinding.getAlgorithmSuite();
// Action
WSSSecurityProperties properties = getProperties();
WSSConstants.Action actionToPerform = WSSConstants.ENCRYPT;
if (recToken.getToken().getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
actionToPerform = WSSConstants.ENCRYPT_WITH_DERIVED_KEY;
}
properties.addAction(actionToPerform);
properties.getEncryptionSecureParts().addAll(encrParts);
properties.setEncryptionKeyIdentifier(getKeyIdentifierType(encrToken));
// Find out do we also need to include the token as per the Inclusion requirement
WSSecurityTokenConstants.KeyIdentifier keyIdentifier = properties.getEncryptionKeyIdentifier();
if (encrToken instanceof X509Token
&& isTokenRequired(encrToken.getIncludeTokenType())
&& (WSSecurityTokenConstants.KeyIdentifier_IssuerSerial.equals(keyIdentifier)
|| WSSecurityTokenConstants.KEYIDENTIFIER_THUMBPRINT_IDENTIFIER.equals(keyIdentifier)
|| WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE.equals(
keyIdentifier))) {
properties.setIncludeEncryptionToken(true);
} else {
properties.setIncludeEncryptionToken(false);
}
properties.setEncryptionKeyTransportAlgorithm(
algorithmSuite.getAlgorithmSuiteType().getAsymmetricKeyWrap());
properties.setEncryptionSymAlgorithm(
algorithmSuite.getAlgorithmSuiteType().getEncryption());
properties.setEncryptionKeyTransportDigestAlgorithm(
algorithmSuite.getAlgorithmSuiteType().getEncryptionDigest());
properties.setEncryptionKeyTransportMGFAlgorithm(
algorithmSuite.getAlgorithmSuiteType().getMGFAlgo());
String encUser =
(String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_USERNAME, message);
if (encUser == null) {
encUser = (String)SecurityUtils.getSecurityPropertyValue(SecurityConstants.USERNAME, message);
}
if (encUser != null && properties.getEncryptionUser() == null) {
properties.setEncryptionUser(encUser);
}
if (ConfigurationConstants.USE_REQ_SIG_CERT.equals(encUser)) {
properties.setUseReqSigCertForEncryption(true);
}
//
// Using a stored cert is only suitable for the Issued Token case, where
// we're extracting the cert from a SAML Assertion on the provider side
//
if (!isRequestor() && recToken.getToken() instanceof IssuedToken) {
properties.setUseReqSigCertForEncryption(true);
}
}
}
private void doSignature(AbstractTokenWrapper wrapper, List<SecurePart> sigParts)
throws WSSecurityException, SOAPException {
// Action
WSSSecurityProperties properties = getProperties();
WSSConstants.Action actionToPerform = WSSConstants.SIGNATURE;
if (wrapper.getToken().getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
actionToPerform = WSSConstants.SIGNATURE_WITH_DERIVED_KEY;
}
List<WSSConstants.Action> actionList = properties.getActions();
// Add a Signature directly before Kerberos, otherwise just append it
boolean actionAdded = false;
for (int i = 0; i < actionList.size(); i++) {
WSSConstants.Action action = actionList.get(i);
if (action.equals(WSSConstants.KERBEROS_TOKEN)) {
actionList.add(i, actionToPerform);
actionAdded = true;
break;
}
}
if (!actionAdded) {
actionList.add(actionToPerform);
}
properties.getSignatureSecureParts().addAll(sigParts);
AbstractToken sigToken = wrapper.getToken();
configureSignature(sigToken, false);
if (abinding.isProtectTokens() && (sigToken instanceof X509Token)
&& sigToken.getIncludeTokenType() != IncludeTokenType.INCLUDE_TOKEN_NEVER) {
SecurePart securePart =
new SecurePart(new QName(WSSConstants.NS_WSSE10, "BinarySecurityToken"), Modifier.Element);
properties.addSignaturePart(securePart);
} else if (sigToken instanceof IssuedToken || sigToken instanceof SecurityContextToken
|| sigToken instanceof SecureConversationToken || sigToken instanceof SpnegoContextToken
|| sigToken instanceof SamlToken) {
properties.setIncludeSignatureToken(false);
}
if (sigToken.getDerivedKeys() == DerivedKeys.RequireDerivedKeys) {
properties.setSignatureAlgorithm(
abinding.getAlgorithmSuite().getSymmetricSignature());
}
}
}