blob: 2da5fa4556217435cd6d3dedeb0a3c9f13519616 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.stax.impl.processor.output;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import org.apache.wss4j.common.ext.Attachment;
import org.apache.wss4j.common.ext.AttachmentRequestCallback;
import org.apache.wss4j.common.ext.AttachmentResultCallback;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.AttachmentUtils;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.ext.WSSSecurePart;
import org.apache.wss4j.stax.ext.WSSSecurityProperties;
import org.apache.wss4j.stax.impl.transformer.AttachmentContentSignatureTransform;
import org.apache.wss4j.stax.utils.WSSUtils;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.ext.OutputProcessorChain;
import org.apache.xml.security.stax.ext.SecurePart;
import org.apache.xml.security.stax.ext.Transformer;
import org.apache.xml.security.stax.ext.XMLSecurityConstants;
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
import org.apache.xml.security.stax.impl.SignaturePartDef;
import org.apache.xml.security.stax.impl.processor.output.AbstractSignatureOutputProcessor;
import org.apache.xml.security.stax.impl.util.DigestOutputStream;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.apache.xml.security.utils.XMLUtils;
public class WSSSignatureOutputProcessor extends AbstractSignatureOutputProcessor {
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(WSSSignatureOutputProcessor.class);
public WSSSignatureOutputProcessor() throws XMLSecurityException {
super();
}
@Override
public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityException {
super.init(outputProcessorChain);
WSSSignatureEndingOutputProcessor signatureEndingOutputProcessor = new WSSSignatureEndingOutputProcessor(this);
signatureEndingOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
signatureEndingOutputProcessor.setAction(getAction());
signatureEndingOutputProcessor.init(outputProcessorChain);
}
@Override
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException {
if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
//avoid double signature when child elements matches too
if (getActiveInternalSignatureOutputProcessor() == null) {
SecurePart securePart = securePartMatches(xmlSecStartElement, outputProcessorChain, WSSConstants.SIGNATURE_PARTS);
if (securePart != null) {
LOG.debug("Matched securePart for signature");
SignaturePartDef signaturePartDef = new SignaturePartDef();
signaturePartDef.setSecurePart(securePart);
signaturePartDef.setTransforms(securePart.getTransforms());
if (signaturePartDef.getTransforms() == null) {
signaturePartDef.setTransforms(new String[]{XMLSecurityConstants.NS_C14N_EXCL_OMIT_COMMENTS});
}
signaturePartDef.setExcludeVisibleC14Nprefixes(true);
signaturePartDef.setDigestAlgo(securePart.getDigestMethod());
if (signaturePartDef.getDigestAlgo() == null) {
signaturePartDef.setDigestAlgo(getSecurityProperties().getSignatureDigestAlgorithm());
}
if (securePart.getIdToSecure() == null) {
signaturePartDef.setGenerateXPointer(securePart.isGenerateXPointer());
signaturePartDef.setSigRefId(IDGenerator.generateID(null));
Attribute attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_WSU_ID);
if (attribute != null) {
signaturePartDef.setSigRefId(attribute.getValue());
} else {
List<XMLSecAttribute> attributeList = new ArrayList<>(1);
attributeList.add(createAttribute(WSSConstants.ATT_WSU_ID, signaturePartDef.getSigRefId()));
xmlSecEvent = addAttributes(xmlSecStartElement, attributeList);
}
} else {
if (WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(securePart.getName().getLocalPart())) {
if (securePart instanceof WSSSecurePart) {
signaturePartDef.setSigRefId(((WSSSecurePart)securePart).getIdToReference());
}
String[] transforms = new String[]{
WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM,
WSSConstants.NS_C14N_EXCL
};
signaturePartDef.setTransforms(transforms);
} else {
signaturePartDef.setSigRefId(securePart.getIdToSecure());
}
}
getSignaturePartDefList().add(signaturePartDef);
InternalSignatureOutputProcessor internalSignatureOutputProcessor =
new InternalWSSSignatureOutputProcessor(signaturePartDef, xmlSecStartElement);
internalSignatureOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
internalSignatureOutputProcessor.setAction(getAction());
internalSignatureOutputProcessor.addAfterProcessor(WSSSignatureOutputProcessor.class.getName());
internalSignatureOutputProcessor.addBeforeProcessor(WSSSignatureEndingOutputProcessor.class.getName());
internalSignatureOutputProcessor.init(outputProcessorChain);
setActiveInternalSignatureOutputProcessor(internalSignatureOutputProcessor);
//we can remove this processor when the whole body will be signed since there is
//nothing more which can be signed.
if (WSSConstants.TAG_SOAP_BODY_LN.equals(xmlSecStartElement.getName().getLocalPart())
&& WSSUtils.isInSOAPBody(xmlSecStartElement)) {
doFinalInternal(outputProcessorChain);
outputProcessorChain.removeProcessor(this);
}
}
}
}
outputProcessorChain.processEvent(xmlSecEvent);
}
@Override
protected void digestExternalReference(
OutputProcessorChain outputProcessorChain, SecurePart securePart)
throws XMLSecurityException, XMLStreamException {
if (securePart.getExternalReference() != null && securePart.getExternalReference().startsWith("cid:")) {
CallbackHandler attachmentCallbackHandler =
((WSSSecurityProperties) getSecurityProperties()).getAttachmentCallbackHandler();
if (attachmentCallbackHandler == null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILED_SIGNATURE,
"empty",
new Object[] {"no attachment callbackhandler supplied"}
);
}
AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
String id = AttachmentUtils.getAttachmentId(securePart.getExternalReference());
attachmentRequestCallback.setAttachmentId(id);
try {
attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
} catch (Exception e) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILED_SIGNATURE, e
);
}
List<Attachment> attachments = attachmentRequestCallback.getAttachments();
if (attachments != null) {
for (int i = 0; i < attachments.size(); i++) {
final Attachment attachment = attachments.get(i);
SignaturePartDef signaturePartDef = new SignaturePartDef();
signaturePartDef.setSecurePart(securePart);
signaturePartDef.setSigRefId("cid:" + attachment.getId());
signaturePartDef.setExternalResource(true);
signaturePartDef.setTransforms(securePart.getTransforms());
if (signaturePartDef.getTransforms() == null) {
if (securePart.getModifier() == SecurePart.Modifier.Element) {
signaturePartDef.setTransforms(new String[]{WSSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS});
} else {
signaturePartDef.setTransforms(new String[]{WSSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS});
}
}
signaturePartDef.setExcludeVisibleC14Nprefixes(true);
signaturePartDef.setDigestAlgo(securePart.getDigestMethod());
if (signaturePartDef.getDigestAlgo() == null) {
signaturePartDef.setDigestAlgo(getSecurityProperties().getSignatureDigestAlgorithm());
}
DigestOutputStream digestOutputStream = createMessageDigestOutputStream(signaturePartDef.getDigestAlgo());
InputStream inputStream = attachment.getSourceStream();
if (!inputStream.markSupported()) {
inputStream = new BufferedInputStream(inputStream);
}
inputStream.mark(Integer.MAX_VALUE); //we can process at maximum 2G with the standard jdk streams
try {
Transformer transformer = buildTransformerChain(digestOutputStream, signaturePartDef, null);
Map<String, Object> transformerProperties = new HashMap<>(2);
transformerProperties.put(
AttachmentContentSignatureTransform.ATTACHMENT, attachment);
transformer.setProperties(transformerProperties);
transformer.transform(inputStream);
transformer.doFinal();
digestOutputStream.close();
//reset the inputStream to be able to reuse it
inputStream.reset();
} catch (IOException | XMLStreamException e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_SIGNATURE, e);
}
String calculatedDigest = XMLUtils.encodeToString(digestOutputStream.getDigestValue());
LOG.debug("Calculated Digest: {}", calculatedDigest);
signaturePartDef.setDigestValue(calculatedDigest);
//create a new attachment and do the result callback
Attachment resultAttachment = new Attachment();
resultAttachment.setId(attachment.getId());
resultAttachment.setMimeType(attachment.getMimeType());
resultAttachment.addHeaders(attachment.getHeaders());
resultAttachment.setSourceStream(inputStream);
AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
attachmentResultCallback.setAttachmentId(resultAttachment.getId());
attachmentResultCallback.setAttachment(resultAttachment);
try {
attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
} catch (Exception e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_SIGNATURE, e);
}
getSignaturePartDefList().add(signaturePartDef);
}
}
} else {
super.digestExternalReference(outputProcessorChain, securePart);
}
}
@Override
protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
if (!xmlSecStartElement.getOnElementDeclaredAttributes().isEmpty()) {
Attribute attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_WSU_ID);
if (attribute != null) {
SecurePart securePart = secureParts.get(attribute.getValue());
if (securePart != null) {
return securePart;
}
}
attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_Id);
if (attribute != null) {
SecurePart securePart = secureParts.get(attribute.getValue());
if (securePart != null) {
return securePart;
}
}
attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ID);
if (attribute != null) {
SecurePart securePart = secureParts.get(attribute.getValue());
if (securePart != null) {
return securePart;
}
}
attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ASSERTION_ID);
if (attribute != null) {
SecurePart securePart = secureParts.get(attribute.getValue());
if (securePart != null) {
return securePart;
}
}
}
return secureParts.get(xmlSecStartElement.getName());
}
class InternalWSSSignatureOutputProcessor extends InternalSignatureOutputProcessor {
InternalWSSSignatureOutputProcessor(SignaturePartDef signaturePartDef, XMLSecStartElement xmlSecStartElement)
throws XMLSecurityException {
super(signaturePartDef, xmlSecStartElement);
this.addBeforeProcessor(InternalWSSSignatureOutputProcessor.class.getName());
}
}
}