1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.wss4j.stax.impl.processor.input;
20
21 import java.io.BufferedInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.time.Instant;
26 import java.time.temporal.ChronoField;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 import javax.security.auth.callback.Callback;
34 import javax.security.auth.callback.CallbackHandler;
35 import javax.xml.namespace.QName;
36 import javax.xml.stream.XMLStreamException;
37
38 import org.apache.wss4j.binding.wss10.TransformationParametersType;
39 import org.apache.wss4j.common.bsp.BSPRule;
40 import org.apache.wss4j.common.cache.ReplayCache;
41 import org.apache.wss4j.common.ext.Attachment;
42 import org.apache.wss4j.common.ext.AttachmentRequestCallback;
43 import org.apache.wss4j.common.ext.AttachmentResultCallback;
44 import org.apache.wss4j.common.ext.WSSecurityException;
45 import org.apache.wss4j.common.util.AttachmentUtils;
46 import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
47 import org.apache.wss4j.stax.ext.WSSConstants;
48 import org.apache.wss4j.stax.ext.WSSSecurityProperties;
49 import org.apache.wss4j.stax.impl.transformer.AttachmentContentSignatureTransform;
50 import org.apache.wss4j.stax.securityEvent.SignedPartSecurityEvent;
51 import org.apache.wss4j.stax.securityEvent.TimestampSecurityEvent;
52 import org.apache.wss4j.stax.securityToken.SecurityTokenReference;
53 import org.apache.wss4j.stax.utils.WSSUtils;
54 import org.apache.xml.security.binding.excc14n.InclusiveNamespaces;
55 import org.apache.xml.security.binding.xmldsig.CanonicalizationMethodType;
56 import org.apache.xml.security.binding.xmldsig.ReferenceType;
57 import org.apache.xml.security.binding.xmldsig.SignatureType;
58 import org.apache.xml.security.binding.xmldsig.TransformType;
59 import org.apache.xml.security.exceptions.XMLSecurityException;
60 import org.apache.xml.security.stax.ext.DocumentContext;
61 import org.apache.xml.security.stax.ext.InputProcessorChain;
62 import org.apache.xml.security.stax.ext.Transformer;
63 import org.apache.xml.security.stax.ext.XMLSecurityConstants;
64 import org.apache.xml.security.stax.ext.XMLSecurityProperties;
65 import org.apache.xml.security.stax.ext.XMLSecurityUtils;
66 import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
67 import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
68 import org.apache.xml.security.stax.impl.processor.input.AbstractSignatureReferenceVerifyInputProcessor;
69 import org.apache.xml.security.stax.impl.transformer.canonicalizer.Canonicalizer20010315_Excl;
70 import org.apache.xml.security.stax.impl.util.DigestOutputStream;
71 import org.apache.xml.security.stax.securityEvent.AlgorithmSuiteSecurityEvent;
72 import org.apache.xml.security.stax.securityEvent.SignedElementSecurityEvent;
73 import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
74 import org.apache.xml.security.stax.securityToken.SecurityToken;
75 import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
76 import org.apache.xml.security.utils.UnsyncBufferedOutputStream;
77
78 public class WSSSignatureReferenceVerifyInputProcessor extends AbstractSignatureReferenceVerifyInputProcessor {
79
80 private boolean replayChecked = false;
81
82 public WSSSignatureReferenceVerifyInputProcessor(InputProcessorChain inputProcessorChain,
83 SignatureType signatureType, InboundSecurityToken inboundSecurityToken,
84 XMLSecurityProperties securityProperties) throws XMLSecurityException {
85 super(inputProcessorChain, signatureType, inboundSecurityToken, securityProperties);
86 this.addAfterProcessor(WSSSignatureReferenceVerifyInputProcessor.class.getName());
87
88 checkBSPCompliance((WSInboundSecurityContext)inputProcessorChain.getSecurityContext());
89 }
90
91 @Override
92 protected void verifyExternalReference(
93 InputProcessorChain inputProcessorChain, InputStream inputStream,
94 final ReferenceType referenceType) throws XMLSecurityException, XMLStreamException {
95
96 if (referenceType.getURI().startsWith("cid:")) {
97
98 CallbackHandler attachmentCallbackHandler =
99 ((WSSSecurityProperties) getSecurityProperties()).getAttachmentCallbackHandler();
100 if (attachmentCallbackHandler == null) {
101 throw new WSSecurityException(
102 WSSecurityException.ErrorCode.INVALID_SECURITY,
103 "empty", new Object[] {"no attachment callbackhandler supplied"}
104 );
105 }
106
107 String attachmentId = AttachmentUtils.getAttachmentId(referenceType.getURI());
108
109 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
110 attachmentRequestCallback.setAttachmentId(attachmentId);
111 try {
112 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
113 } catch (Exception e) {
114 throw new WSSecurityException(
115 WSSecurityException.ErrorCode.INVALID_SECURITY, e);
116 }
117 List<Attachment> attachments = attachmentRequestCallback.getAttachments();
118 if (attachments == null || attachments.isEmpty() || !attachmentId.equals(attachments.get(0).getId())) {
119 throw new WSSecurityException(
120 WSSecurityException.ErrorCode.INVALID_SECURITY,
121 "empty", new Object[] {"Attachment not found"}
122 );
123 }
124
125 final Attachment attachment = attachments.get(0);
126
127 InputStream attachmentInputStream = attachment.getSourceStream();
128 if (!attachmentInputStream.markSupported()) {
129 attachmentInputStream = new BufferedInputStream(attachmentInputStream);
130 }
131
132 attachmentInputStream.mark(Integer.MAX_VALUE);
133
134 try {
135 DigestOutputStream digestOutputStream =
136 createMessageDigestOutputStream(referenceType, inputProcessorChain.getSecurityContext());
137 try (UnsyncBufferedOutputStream bufferedDigestOutputStream =
138 new UnsyncBufferedOutputStream(digestOutputStream)) {
139 if (referenceType.getTransforms() != null) {
140 Transformer transformer =
141 buildTransformerChain(referenceType, bufferedDigestOutputStream, inputProcessorChain, null);
142 if (!(transformer instanceof AttachmentContentSignatureTransform)) {
143 throw new WSSecurityException(
144 WSSecurityException.ErrorCode.INVALID_SECURITY,
145 "empty",
146 new Object[]{"First transform must be Attachment[Content|Complete]SignatureTransform"}
147 );
148 }
149 Map<String, Object> transformerProperties = new HashMap<>(2);
150 transformerProperties.put(
151 AttachmentContentSignatureTransform.ATTACHMENT, attachment);
152 transformer.setProperties(transformerProperties);
153
154 transformer.transform(attachmentInputStream);
155 } else {
156 XMLSecurityUtils.copy(attachmentInputStream, bufferedDigestOutputStream);
157 }
158 }
159 compareDigest(digestOutputStream.getDigestValue(), referenceType);
160
161
162 attachmentInputStream.reset();
163
164 } catch (IOException e) {
165 throw new XMLSecurityException(e);
166 }
167
168
169 final Attachment resultAttachment = new Attachment();
170 resultAttachment.setId(attachmentId);
171 resultAttachment.setMimeType(attachment.getMimeType());
172 resultAttachment.addHeaders(attachment.getHeaders());
173 resultAttachment.setSourceStream(attachmentInputStream);
174
175 AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
176 attachmentResultCallback.setAttachmentId(attachmentId);
177 attachmentResultCallback.setAttachment(resultAttachment);
178 try {
179 attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
180 } catch (Exception e) {
181 throw new WSSecurityException(
182 WSSecurityException.ErrorCode.INVALID_SECURITY, e);
183 }
184
185
186 final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
187 SignedPartSecurityEvent signedPartSecurityEvent =
188 new SignedPartSecurityEvent(getInboundSecurityToken(), true, documentContext.getProtectionOrder());
189 signedPartSecurityEvent.setAttachment(true);
190 signedPartSecurityEvent.setCorrelationID(referenceType.getId());
191 inputProcessorChain.getSecurityContext().registerSecurityEvent(signedPartSecurityEvent);
192 } else {
193 super.verifyExternalReference(
194 inputProcessorChain, inputStream, referenceType);
195 }
196 }
197
198 private void checkBSPCompliance(WSInboundSecurityContext securityContext) throws WSSecurityException {
199 List<ReferenceType> references = getSignatureType().getSignedInfo().getReference();
200 for (int i = 0; i < references.size(); i++) {
201 ReferenceType referenceType = references.get(i);
202 if (referenceType.getTransforms() == null) {
203 securityContext.handleBSPRule(BSPRule.R5416);
204 } else if (referenceType.getTransforms().getTransform().isEmpty()) {
205 securityContext.handleBSPRule(BSPRule.R5411);
206 } else {
207 List<TransformType> transformTypes = referenceType.getTransforms().getTransform();
208 for (int j = 0; j < transformTypes.size(); j++) {
209 TransformType transformType = transformTypes.get(j);
210 final String algorithm = transformType.getAlgorithm();
211 if (!WSSConstants.NS_C14N_EXCL.equals(algorithm)
212 && !WSSConstants.NS_XMLDSIG_FILTER2.equals(algorithm)
213 && !WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(algorithm)
214 && !WSSConstants.NS_XMLDSIG_ENVELOPED_SIGNATURE.equals(algorithm)
215 && !WSSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm)
216 && !WSSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm)) {
217 securityContext.handleBSPRule(BSPRule.R5423);
218 if (j == transformTypes.size() - 1
219 && !WSSConstants.NS_C14N_EXCL.equals(algorithm)
220 && !WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(algorithm)
221 && !WSSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm)
222 && !WSSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm)) {
223 securityContext.handleBSPRule(BSPRule.R5412);
224 }
225 InclusiveNamespaces inclusiveNamespacesType =
226 XMLSecurityUtils.getQNameType(transformType.getContent(),
227 XMLSecurityConstants.TAG_c14nExcl_InclusiveNamespaces);
228 if (WSSConstants.NS_C14N_EXCL.equals(algorithm)
229 && inclusiveNamespacesType != null
230 && inclusiveNamespacesType.getPrefixList().isEmpty()) {
231 securityContext.handleBSPRule(BSPRule.R5407);
232 }
233 if (WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(algorithm)) {
234 if (inclusiveNamespacesType != null
235 && inclusiveNamespacesType.getPrefixList().isEmpty()) {
236 securityContext.handleBSPRule(BSPRule.R5413);
237 }
238 TransformationParametersType transformationParametersType =
239 XMLSecurityUtils.getQNameType(transformType.getContent(),
240 WSSConstants.TAG_WSSE_TRANSFORMATION_PARAMETERS);
241 if (transformationParametersType == null) {
242 securityContext.handleBSPRule(BSPRule.R3065);
243 } else {
244 CanonicalizationMethodType canonicalizationMethodType =
245 XMLSecurityUtils.getQNameType(transformationParametersType.getAny(),
246 WSSConstants.TAG_dsig_CanonicalizationMethod);
247 if (canonicalizationMethodType == null) {
248 securityContext.handleBSPRule(BSPRule.R3065);
249 }
250 }
251 }
252 }
253 }
254 }
255 if (!(WSSConstants.NS_XMLDSIG_SHA1.equals(referenceType.getDigestMethod().getAlgorithm())
256 || WSSConstants.NS_XENC_SHA256.equals(referenceType.getDigestMethod().getAlgorithm())
257 || WSSConstants.NS_XENC_SHA512.equals(referenceType.getDigestMethod().getAlgorithm()))) {
258
259 securityContext.handleBSPRule(BSPRule.R5420);
260 }
261 }
262 }
263
264 @Override
265 public XMLSecEvent processEvent(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException {
266
267
268 if (!replayChecked) {
269 replayChecked = true;
270 detectReplayAttack(inputProcessorChain);
271 }
272 return super.processEvent(inputProcessorChain);
273 }
274
275 @Override
276 protected void processElementPath(List<QName> elementPath, InputProcessorChain inputProcessorChain,
277 XMLSecEvent xmlSecEvent, ReferenceType referenceType)
278 throws XMLSecurityException {
279
280 final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
281 if (elementPath.size() == 3 && WSSUtils.isInSOAPHeader(elementPath)
282 || elementPath.size() == 2 && WSSUtils.isInSOAPBody(elementPath)) {
283 SignedPartSecurityEvent signedPartSecurityEvent =
284 new SignedPartSecurityEvent(getInboundSecurityToken(), true, documentContext.getProtectionOrder());
285 signedPartSecurityEvent.setElementPath(elementPath);
286 signedPartSecurityEvent.setXmlSecEvent(xmlSecEvent);
287 signedPartSecurityEvent.setCorrelationID(referenceType.getId());
288 inputProcessorChain.getSecurityContext().registerSecurityEvent(signedPartSecurityEvent);
289 } else {
290 SignedElementSecurityEvent signedElementSecurityEvent =
291 new SignedElementSecurityEvent(getInboundSecurityToken(), true, documentContext.getProtectionOrder());
292 signedElementSecurityEvent.setElementPath(elementPath);
293 signedElementSecurityEvent.setXmlSecEvent(xmlSecEvent);
294 signedElementSecurityEvent.setCorrelationID(referenceType.getId());
295 inputProcessorChain.getSecurityContext().registerSecurityEvent(signedElementSecurityEvent);
296 }
297 }
298
299 @Override
300 protected InternalSignatureReferenceVerifier getSignatureReferenceVerifier(
301 XMLSecurityProperties securityProperties, InputProcessorChain inputProcessorChain,
302 ReferenceType referenceType, XMLSecStartElement startElement) throws XMLSecurityException {
303 return new WSS4JInternalSignatureReferenceVerifier((WSSSecurityProperties) securityProperties,
304 inputProcessorChain, referenceType, startElement);
305 }
306
307 private void detectReplayAttack(InputProcessorChain inputProcessorChain) throws WSSecurityException {
308 TimestampSecurityEvent timestampSecurityEvent =
309 inputProcessorChain.getSecurityContext().get(WSSConstants.PROP_TIMESTAMP_SECURITYEVENT);
310 ReplayCache replayCache =
311 ((WSSSecurityProperties)getSecurityProperties()).getTimestampReplayCache();
312 if (timestampSecurityEvent != null && replayCache != null) {
313 final String cacheKey =
314 timestampSecurityEvent.getCreated().get(ChronoField.MILLI_OF_SECOND)
315 + "" + Arrays.hashCode(getSignatureType().getSignatureValue().getValue());
316 if (replayCache.contains(cacheKey)) {
317 throw new WSSecurityException(WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
318 }
319
320
321 Instant expires = timestampSecurityEvent.getExpires();
322 if (expires != null) {
323 replayCache.add(cacheKey, expires);
324 } else {
325 replayCache.add(cacheKey);
326 }
327 }
328 }
329
330 @Override
331 protected Transformer buildTransformerChain(
332 ReferenceType referenceType, OutputStream outputStream,
333 InputProcessorChain inputProcessorChain,
334 AbstractSignatureReferenceVerifyInputProcessor.InternalSignatureReferenceVerifier internalSignatureReferenceVerifier)
335 throws XMLSecurityException {
336
337 if (referenceType.getTransforms() == null || referenceType.getTransforms().getTransform().isEmpty()) {
338 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
339 }
340 List<TransformType> transformTypeList = referenceType.getTransforms().getTransform();
341
342 if (transformTypeList.size() > maximumAllowedTransformsPerReference) {
343 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY,
344 "secureProcessing.MaximumAllowedTransformsPerReference",
345 new Object[] {transformTypeList.size(), maximumAllowedTransformsPerReference});
346 }
347
348 String algorithm = null;
349 Transformer parentTransformer = null;
350 for (int i = transformTypeList.size() - 1; i >= 0; i--) {
351 TransformType transformType = transformTypeList.get(i);
352 TransformationParametersType transformationParametersType =
353 XMLSecurityUtils.getQNameType(transformType.getContent(), WSSConstants.TAG_WSSE_TRANSFORMATION_PARAMETERS);
354 if (transformationParametersType != null) {
355 CanonicalizationMethodType canonicalizationMethodType =
356 XMLSecurityUtils.getQNameType(transformationParametersType.getAny(),
357 WSSConstants.TAG_dsig_CanonicalizationMethod);
358 if (canonicalizationMethodType != null) {
359
360 algorithm = canonicalizationMethodType.getAlgorithm();
361
362 InclusiveNamespaces inclusiveNamespacesType =
363 XMLSecurityUtils.getQNameType(canonicalizationMethodType.getContent(),
364 XMLSecurityConstants.TAG_c14nExcl_InclusiveNamespaces);
365
366 Map<String, Object> transformerProperties = null;
367 if (inclusiveNamespacesType != null) {
368 transformerProperties = new HashMap<>();
369 transformerProperties.put(
370 Canonicalizer20010315_Excl.INCLUSIVE_NAMESPACES_PREFIX_LIST,
371 inclusiveNamespacesType.getPrefixList());
372 }
373 parentTransformer = WSSUtils.getTransformer(
374 null, outputStream, transformerProperties, algorithm, XMLSecurityConstants.DIRECTION.IN);
375 }
376 }
377 algorithm = transformType.getAlgorithm();
378 AlgorithmSuiteSecurityEvent algorithmSuiteSecurityEvent = new AlgorithmSuiteSecurityEvent();
379 algorithmSuiteSecurityEvent.setAlgorithmURI(algorithm);
380 algorithmSuiteSecurityEvent.setAlgorithmUsage(WSSConstants.SigTransform);
381 algorithmSuiteSecurityEvent.setCorrelationID(referenceType.getId());
382 inputProcessorChain.getSecurityContext().registerSecurityEvent(algorithmSuiteSecurityEvent);
383
384 InclusiveNamespaces inclusiveNamespacesType =
385 XMLSecurityUtils.getQNameType(transformType.getContent(),
386 XMLSecurityConstants.TAG_c14nExcl_InclusiveNamespaces);
387
388 Map<String, Object> transformerProperties = null;
389 if (inclusiveNamespacesType != null) {
390 transformerProperties = new HashMap<>();
391 transformerProperties.put(
392 Canonicalizer20010315_Excl.INCLUSIVE_NAMESPACES_PREFIX_LIST,
393 inclusiveNamespacesType.getPrefixList());
394 }
395
396 if (parentTransformer != null) {
397 parentTransformer = WSSUtils.getTransformer(
398 parentTransformer, null, transformerProperties, algorithm, XMLSecurityConstants.DIRECTION.IN);
399 } else {
400 parentTransformer = WSSUtils.getTransformer(
401 null, outputStream, transformerProperties, algorithm, XMLSecurityConstants.DIRECTION.IN);
402 }
403 }
404
405 if (WSSConstants.SOAPMESSAGE_NS10_STR_TRANSFORM.equals(algorithm)) {
406
407 internalSignatureReferenceVerifier.setTransformer(parentTransformer);
408
409 String uri = XMLSecurityUtils.dropReferenceMarker(referenceType.getURI());
410 SecurityTokenProvider<? extends InboundSecurityToken> securityTokenProvider =
411 inputProcessorChain.getSecurityContext().getSecurityTokenProvider(uri);
412 if (securityTokenProvider == null) {
413 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN, "noReference");
414 }
415 SecurityToken securityToken = securityTokenProvider.getSecurityToken();
416 if (!(securityToken instanceof SecurityTokenReference)) {
417 throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN);
418 }
419 SecurityTokenReference securityTokenReference = (SecurityTokenReference) securityToken;
420
421 int index = inputProcessorChain.getProcessors().indexOf(internalSignatureReferenceVerifier);
422 inputProcessorChain.getDocumentContext().setIsInSignedContent(index, internalSignatureReferenceVerifier);
423 XMLSecStartElement xmlSecStartElement = securityTokenReference.getXmlSecEvents().getLast().asStartElement();
424 internalSignatureReferenceVerifier.setStartElement(xmlSecStartElement);
425 Iterator<XMLSecEvent> xmlSecEventIterator = securityTokenReference.getXmlSecEvents().descendingIterator();
426 try {
427 while (xmlSecEventIterator.hasNext()) {
428 internalSignatureReferenceVerifier.processEvent(xmlSecEventIterator.next(), inputProcessorChain);
429 }
430 } catch (XMLStreamException e) {
431 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
432 }
433 }
434 return parentTransformer;
435 }
436
437 class WSS4JInternalSignatureReferenceVerifier
438 extends AbstractSignatureReferenceVerifyInputProcessor.InternalSignatureReferenceVerifier {
439
440 WSS4JInternalSignatureReferenceVerifier(WSSSecurityProperties securityProperties, InputProcessorChain inputProcessorChain,
441 ReferenceType referenceType, XMLSecStartElement startElement) throws XMLSecurityException {
442 super(securityProperties, inputProcessorChain, referenceType, startElement);
443 this.addAfterProcessor(WSSSignatureReferenceVerifyInputProcessor.class.getName());
444 }
445 }
446 }