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.IOException;
22 import java.io.InputStream;
23 import java.lang.reflect.Constructor;
24 import java.lang.reflect.InvocationTargetException;
25 import java.security.Key;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.crypto.Cipher;
32 import javax.security.auth.callback.Callback;
33 import javax.security.auth.callback.CallbackHandler;
34 import jakarta.xml.bind.JAXBElement;
35 import javax.xml.namespace.QName;
36 import javax.xml.stream.XMLStreamException;
37
38 import org.apache.wss4j.binding.wss10.SecurityTokenReferenceType;
39 import org.apache.wss4j.common.bsp.BSPRule;
40 import org.apache.wss4j.common.ext.Attachment;
41 import org.apache.wss4j.common.ext.AttachmentRequestCallback;
42 import org.apache.wss4j.common.ext.AttachmentResultCallback;
43 import org.apache.wss4j.common.ext.WSSecurityException;
44 import org.apache.wss4j.common.util.AttachmentUtils;
45 import org.apache.wss4j.stax.ext.WSInboundSecurityContext;
46 import org.apache.wss4j.stax.ext.WSSConstants;
47 import org.apache.wss4j.stax.ext.WSSSecurityProperties;
48 import org.apache.wss4j.stax.securityEvent.EncryptedPartSecurityEvent;
49 import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
50 import org.apache.wss4j.stax.utils.LimitingInputStream;
51 import org.apache.wss4j.stax.utils.WSSUtils;
52 import org.apache.xml.security.binding.xmldsig.KeyInfoType;
53 import org.apache.xml.security.binding.xmldsig.TransformType;
54 import org.apache.xml.security.binding.xmldsig.TransformsType;
55 import org.apache.xml.security.binding.xmlenc.CipherReferenceType;
56 import org.apache.xml.security.binding.xmlenc.EncryptedDataType;
57 import org.apache.xml.security.binding.xmlenc.ReferenceList;
58 import org.apache.xml.security.binding.xmlenc.ReferenceType;
59 import org.apache.xml.security.exceptions.XMLSecurityException;
60 import org.apache.xml.security.stax.config.ConfigurationProperties;
61 import org.apache.xml.security.stax.config.TransformerAlgorithmMapper;
62 import org.apache.xml.security.stax.ext.DocumentContext;
63 import org.apache.xml.security.stax.ext.InboundSecurityContext;
64 import org.apache.xml.security.stax.ext.InputProcessorChain;
65 import org.apache.xml.security.stax.ext.SecurePart;
66 import org.apache.xml.security.stax.ext.XMLSecurityConstants;
67 import org.apache.xml.security.stax.ext.XMLSecurityProperties;
68 import org.apache.xml.security.stax.ext.XMLSecurityUtils;
69 import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
70 import org.apache.xml.security.stax.impl.processor.input.AbstractDecryptInputProcessor;
71 import org.apache.xml.security.stax.securityEvent.ContentEncryptedElementSecurityEvent;
72 import org.apache.xml.security.stax.securityEvent.EncryptedElementSecurityEvent;
73 import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent;
74 import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
75 import org.apache.xml.security.stax.securityToken.SecurityToken;
76
77
78
79
80 public class DecryptInputProcessor extends AbstractDecryptInputProcessor {
81
82 private static final transient org.slf4j.Logger LOG =
83 org.slf4j.LoggerFactory.getLogger(DecryptInputProcessor.class);
84
85 private static final Long MAX_ALLOWED_DECOMPRESSED_BYTES =
86 Long.valueOf(ConfigurationProperties.getProperty("MaximumAllowedDecompressedBytes"));
87
88 private List<DeferredAttachment> attachmentReferences = new ArrayList<>();
89
90 public DecryptInputProcessor(KeyInfoType keyInfoType, ReferenceList referenceList,
91 WSSSecurityProperties securityProperties, WSInboundSecurityContext securityContext)
92 throws XMLSecurityException {
93
94 super(keyInfoType, referenceList, securityProperties);
95 checkBSPCompliance(keyInfoType, referenceList, securityContext, BSPRule.R3006);
96 }
97
98 private void checkBSPCompliance(KeyInfoType keyInfoType, ReferenceList referenceList, WSInboundSecurityContext securityContext,
99 BSPRule bspRule) throws WSSecurityException {
100 if (keyInfoType != null) {
101 if (keyInfoType.getContent().size() != 1) {
102 securityContext.handleBSPRule(BSPRule.R5424);
103 }
104 SecurityTokenReferenceType securityTokenReferenceType = XMLSecurityUtils.getQNameType(keyInfoType.getContent(),
105 WSSConstants.TAG_WSSE_SECURITY_TOKEN_REFERENCE);
106 if (securityTokenReferenceType == null) {
107 securityContext.handleBSPRule(BSPRule.R5426);
108 }
109 }
110
111 if (referenceList != null) {
112 List<JAXBElement<ReferenceType>> references = referenceList.getDataReferenceOrKeyReference();
113 Iterator<JAXBElement<ReferenceType>> referenceTypeIterator = references.iterator();
114 while (referenceTypeIterator.hasNext()) {
115 ReferenceType referenceType = referenceTypeIterator.next().getValue();
116 if (!referenceType.getURI().startsWith("#")) {
117 securityContext.handleBSPRule(bspRule);
118 }
119 }
120 }
121 }
122
123 @Override
124 protected InputStream applyTransforms(ReferenceType referenceType, InputStream inputStream) throws XMLSecurityException {
125 if (referenceType != null) {
126 TransformsType transformsType =
127 XMLSecurityUtils.getQNameType(referenceType.getAny(), XMLSecurityConstants.TAG_dsig_Transforms);
128 if (transformsType != null) {
129 List<TransformType> transformTypes = transformsType.getTransform();
130
131 if (transformTypes.size() > 1) {
132 throw new XMLSecurityException("stax.encryption.Transforms.NotYetImplemented");
133 }
134 TransformType transformType = transformTypes.get(0);
135 @SuppressWarnings("unchecked")
136 Class<InputStream> transformerClass =
137 (Class<InputStream>) TransformerAlgorithmMapper.getTransformerClass(
138 transformType.getAlgorithm(), XMLSecurityConstants.DIRECTION.IN);
139 try {
140 Constructor<InputStream> constructor = transformerClass.getConstructor(InputStream.class);
141 inputStream = new LimitingInputStream(
142 constructor.newInstance(inputStream), MAX_ALLOWED_DECOMPRESSED_BYTES);
143 } catch (InvocationTargetException | NoSuchMethodException
144 | InstantiationException | IllegalAccessException e) {
145 throw new XMLSecurityException(e);
146 }
147 }
148 }
149 return inputStream;
150 }
151
152 @Override
153 protected void handleEncryptedContent(
154 InputProcessorChain inputProcessorChain, XMLSecStartElement parentStartXMLEvent,
155 InboundSecurityToken inboundSecurityToken, EncryptedDataType encryptedDataType) throws XMLSecurityException {
156
157 final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
158 List<QName> elementPath = parentStartXMLEvent.getElementPath();
159 if (elementPath.size() == 2 && WSSUtils.isInSOAPBody(elementPath)) {
160
161 EncryptedPartSecurityEvent encryptedPartSecurityEvent =
162 new EncryptedPartSecurityEvent(inboundSecurityToken, true, documentContext.getProtectionOrder());
163 encryptedPartSecurityEvent.setElementPath(elementPath);
164 encryptedPartSecurityEvent.setXmlSecEvent(parentStartXMLEvent);
165 encryptedPartSecurityEvent.setCorrelationID(encryptedDataType.getId());
166 inputProcessorChain.getSecurityContext().registerSecurityEvent(encryptedPartSecurityEvent);
167 } else {
168 ContentEncryptedElementSecurityEvent contentEncryptedElementSecurityEvent =
169 new ContentEncryptedElementSecurityEvent(inboundSecurityToken, true, documentContext.getProtectionOrder());
170 contentEncryptedElementSecurityEvent.setElementPath(elementPath);
171 contentEncryptedElementSecurityEvent.setXmlSecEvent(parentStartXMLEvent);
172 contentEncryptedElementSecurityEvent.setCorrelationID(encryptedDataType.getId());
173 inputProcessorChain.getSecurityContext().registerSecurityEvent(contentEncryptedElementSecurityEvent);
174 }
175 }
176
177 @Override
178 protected void handleCipherReference(InputProcessorChain inputProcessorChain, EncryptedDataType encryptedDataType,
179 Cipher cipher, InboundSecurityToken inboundSecurityToken) throws XMLSecurityException {
180
181 String typeStr = encryptedDataType.getType();
182 if (typeStr != null
183 && (WSSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_CONTENT_ONLY.equals(typeStr)
184 || WSSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE.equals(typeStr))) {
185
186 CipherReferenceType cipherReferenceType = encryptedDataType.getCipherData().getCipherReference();
187 if (cipherReferenceType == null) {
188 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
189 }
190
191 final String uri = cipherReferenceType.getURI();
192 if (uri == null || uri.length() < 5) {
193 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
194 }
195 if (!uri.startsWith("cid:")) {
196 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
197 }
198
199
200
201
202 attachmentReferences.add(
203 new DeferredAttachment(encryptedDataType, cipher, inboundSecurityToken)
204 );
205 }
206 }
207
208 @Override
209 protected InputStream handleXOPInclude(InputProcessorChain inputProcessorChain, EncryptedDataType encryptedDataType, String href,
210 Cipher cipher, InboundSecurityToken inboundSecurityToken) throws XMLSecurityException {
211 if (href == null || href.length() < 5) {
212 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
213 }
214 if (!href.startsWith("cid:")) {
215 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
216 }
217
218 final String attachmentId = AttachmentUtils.getAttachmentId(href);
219
220 CallbackHandler attachmentCallbackHandler =
221 ((WSSSecurityProperties) getSecurityProperties()).getAttachmentCallbackHandler();
222 if (attachmentCallbackHandler == null) {
223 throw new WSSecurityException(
224 WSSecurityException.ErrorCode.INVALID_SECURITY,
225 "empty", new Object[] {"no attachment callbackhandler supplied"}
226 );
227 }
228
229 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
230 attachmentRequestCallback.setAttachmentId(attachmentId);
231 try {
232 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
233 } catch (Exception e) {
234 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
235 }
236 List<Attachment> attachments = attachmentRequestCallback.getAttachments();
237 if (attachments == null || attachments.isEmpty() || !attachmentId.equals(attachments.get(0).getId())) {
238 throw new WSSecurityException(
239 WSSecurityException.ErrorCode.INVALID_SECURITY,
240 "empty", new Object[] {"Attachment not found"}
241 );
242 }
243
244 final Attachment attachment = attachments.get(0);
245
246 final String encAlgo = encryptedDataType.getEncryptionMethod().getAlgorithm();
247 final Key symmetricKey =
248 inboundSecurityToken.getSecretKey(encAlgo, XMLSecurityConstants.Enc, encryptedDataType.getId());
249
250 return
251 AttachmentUtils.setupAttachmentDecryptionStream(encAlgo, cipher, symmetricKey, attachment.getSourceStream());
252 }
253
254 @Override
255 protected AbstractDecryptedEventReaderInputProcessor newDecryptedEventReaderInputProcessor(
256 boolean encryptedHeader, XMLSecStartElement xmlSecStartElement, EncryptedDataType encryptedDataType,
257 InboundSecurityToken inboundSecurityToken, InboundSecurityContext inboundSecurityContext) throws XMLSecurityException {
258
259
260 String encryptionAlgorithm = encryptedDataType.getEncryptionMethod().getAlgorithm();
261 if (this.getSecurityProperties().getEncryptionSymAlgorithm() != null
262 && !this.getSecurityProperties().getEncryptionSymAlgorithm().equals(encryptionAlgorithm)) {
263 LOG.warn(
264 "The Key encryption method does not match the requirement"
265 );
266 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
267 }
268
269 if (!WSSConstants.NS_XENC_TRIPLE_DES.equals(encryptionAlgorithm)
270 && !WSSConstants.NS_XENC_AES128.equals(encryptionAlgorithm)
271 && !WSSConstants.NS_XENC11_AES128_GCM.equals(encryptionAlgorithm)
272 && !WSSConstants.NS_XENC_AES256.equals(encryptionAlgorithm)
273 && !WSSConstants.NS_XENC11_AES256_GCM.equals(encryptionAlgorithm)) {
274 ((WSInboundSecurityContext) inboundSecurityContext).handleBSPRule(BSPRule.R5620);
275 }
276
277 return new DecryptedEventReaderInputProcessor(getSecurityProperties(),
278 SecurePart.Modifier.getModifier(encryptedDataType.getType()),
279 encryptedHeader, xmlSecStartElement, encryptedDataType, this, inboundSecurityToken);
280 }
281
282 @Override
283 protected void handleSecurityToken(InboundSecurityToken inboundSecurityToken, InboundSecurityContext inboundSecurityContext,
284 EncryptedDataType encryptedDataType) throws XMLSecurityException {
285 inboundSecurityToken.addTokenUsage(WSSecurityTokenConstants.TokenUsage_Encryption);
286 TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent =
287 WSSUtils.createTokenSecurityEvent(inboundSecurityToken, encryptedDataType.getId());
288 inboundSecurityContext.registerSecurityEvent(tokenSecurityEvent);
289 }
290
291 @Override
292 public void doFinal(InputProcessorChain inputProcessorChain) throws XMLStreamException, XMLSecurityException {
293
294 List<String> encryptedDataRefs = inputProcessorChain.getSecurityContext().getAsList(WSSConstants.PROP_ENCRYPTED_DATA_REFS);
295 if (encryptedDataRefs != null && !encryptedDataRefs.isEmpty()) {
296 Map<String, ReferenceType> references = getReferences();
297 List<ReferenceType> processedReferences = getProcessedReferences();
298 if (references != null) {
299 Iterator<Map.Entry<String, ReferenceType>> iterator = references.entrySet().iterator();
300 while (iterator.hasNext()) {
301 Map.Entry<String, ReferenceType> next = iterator.next();
302 final ReferenceType referenceType = next.getValue();
303 String uri = WSSUtils.dropReferenceMarker(referenceType.getURI());
304
305 Iterator<String> encryptedDataIterator = encryptedDataRefs.iterator();
306 while (encryptedDataIterator.hasNext()) {
307 String s = encryptedDataIterator.next();
308 if (s.equals(uri)) {
309 processedReferences.add(referenceType);
310 }
311 }
312 }
313 }
314 }
315 super.doFinal(inputProcessorChain);
316
317
318 for (int i = 0; i < attachmentReferences.size(); i++) {
319 DeferredAttachment deferredAttachment = attachmentReferences.get(i);
320
321 final EncryptedDataType encryptedDataType = deferredAttachment.getEncryptedDataType();
322 final InboundSecurityToken inboundSecurityToken = deferredAttachment.getInboundSecurityToken();
323 final Cipher cipher = deferredAttachment.getCipher();
324 final String uri = encryptedDataType.getCipherData().getCipherReference().getURI();
325 final String attachmentId = AttachmentUtils.getAttachmentId(uri);
326
327 CallbackHandler attachmentCallbackHandler =
328 ((WSSSecurityProperties) getSecurityProperties()).getAttachmentCallbackHandler();
329 if (attachmentCallbackHandler == null) {
330 throw new WSSecurityException(
331 WSSecurityException.ErrorCode.INVALID_SECURITY,
332 "empty", new Object[] {"no attachment callbackhandler supplied"}
333 );
334 }
335
336 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
337 attachmentRequestCallback.setAttachmentId(attachmentId);
338 try {
339 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
340 } catch (Exception e) {
341 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
342 }
343 List<Attachment> attachments = attachmentRequestCallback.getAttachments();
344 if (attachments == null || attachments.isEmpty() || !attachmentId.equals(attachments.get(0).getId())) {
345 throw new WSSecurityException(
346 WSSecurityException.ErrorCode.INVALID_SECURITY,
347 "empty", new Object[] {"Attachment not found"}
348 );
349 }
350
351 final Attachment attachment = attachments.get(0);
352
353 final String encAlgo = encryptedDataType.getEncryptionMethod().getAlgorithm();
354 final Key symmetricKey =
355 inboundSecurityToken.getSecretKey(encAlgo, XMLSecurityConstants.Enc, encryptedDataType.getId());
356
357 InputStream attachmentInputStream =
358 AttachmentUtils.setupAttachmentDecryptionStream(encAlgo, cipher, symmetricKey, attachment.getSourceStream());
359
360 Attachment resultAttachment = new Attachment();
361 resultAttachment.setId(attachment.getId());
362 resultAttachment.setMimeType(encryptedDataType.getMimeType());
363 resultAttachment.setSourceStream(attachmentInputStream);
364 resultAttachment.addHeaders(attachment.getHeaders());
365
366 if (WSSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE.equals(encryptedDataType.getType())) {
367 try {
368 AttachmentUtils.readAndReplaceEncryptedAttachmentHeaders(
369 resultAttachment.getHeaders(), attachmentInputStream);
370 } catch (IOException e) {
371 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
372 }
373 }
374
375 AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
376 attachmentResultCallback.setAttachment(resultAttachment);
377 attachmentResultCallback.setAttachmentId(resultAttachment.getId());
378 try {
379 attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
380 } catch (Exception e) {
381 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
382 }
383
384
385 final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
386 EncryptedPartSecurityEvent encryptedPartSecurityEvent =
387 new EncryptedPartSecurityEvent(inboundSecurityToken, true, documentContext.getProtectionOrder());
388 encryptedPartSecurityEvent.setAttachment(true);
389 encryptedPartSecurityEvent.setCorrelationID(encryptedDataType.getId());
390 inputProcessorChain.getSecurityContext().registerSecurityEvent(encryptedPartSecurityEvent);
391 }
392 }
393
394 private static final class DeferredAttachment {
395
396 private EncryptedDataType encryptedDataType;
397 private Cipher cipher;
398 private InboundSecurityToken inboundSecurityToken;
399
400 private DeferredAttachment(
401 EncryptedDataType encryptedDataType, Cipher cipher,
402 InboundSecurityToken inboundSecurityToken) {
403
404 this.encryptedDataType = encryptedDataType;
405 this.cipher = cipher;
406 this.inboundSecurityToken = inboundSecurityToken;
407 }
408
409 private EncryptedDataType getEncryptedDataType() {
410 return encryptedDataType;
411 }
412
413 private Cipher getCipher() {
414 return cipher;
415 }
416
417 private InboundSecurityToken getInboundSecurityToken() {
418 return inboundSecurityToken;
419 }
420 }
421
422
423
424
425
426 class DecryptedEventReaderInputProcessor extends AbstractDecryptedEventReaderInputProcessor {
427
428 DecryptedEventReaderInputProcessor(
429 XMLSecurityProperties securityProperties, SecurePart.Modifier encryptionModifier,
430 boolean encryptedHeader, XMLSecStartElement xmlSecStartElement,
431 EncryptedDataType encryptedDataType,
432 DecryptInputProcessor decryptInputProcessor,
433 InboundSecurityToken inboundSecurityToken
434 ) {
435 super(securityProperties, encryptionModifier, encryptedHeader, xmlSecStartElement,
436 encryptedDataType, decryptInputProcessor, inboundSecurityToken);
437 }
438
439 @Override
440 protected void handleEncryptedElement(
441 InputProcessorChain inputProcessorChain, XMLSecStartElement xmlSecStartElement,
442 InboundSecurityToken inboundSecurityToken, EncryptedDataType encryptedDataType) throws XMLSecurityException {
443
444
445 final DocumentContext documentContext = inputProcessorChain.getDocumentContext();
446 List<QName> elementPath = xmlSecStartElement.getElementPath();
447 if (elementPath.size() == 3 && WSSUtils.isInSOAPHeader(elementPath)) {
448 EncryptedPartSecurityEvent encryptedPartSecurityEvent =
449 new EncryptedPartSecurityEvent(inboundSecurityToken, true, documentContext.getProtectionOrder());
450 encryptedPartSecurityEvent.setElementPath(elementPath);
451 encryptedPartSecurityEvent.setXmlSecEvent(xmlSecStartElement);
452 encryptedPartSecurityEvent.setCorrelationID(encryptedDataType.getId());
453 inputProcessorChain.getSecurityContext().registerSecurityEvent(encryptedPartSecurityEvent);
454 } else {
455 EncryptedElementSecurityEvent encryptedElementSecurityEvent =
456 new EncryptedElementSecurityEvent(inboundSecurityToken, true, documentContext.getProtectionOrder());
457 encryptedElementSecurityEvent.setElementPath(elementPath);
458 encryptedElementSecurityEvent.setXmlSecEvent(xmlSecStartElement);
459 encryptedElementSecurityEvent.setCorrelationID(encryptedDataType.getId());
460 inputProcessorChain.getSecurityContext().registerSecurityEvent(encryptedElementSecurityEvent);
461 }
462 }
463 }
464 }