1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.util;
21
22 import org.apache.wss4j.common.ext.Attachment;
23 import org.apache.wss4j.common.ext.AttachmentRequestCallback;
24 import org.apache.wss4j.common.ext.AttachmentResultCallback;
25 import org.apache.wss4j.common.ext.WSSecurityException;
26 import org.apache.wss4j.common.util.AttachmentUtils;
27 import org.apache.wss4j.common.util.XMLUtils;
28 import org.apache.wss4j.dom.WSConstants;
29 import org.apache.wss4j.dom.WSDataRef;
30 import org.apache.wss4j.dom.WSDocInfo;
31 import org.apache.wss4j.dom.callback.CallbackLookup;
32 import org.apache.xml.security.algorithms.JCEMapper;
33 import org.apache.xml.security.encryption.Serializer;
34 import org.apache.xml.security.encryption.XMLCipher;
35 import org.apache.xml.security.encryption.XMLEncryptionException;
36 import org.apache.xml.security.parser.XMLParserException;
37 import org.apache.xml.security.utils.JavaUtils;
38 import org.w3c.dom.Attr;
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.NamedNodeMap;
42 import org.w3c.dom.Node;
43 import org.xml.sax.SAXException;
44
45 import javax.crypto.Cipher;
46 import javax.crypto.NoSuchPaddingException;
47 import javax.crypto.SecretKey;
48 import javax.security.auth.callback.Callback;
49 import javax.security.auth.callback.CallbackHandler;
50 import javax.security.auth.callback.UnsupportedCallbackException;
51
52 import java.io.ByteArrayInputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.security.NoSuchAlgorithmException;
56 import java.util.List;
57
58 public final class EncryptionUtils {
59
60 private EncryptionUtils() {
61
62 }
63
64
65
66
67
68
69
70
71
72
73
74 public static Element
75 findEncryptedDataElement(
76 WSDocInfo wsDocInfo,
77 String dataRefURI
78 ) throws WSSecurityException {
79 CallbackLookup callbackLookup = wsDocInfo.getCallbackLookup();
80 Element encryptedDataElement =
81 callbackLookup.getElement(dataRefURI, null, true);
82 if (encryptedDataElement == null) {
83 throw new WSSecurityException(
84 WSSecurityException.ErrorCode.INVALID_SECURITY, "dataRef",
85 new Object[] {dataRefURI});
86 }
87 if (encryptedDataElement.getLocalName().equals(WSConstants.ENCRYPTED_HEADER)
88 && encryptedDataElement.getNamespaceURI().equals(WSConstants.WSSE11_NS)) {
89 Node child = encryptedDataElement.getFirstChild();
90 while (child != null && child.getNodeType() != Node.ELEMENT_NODE) {
91 child = child.getNextSibling();
92 }
93 return (Element)child;
94 }
95 return encryptedDataElement;
96 }
97
98
99
100
101
102
103
104
105
106
107
108 public static WSDataRef
109 decryptEncryptedData(
110 Document doc,
111 String dataRefURI,
112 Element encData,
113 SecretKey symmetricKey,
114 String symEncAlgo,
115 CallbackHandler attachmentCallbackHandler
116 ) throws WSSecurityException {
117 return decryptEncryptedData(doc, dataRefURI, encData, symmetricKey,
118 symEncAlgo, attachmentCallbackHandler, null);
119
120 }
121
122
123
124
125
126
127
128
129
130
131 public static WSDataRef
132 decryptEncryptedData(
133 Document doc,
134 String dataRefURI,
135 Element encData,
136 SecretKey symmetricKey,
137 String symEncAlgo,
138 CallbackHandler attachmentCallbackHandler,
139 Serializer encryptionSerializer
140 ) throws WSSecurityException {
141
142
143 String typeStr = encData.getAttributeNS(null, "Type");
144 String xopURI = getXOPURIFromEncryptedData(encData);
145 if (typeStr != null
146 && (WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_CONTENT_ONLY.equals(typeStr)
147 || WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE.equals(typeStr))) {
148
149 Element cipherData = XMLUtils.getDirectChildElement(encData, "CipherData", WSConstants.ENC_NS);
150 if (cipherData == null) {
151 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
152 }
153 Element cipherReference = XMLUtils.getDirectChildElement(cipherData, "CipherReference", WSConstants.ENC_NS);
154 if (cipherReference == null) {
155 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
156 }
157 String uri = cipherReference.getAttributeNS(null, "URI");
158
159 return decryptAttachment(dataRefURI, uri, encData, symmetricKey, symEncAlgo, attachmentCallbackHandler);
160 }
161
162 WSDataRef dataRef = new WSDataRef();
163 dataRef.setEncryptedElement(encData);
164 dataRef.setWsuId(dataRefURI);
165 dataRef.setAlgorithm(symEncAlgo);
166
167 boolean content = X509Util.isContent(encData);
168 dataRef.setContent(content);
169
170 Element encDataOrig = encData;
171 Node parent = encData.getParentNode();
172 Node previousSibling = encData.getPreviousSibling();
173 if (content) {
174 encData = (Element) encData.getParentNode();
175 parent = encData.getParentNode();
176 }
177
178 XMLCipher xmlCipher = null;
179 try {
180 if (encryptionSerializer != null) {
181 xmlCipher = XMLCipher.getInstance(encryptionSerializer, symEncAlgo);
182 } else {
183 xmlCipher = XMLCipher.getInstance(symEncAlgo);
184 }
185 xmlCipher.setSecureValidation(true);
186 xmlCipher.init(XMLCipher.DECRYPT_MODE, symmetricKey);
187 } catch (XMLEncryptionException ex) {
188 throw new WSSecurityException(
189 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, ex
190 );
191 }
192
193 Node decryptedNode = null;
194 try {
195 if (xopURI != null) {
196 Element tempEncData;
197
198
199
200 if (content) {
201 tempEncData = encDataOrig;
202 } else {
203 tempEncData = encData;
204 }
205 decryptedNode = decryptXopAttachment(symmetricKey, symEncAlgo, attachmentCallbackHandler,
206 xopURI, tempEncData);
207 } else {
208
209
210 xmlCipher.doFinal(doc, encData, content);
211 }
212 } catch (Exception ex) {
213 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
214 }
215
216 if (parent.getLocalName().equals(WSConstants.ENCRYPTED_HEADER)
217 && parent.getNamespaceURI().equals(WSConstants.WSSE11_NS)
218 || parent.getLocalName().equals(WSConstants.ENCRYPED_ASSERTION_LN)
219 && parent.getNamespaceURI().equals(WSConstants.SAML2_NS)) {
220
221 Node decryptedHeader = parent.getFirstChild();
222 Node soapHeader = parent.getParentNode();
223 soapHeader.replaceChild(decryptedHeader, parent);
224
225 dataRef.setProtectedElement((Element)decryptedHeader);
226 dataRef.setXpath(getXPath(decryptedHeader));
227 } else if (content) {
228 dataRef.setProtectedElement(encData);
229 dataRef.setXpath(getXPath(encData));
230 } else {
231 if (decryptedNode == null) {
232 if (previousSibling == null) {
233 decryptedNode = parent.getFirstChild();
234 } else {
235 decryptedNode = previousSibling.getNextSibling();
236 }
237 }
238 if (decryptedNode != null && Node.ELEMENT_NODE == decryptedNode.getNodeType()) {
239 dataRef.setProtectedElement((Element)decryptedNode);
240 }
241 dataRef.setXpath(getXPath(decryptedNode));
242 }
243
244 return dataRef;
245 }
246
247 private static String getXOPURIFromEncryptedData(Element encData) {
248 Element cipherValue = getCipherValueFromEncryptedData(encData);
249 if (cipherValue != null) {
250 return getXOPURIFromCipherValue(cipherValue);
251 }
252
253 return null;
254 }
255
256 public static Element getCipherValueFromEncryptedData(Element encData) {
257 Element cipherData = XMLUtils.getDirectChildElement(encData, "CipherData", WSConstants.ENC_NS);
258 if (cipherData != null) {
259 return XMLUtils.getDirectChildElement(cipherData, "CipherValue", WSConstants.ENC_NS);
260 }
261
262 return null;
263 }
264
265 public static String getXOPURIFromCipherValue(Element cipherValue) {
266 if (cipherValue != null) {
267 Element cipherValueChild =
268 XMLUtils.getDirectChildElement(cipherValue, "Include", WSConstants.XOP_NS);
269 if (cipherValueChild != null && cipherValueChild.hasAttributeNS(null, "href")) {
270 return cipherValueChild.getAttributeNS(null, "href");
271 }
272 }
273
274 return null;
275 }
276
277
278 private static WSDataRef
279 decryptAttachment(
280 String dataRefURI,
281 String uri,
282 Element encData,
283 SecretKey symmetricKey,
284 String symEncAlgo,
285 CallbackHandler attachmentCallbackHandler
286 ) throws WSSecurityException {
287 WSDataRef dataRef = new WSDataRef();
288 dataRef.setWsuId(dataRefURI);
289 dataRef.setAlgorithm(symEncAlgo);
290
291 try {
292 if (uri == null || uri.length() < 5 || !uri.startsWith("cid:")) {
293 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
294 }
295 dataRef.setWsuId(uri);
296 dataRef.setAttachment(true);
297
298 if (attachmentCallbackHandler == null) {
299 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
300 }
301
302 final String attachmentId = AttachmentUtils.getAttachmentId(uri);
303
304 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
305 attachmentRequestCallback.setAttachmentId(attachmentId);
306
307 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
308 List<Attachment> attachments = attachmentRequestCallback.getAttachments();
309 if (attachments == null || attachments.isEmpty() || !attachmentId.equals(attachments.get(0).getId())) {
310 throw new WSSecurityException(
311 WSSecurityException.ErrorCode.INVALID_SECURITY,
312 "empty", new Object[] {"Attachment not found"}
313 );
314 }
315 Attachment attachment = attachments.get(0);
316
317 final String encAlgo = X509Util.getEncAlgo(encData);
318 final String jceAlgorithm =
319 JCEMapper.translateURItoJCEID(encAlgo);
320 final Cipher cipher = Cipher.getInstance(jceAlgorithm);
321
322 InputStream attachmentInputStream =
323 AttachmentUtils.setupAttachmentDecryptionStream(
324 encAlgo, cipher, symmetricKey, attachment.getSourceStream());
325
326 Attachment resultAttachment = new Attachment();
327 resultAttachment.setId(attachment.getId());
328 resultAttachment.setMimeType(encData.getAttributeNS(null, "MimeType"));
329 resultAttachment.setSourceStream(attachmentInputStream);
330 resultAttachment.addHeaders(attachment.getHeaders());
331
332 String typeStr = encData.getAttributeNS(null, "Type");
333 if (WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE.equals(typeStr)) {
334 AttachmentUtils.readAndReplaceEncryptedAttachmentHeaders(
335 resultAttachment.getHeaders(), attachmentInputStream);
336 }
337
338 AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
339 attachmentResultCallback.setAttachment(resultAttachment);
340 attachmentResultCallback.setAttachmentId(resultAttachment.getId());
341 attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
342
343 } catch (UnsupportedCallbackException | IOException
344 | NoSuchAlgorithmException | NoSuchPaddingException e) {
345 throw new WSSecurityException(
346 WSSecurityException.ErrorCode.FAILED_CHECK, e);
347 }
348
349 dataRef.setContent(true);
350
351 encData.getParentNode().removeChild(encData);
352
353 return dataRef;
354 }
355
356
357 private static Node decryptXopAttachment(
358 SecretKey symmetricKey, String symEncAlgo, CallbackHandler attachmentCallbackHandler,
359 String xopURI, Element encData
360 ) throws WSSecurityException, IOException, UnsupportedCallbackException, NoSuchAlgorithmException,
361 NoSuchPaddingException, XMLParserException {
362
363 if (attachmentCallbackHandler == null) {
364 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
365 }
366 final String attachmentId = AttachmentUtils.getAttachmentId(xopURI);
367
368 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
369 attachmentRequestCallback.setAttachmentId(attachmentId);
370
371 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
372 List<Attachment> attachments = attachmentRequestCallback.getAttachments();
373 if (attachments == null || attachments.isEmpty() || !attachmentId.equals(attachments.get(0).getId())) {
374 throw new WSSecurityException(
375 WSSecurityException.ErrorCode.INVALID_SECURITY,
376 "empty", new Object[] {"Attachment not found"}
377 );
378 }
379 Attachment attachment = attachments.get(0);
380
381 final String jceAlgorithm =
382 JCEMapper.translateURItoJCEID(symEncAlgo);
383 final Cipher cipher = Cipher.getInstance(jceAlgorithm);
384
385 InputStream attachmentInputStream =
386 AttachmentUtils.setupAttachmentDecryptionStream(
387 symEncAlgo, cipher, symmetricKey, attachment.getSourceStream());
388
389
390
391 byte[] bytes = JavaUtils.getBytesFromStream(attachmentInputStream);
392
393 Document document = null;
394 try {
395 document = org.apache.xml.security.utils.XMLUtils.read(new ByteArrayInputStream(bytes), true);
396 } catch (XMLParserException ex) {
397 if (ex.getCause() instanceof SAXException) {
398
399 String fixedElementStr = setParentPrefixes(encData, new String(bytes));
400 document = org.apache.xml.security.utils.XMLUtils.read(
401 new ByteArrayInputStream(fixedElementStr.getBytes()), true);
402 } else {
403 throw ex;
404 }
405 }
406
407 Node decryptedNode =
408 encData.getOwnerDocument().importNode(document.getDocumentElement(), true);
409 encData.getParentNode().appendChild(decryptedNode);
410 encData.getParentNode().removeChild(encData);
411 return decryptedNode;
412 }
413
414
415
416
417 private static String setParentPrefixes(Element target, String str) {
418 Node parent = target;
419
420
421 int insertionIndex = str.indexOf('>');
422 StringBuilder prefix = new StringBuilder(str.substring(0, insertionIndex));
423 StringBuilder suffix = new StringBuilder(str.substring(insertionIndex, str.length()));
424
425
426 int prefixAddedCount = 0;
427 while (parent.getParentNode() != null && prefixAddedCount < 20
428 && Node.DOCUMENT_NODE != parent.getParentNode().getNodeType()) {
429 parent = parent.getParentNode();
430 NamedNodeMap attributes = parent.getAttributes();
431 int length = attributes.getLength();
432 for (int i = 0; i < length; i++) {
433 Node attribute = attributes.item(i);
434 String attrDef = "xmlns:" + attribute.getLocalName();
435 if (WSConstants.XMLNS_NS.equals(attribute.getNamespaceURI()) && !prefix.toString().contains(attrDef)) {
436 attrDef += "=\"" + attribute.getNodeValue() + "\"";
437 prefix.append(' ').append(attrDef);
438 prefixAddedCount++;
439 }
440 if (prefixAddedCount >= 20) {
441 break;
442 }
443 }
444 }
445
446 return prefix.toString() + suffix.toString();
447 }
448
449
450
451
452
453
454
455
456 public static String getXPath(Node decryptedNode) {
457 if (decryptedNode == null) {
458 return null;
459 }
460
461 String result = "";
462 if (Node.ELEMENT_NODE == decryptedNode.getNodeType()) {
463 result = decryptedNode.getNodeName();
464 result = prependFullPath(result, decryptedNode.getParentNode());
465 } else if (Node.ATTRIBUTE_NODE == decryptedNode.getNodeType()) {
466 result = "@" + decryptedNode.getNodeName();
467 result = prependFullPath(result, ((Attr)decryptedNode).getOwnerElement());
468 } else {
469 return null;
470 }
471
472 return result;
473 }
474
475
476
477
478
479
480
481
482
483 private static String prependFullPath(String xpath, Node node) {
484 if (node == null) {
485
486 return null;
487 } else if (Node.ELEMENT_NODE == node.getNodeType()) {
488 xpath = node.getNodeName() + "/" + xpath;
489 return prependFullPath(xpath, node.getParentNode());
490 } else if (Node.DOCUMENT_NODE == node.getNodeType()) {
491 return "/" + xpath;
492 } else {
493 return prependFullPath(xpath, node.getParentNode());
494 }
495 }
496
497 public static String getDigestAlgorithm(Node encBodyData) throws WSSecurityException {
498 Element tmpE =
499 XMLUtils.getDirectChildElement(
500 encBodyData, "EncryptionMethod", WSConstants.ENC_NS
501 );
502 if (tmpE != null) {
503 Element digestElement =
504 XMLUtils.getDirectChildElement(tmpE, "DigestMethod", WSConstants.SIG_NS);
505 if (digestElement != null) {
506 return digestElement.getAttributeNS(null, "Algorithm");
507 }
508 }
509 return null;
510 }
511
512 public static String getMGFAlgorithm(Node encBodyData) throws WSSecurityException {
513 Element tmpE =
514 XMLUtils.getDirectChildElement(
515 encBodyData, "EncryptionMethod", WSConstants.ENC_NS
516 );
517 if (tmpE != null) {
518 Element mgfElement =
519 XMLUtils.getDirectChildElement(tmpE, "MGF", WSConstants.ENC11_NS);
520 if (mgfElement != null) {
521 return mgfElement.getAttributeNS(null, "Algorithm");
522 }
523 }
524 return null;
525 }
526
527 public static byte[] getPSource(Node encBodyData) throws WSSecurityException {
528 Element tmpE =
529 XMLUtils.getDirectChildElement(
530 encBodyData, "EncryptionMethod", WSConstants.ENC_NS
531 );
532 if (tmpE != null) {
533 Element pSourceElement =
534 XMLUtils.getDirectChildElement(tmpE, "OAEPparams", WSConstants.ENC_NS);
535 if (pSourceElement != null) {
536 return getDecodedBase64EncodedData(pSourceElement);
537 }
538 }
539 return new byte[0];
540 }
541
542
543
544
545
546
547
548
549 public static byte[] getDecodedBase64EncodedData(Element element) throws WSSecurityException {
550 String text = XMLUtils.getElementText(element);
551 if (text == null) {
552 return new byte[0];
553 }
554 return org.apache.xml.security.utils.XMLUtils.decode(text);
555 }
556 }