View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.wss4j.stax.impl.processor.output;
20  
21  import java.io.BufferedInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.security.auth.callback.Callback;
30  import javax.security.auth.callback.CallbackHandler;
31  import javax.xml.stream.XMLStreamConstants;
32  import javax.xml.stream.XMLStreamException;
33  import javax.xml.stream.events.Attribute;
34  
35  import org.apache.wss4j.common.ext.Attachment;
36  import org.apache.wss4j.common.ext.AttachmentRequestCallback;
37  import org.apache.wss4j.common.ext.AttachmentResultCallback;
38  import org.apache.wss4j.common.ext.WSSecurityException;
39  import org.apache.wss4j.common.util.AttachmentUtils;
40  import org.apache.wss4j.stax.ext.WSSConstants;
41  import org.apache.wss4j.stax.ext.WSSSecurePart;
42  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
43  import org.apache.wss4j.stax.impl.transformer.AttachmentContentSignatureTransform;
44  import org.apache.wss4j.stax.utils.WSSUtils;
45  import org.apache.xml.security.exceptions.XMLSecurityException;
46  import org.apache.xml.security.stax.ext.OutputProcessorChain;
47  import org.apache.xml.security.stax.ext.SecurePart;
48  import org.apache.xml.security.stax.ext.Transformer;
49  import org.apache.xml.security.stax.ext.XMLSecurityConstants;
50  import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
51  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
52  import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
53  import org.apache.xml.security.stax.impl.SignaturePartDef;
54  import org.apache.xml.security.stax.impl.processor.output.AbstractSignatureOutputProcessor;
55  import org.apache.xml.security.stax.impl.util.DigestOutputStream;
56  import org.apache.xml.security.stax.impl.util.IDGenerator;
57  import org.apache.xml.security.utils.XMLUtils;
58  
59  public class WSSSignatureOutputProcessor extends AbstractSignatureOutputProcessor {
60  
61      private static final org.slf4j.Logger LOG =
62          org.slf4j.LoggerFactory.getLogger(WSSSignatureOutputProcessor.class);
63  
64      public WSSSignatureOutputProcessor() throws XMLSecurityException {
65          super();
66      }
67  
68      @Override
69      public void init(OutputProcessorChain outputProcessorChain) throws XMLSecurityException {
70          super.init(outputProcessorChain);
71          WSSSignatureEndingOutputProcessor signatureEndingOutputProcessor = new WSSSignatureEndingOutputProcessor(this);
72          signatureEndingOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
73          signatureEndingOutputProcessor.setAction(getAction(), getActionOrder());
74          signatureEndingOutputProcessor.init(outputProcessorChain);
75      }
76  
77      @Override
78      public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
79          throws XMLStreamException, XMLSecurityException {
80          if (xmlSecEvent.getEventType() == XMLStreamConstants.START_ELEMENT) {
81              XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
82  
83              //avoid double signature when child elements matches too
84              if (getActiveInternalSignatureOutputProcessor() == null) {
85                  SecurePart securePart = securePartMatches(xmlSecStartElement, outputProcessorChain, WSSConstants.SIGNATURE_PARTS);
86                  if (securePart != null) {
87                      LOG.debug("Matched securePart for signature");
88  
89                      SignaturePartDef signaturePartDef = new SignaturePartDef();
90                      signaturePartDef.setSecurePart(securePart);
91                      signaturePartDef.setTransforms(securePart.getTransforms());
92                      if (signaturePartDef.getTransforms() == null) {
93                          signaturePartDef.setTransforms(new String[]{XMLSecurityConstants.NS_C14N_EXCL_OMIT_COMMENTS});
94                      }
95                      signaturePartDef.setExcludeVisibleC14Nprefixes(true);
96                      signaturePartDef.setDigestAlgo(securePart.getDigestMethod());
97                      if (signaturePartDef.getDigestAlgo() == null) {
98                          signaturePartDef.setDigestAlgo(getSecurityProperties().getSignatureDigestAlgorithm());
99                      }
100 
101                     if (securePart.getIdToSecure() == null) {
102                         signaturePartDef.setGenerateXPointer(securePart.isGenerateXPointer());
103                         signaturePartDef.setSigRefId(IDGenerator.generateID(null));
104 
105                         Attribute attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_WSU_ID);
106                         if (attribute != null) {
107                             signaturePartDef.setSigRefId(attribute.getValue());
108                         } else {
109                             List<XMLSecAttribute> attributeList = new ArrayList<>(1);
110                             attributeList.add(createAttribute(WSSConstants.ATT_WSU_ID, signaturePartDef.getSigRefId()));
111                             xmlSecEvent = addAttributes(xmlSecStartElement, attributeList);
112                         }
113                     } else {
114                         if (WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(securePart.getName().getLocalPart())) {
115                             if (securePart instanceof WSSSecurePart) {
116                                 signaturePartDef.setSigRefId(((WSSSecurePart)securePart).getIdToReference());
117                             }
118                             String[] transforms = new String[]{
119                                     WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM,
120                                     WSSConstants.NS_C14N_EXCL
121                             };
122                             signaturePartDef.setTransforms(transforms);
123                         } else {
124                             signaturePartDef.setSigRefId(securePart.getIdToSecure());
125                         }
126                     }
127 
128                     getSignaturePartDefList().add(signaturePartDef);
129                     InternalSignatureOutputProcessor internalSignatureOutputProcessor =
130                             new InternalWSSSignatureOutputProcessor(signaturePartDef, xmlSecStartElement);
131                     internalSignatureOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
132                     internalSignatureOutputProcessor.setAction(getAction(), getActionOrder());
133                     internalSignatureOutputProcessor.addAfterProcessor(WSSSignatureOutputProcessor.class);
134                     internalSignatureOutputProcessor.addBeforeProcessor(WSSSignatureEndingOutputProcessor.class);
135                     internalSignatureOutputProcessor.init(outputProcessorChain);
136 
137                     setActiveInternalSignatureOutputProcessor(internalSignatureOutputProcessor);
138                     //we can remove this processor when the whole body will be signed since there is
139                     //nothing more which can be signed.
140                     if (WSSConstants.TAG_SOAP_BODY_LN.equals(xmlSecStartElement.getName().getLocalPart())
141                             && WSSUtils.isInSOAPBody(xmlSecStartElement)) {
142                         doFinalInternal(outputProcessorChain);
143                         outputProcessorChain.removeProcessor(this);
144                     }
145                 }
146             }
147         }
148         outputProcessorChain.processEvent(xmlSecEvent);
149     }
150 
151     @Override
152     protected void digestExternalReference(
153             OutputProcessorChain outputProcessorChain, SecurePart securePart)
154             throws XMLSecurityException, XMLStreamException {
155 
156         if (securePart.getExternalReference() != null && securePart.getExternalReference().startsWith("cid:")) {
157 
158             CallbackHandler attachmentCallbackHandler =
159                     ((WSSSecurityProperties) getSecurityProperties()).getAttachmentCallbackHandler();
160             if (attachmentCallbackHandler == null) {
161                 throw new WSSecurityException(
162                         WSSecurityException.ErrorCode.FAILED_SIGNATURE,
163                         "empty",
164                         new Object[] {"no attachment callbackhandler supplied"}
165                 );
166             }
167 
168             AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
169             String id = AttachmentUtils.getAttachmentId(securePart.getExternalReference());
170             attachmentRequestCallback.setAttachmentId(id);
171             try {
172                 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
173             } catch (Exception e) {
174                 throw new WSSecurityException(
175                         WSSecurityException.ErrorCode.FAILED_SIGNATURE, e
176                 );
177             }
178             List<Attachment> attachments = attachmentRequestCallback.getAttachments();
179             if (attachments != null) {
180                 for (int i = 0; i < attachments.size(); i++) {
181                     final Attachment attachment = attachments.get(i);
182 
183                     SignaturePartDef signaturePartDef = new SignaturePartDef();
184                     signaturePartDef.setSecurePart(securePart);
185                     signaturePartDef.setSigRefId("cid:" + attachment.getId());
186                     signaturePartDef.setExternalResource(true);
187                     signaturePartDef.setTransforms(securePart.getTransforms());
188                     if (signaturePartDef.getTransforms() == null) {
189                         if (securePart.getModifier() == SecurePart.Modifier.Element) {
190                             signaturePartDef.setTransforms(new String[]{WSSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS});
191                         } else {
192                             signaturePartDef.setTransforms(new String[]{WSSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS});
193                         }
194                     }
195                     signaturePartDef.setExcludeVisibleC14Nprefixes(true);
196                     signaturePartDef.setDigestAlgo(securePart.getDigestMethod());
197                     if (signaturePartDef.getDigestAlgo() == null) {
198                         signaturePartDef.setDigestAlgo(getSecurityProperties().getSignatureDigestAlgorithm());
199                     }
200 
201                     DigestOutputStream digestOutputStream = createMessageDigestOutputStream(signaturePartDef.getDigestAlgo());  //NOPMD
202                     InputStream inputStream = attachment.getSourceStream(); //NOPMD
203                     if (!inputStream.markSupported()) {
204                         inputStream = new BufferedInputStream(inputStream);
205                     }
206                     inputStream.mark(Integer.MAX_VALUE); //we can process at maximum 2G with the standard jdk streams
207 
208                     try {
209                         Transformer transformer = buildTransformerChain(digestOutputStream, signaturePartDef, null);
210 
211                         Map<String, Object> transformerProperties = new HashMap<>(2);
212                         transformerProperties.put(
213                                 AttachmentContentSignatureTransform.ATTACHMENT, attachment);
214                         transformer.setProperties(transformerProperties);
215                         transformer.transform(inputStream);
216                         transformer.doFinal();
217 
218                         digestOutputStream.close();
219 
220                         //reset the inputStream to be able to reuse it
221                         inputStream.reset();
222                     } catch (IOException | XMLStreamException e) {
223                         throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_SIGNATURE, e);
224                     }
225 
226                     String calculatedDigest = XMLUtils.encodeToString(digestOutputStream.getDigestValue());
227                     LOG.debug("Calculated Digest: {}", calculatedDigest);
228 
229                     signaturePartDef.setDigestValue(calculatedDigest);
230 
231                     //create a new attachment and do the result callback
232                     Attachment resultAttachment = new Attachment();
233                     resultAttachment.setId(attachment.getId());
234                     resultAttachment.setMimeType(attachment.getMimeType());
235                     resultAttachment.addHeaders(attachment.getHeaders());
236                     resultAttachment.setSourceStream(inputStream);
237 
238                     AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
239                     attachmentResultCallback.setAttachmentId(resultAttachment.getId());
240                     attachmentResultCallback.setAttachment(resultAttachment);
241                     try {
242                         attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
243                     } catch (Exception e) {
244                         throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_SIGNATURE, e);
245                     }
246 
247                     getSignaturePartDefList().add(signaturePartDef);
248                 }
249             }
250         } else {
251             super.digestExternalReference(outputProcessorChain, securePart);
252         }
253     }
254 
255     @Override
256     protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
257 
258         if (!xmlSecStartElement.getOnElementDeclaredAttributes().isEmpty()) {
259             Attribute attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_WSU_ID);
260             if (attribute != null) {
261                 SecurePart securePart = secureParts.get(attribute.getValue());
262                 if (securePart != null) {
263                     return securePart;
264                 }
265             }
266             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_Id);
267             if (attribute != null) {
268                 SecurePart securePart = secureParts.get(attribute.getValue());
269                 if (securePart != null) {
270                     return securePart;
271                 }
272             }
273             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ID);
274             if (attribute != null) {
275                 SecurePart securePart = secureParts.get(attribute.getValue());
276                 if (securePart != null) {
277                     return securePart;
278                 }
279             }
280             attribute = xmlSecStartElement.getAttributeByName(WSSConstants.ATT_NULL_ASSERTION_ID);
281             if (attribute != null) {
282                 SecurePart securePart = secureParts.get(attribute.getValue());
283                 if (securePart != null) {
284                     return securePart;
285                 }
286             }
287         }
288 
289         return secureParts.get(xmlSecStartElement.getName());
290     }
291 
292     class InternalWSSSignatureOutputProcessor extends InternalSignatureOutputProcessor {
293 
294         InternalWSSSignatureOutputProcessor(SignaturePartDef signaturePartDef, XMLSecStartElement xmlSecStartElement)
295             throws XMLSecurityException {
296             super(signaturePartDef, xmlSecStartElement);
297             this.addBeforeProcessor(InternalWSSSignatureOutputProcessor.class);
298         }
299     }
300 }