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.message;
21
22 import java.io.IOException;
23 import java.security.spec.AlgorithmParameterSpec;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.crypto.BadPaddingException;
30 import javax.crypto.Cipher;
31 import javax.crypto.IllegalBlockSizeException;
32 import javax.crypto.SecretKey;
33 import javax.security.auth.callback.Callback;
34 import javax.security.auth.callback.CallbackHandler;
35 import javax.security.auth.callback.UnsupportedCallbackException;
36
37 import org.apache.wss4j.common.WSEncryptionPart;
38 import org.apache.wss4j.common.ext.Attachment;
39 import org.apache.wss4j.common.ext.AttachmentRequestCallback;
40 import org.apache.wss4j.common.ext.AttachmentResultCallback;
41 import org.apache.wss4j.common.ext.WSSecurityException;
42 import org.apache.wss4j.common.util.AttachmentUtils;
43 import org.apache.wss4j.common.util.XMLUtils;
44 import org.apache.wss4j.dom.WSConstants;
45 import org.apache.wss4j.dom.WSDocInfo;
46 import org.apache.wss4j.dom.WsuIdAllocator;
47 import org.apache.wss4j.dom.callback.CallbackLookup;
48 import org.apache.wss4j.dom.callback.DOMCallbackLookup;
49 import org.apache.wss4j.dom.util.WSSecurityUtil;
50 import org.apache.xml.security.algorithms.JCEMapper;
51 import org.apache.xml.security.encryption.AbstractSerializer;
52 import org.apache.xml.security.encryption.EncryptedData;
53 import org.apache.xml.security.encryption.Serializer;
54 import org.apache.xml.security.encryption.TransformSerializer;
55 import org.apache.xml.security.encryption.XMLCipher;
56 import org.apache.xml.security.encryption.XMLCipherUtil;
57 import org.apache.xml.security.encryption.XMLEncryptionException;
58 import org.apache.xml.security.keys.KeyInfo;
59 import org.apache.xml.security.stax.ext.XMLSecurityConstants;
60 import org.apache.xml.security.utils.EncryptionConstants;
61 import org.w3c.dom.Attr;
62 import org.w3c.dom.Document;
63 import org.w3c.dom.Element;
64 import org.w3c.dom.NamedNodeMap;
65 import org.w3c.dom.Node;
66 import org.w3c.dom.NodeList;
67
68
69
70
71 public class Encryptor {
72
73 private Document doc;
74 private WSSecHeader securityHeader;
75 private WsuIdAllocator idAllocator;
76 private CallbackLookup callbackLookup;
77 private CallbackHandler attachmentCallbackHandler;
78 private boolean storeBytesInAttachment;
79 private Serializer encryptionSerializer;
80 private boolean expandXopInclude;
81 private WSDocInfo wsDocInfo;
82
83 public List<String> doEncryption(
84 KeyInfo keyInfo,
85 SecretKey secretKey,
86 String encryptionAlgorithm,
87 List<WSEncryptionPart> references,
88 List<Element> attachmentEncryptedDataElements
89 ) throws WSSecurityException {
90
91 XMLCipher xmlCipher = null;
92 try {
93 if (encryptionSerializer != null) {
94 xmlCipher = XMLCipher.getInstance(encryptionSerializer, encryptionAlgorithm);
95 } else {
96 xmlCipher = XMLCipher.getInstance(encryptionAlgorithm);
97 }
98 } catch (XMLEncryptionException ex) {
99 throw new WSSecurityException(
100 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, ex
101 );
102 }
103
104 List<String> encDataRef = new ArrayList<>();
105 WSEncryptionPart attachmentEncryptionPart = null;
106 for (WSEncryptionPart encPart : references) {
107 if (encPart.getId() != null && encPart.getId().startsWith("cid:")) {
108 attachmentEncryptionPart = encPart;
109 continue;
110 }
111
112
113
114
115 if (callbackLookup == null) {
116 callbackLookup = new DOMCallbackLookup(doc);
117 }
118 List<Element> elementsToEncrypt =
119 WSSecurityUtil.findElements(encPart, callbackLookup);
120 if (elementsToEncrypt == null || elementsToEncrypt.isEmpty()) {
121 if (!encPart.isRequired()) {
122 continue;
123 }
124 throw new WSSecurityException(
125 WSSecurityException.ErrorCode.FAILURE,
126 "noEncElement",
127 new Object[] {"{" + encPart.getNamespace() + "}" + encPart.getName()});
128 }
129
130 if (expandXopInclude) {
131 for (Element elementToEncrypt : elementsToEncrypt) {
132 Element encrElement = elementToEncrypt;
133
134
135 List<Element> includeElements =
136 XMLUtils.findElements(elementToEncrypt.getFirstChild(), "Include", WSConstants.XOP_NS);
137 if (includeElements != null && !includeElements.isEmpty()) {
138
139 Element matchingElement = findMatchingExpandedElement(encrElement);
140 if (matchingElement != null && matchingElement != encrElement) {
141
142 encrElement.getParentNode().replaceChild(matchingElement, encrElement);
143 encrElement = matchingElement;
144
145
146 for (Element includeElement : includeElements) {
147 String xopURI = includeElement.getAttributeNS(null, "href");
148 if (xopURI != null) {
149
150
151 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
152 attachmentRequestCallback.setAttachmentId(WSSecurityUtil.getAttachmentId(xopURI));
153
154 try {
155 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
156 } catch (UnsupportedCallbackException | IOException e) {
157 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, e);
158 }
159 }
160 }
161 } else {
162
163 WSSecurityUtil.inlineAttachments(includeElements, attachmentCallbackHandler, true);
164 }
165 }
166
167 if (storeBytesInAttachment) {
168 try {
169 String id =
170 encryptElementInAttachment(keyInfo, secretKey, encryptionAlgorithm, encPart, encrElement);
171 encPart.setEncId(id);
172 encDataRef.add("#" + id);
173 } catch (Exception ex) {
174 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex);
175 }
176 } else {
177 String id =
178 encryptElement(encrElement, encPart.getEncModifier(), xmlCipher, secretKey, keyInfo);
179 encPart.setEncId(id);
180 encDataRef.add("#" + id);
181 }
182 }
183 } else if (storeBytesInAttachment) {
184 for (Element elementToEncrypt : elementsToEncrypt) {
185 try {
186 String id =
187 encryptElementInAttachment(keyInfo, secretKey, encryptionAlgorithm, encPart, elementToEncrypt);
188 encPart.setEncId(id);
189 encDataRef.add("#" + id);
190 } catch (Exception ex) {
191 throw new WSSecurityException(
192 WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex
193 );
194 }
195 }
196 } else {
197 for (Element elementToEncrypt : elementsToEncrypt) {
198 String id =
199 encryptElement(elementToEncrypt, encPart.getEncModifier(), xmlCipher, secretKey, keyInfo);
200 encPart.setEncId(id);
201 encDataRef.add("#" + id);
202 }
203 }
204 }
205
206 if (attachmentEncryptionPart != null) {
207 encryptAttachment(keyInfo, secretKey, encryptionAlgorithm, attachmentEncryptionPart, encDataRef,
208 attachmentEncryptedDataElements);
209 }
210
211 return encDataRef;
212 }
213
214 private Element findMatchingExpandedElement(Element element) {
215 Element matchingElement = null;
216
217 if (element.hasAttributeNS(WSConstants.WSU_NS, "Id")) {
218 String id = element.getAttributeNS(WSConstants.WSU_NS, "Id");
219 matchingElement = wsDocInfo.getTokenElement(id);
220 }
221
222 if (matchingElement == null && element.hasAttributeNS(null, "Id")) {
223 String id = element.getAttributeNS(null, "Id");
224 matchingElement = wsDocInfo.getTokenElement(id);
225 }
226
227
228 if (matchingElement != null && matchingElement.getNamespaceURI().equals(element.getNamespaceURI())
229 && matchingElement.getLocalName().equals(element.getLocalName())) {
230 return matchingElement;
231 }
232
233 return null;
234 }
235
236 private String encryptElementInAttachment(
237 KeyInfo keyInfo,
238 SecretKey secretKey,
239 String encryptionAlgorithm,
240 WSEncryptionPart encryptionPart,
241 Element elementToEncrypt
242 ) throws Exception {
243
244 String type = EncryptionConstants.TYPE_ELEMENT;
245 if ("Content".equals(encryptionPart.getEncModifier())) {
246 type = EncryptionConstants.TYPE_CONTENT;
247 }
248
249 final String attachmentId = idAllocator.createId("", doc);
250 String encEncryptedDataId = idAllocator.createId("ED-", attachmentId);
251
252 if ("Header".equals(encryptionPart.getEncModifier())
253 && elementToEncrypt.getParentNode().equals(WSSecurityUtil.getSOAPHeader(doc))) {
254 createEncryptedHeaderElement(securityHeader, elementToEncrypt, idAllocator);
255 }
256
257 Element encryptedData =
258 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedData");
259 encryptedData.setAttributeNS(null, "Id", encEncryptedDataId);
260 encryptedData.setAttributeNS(null, "Type", type);
261
262 Element encryptionMethod =
263 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod");
264 encryptionMethod.setAttributeNS(null, "Algorithm", encryptionAlgorithm);
265
266 encryptedData.appendChild(encryptionMethod);
267 encryptedData.appendChild(WSSecurityUtil.cloneElement(doc, keyInfo.getElement()));
268
269 Element cipherData =
270 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData");
271 Element cipherValue =
272 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherValue");
273 cipherData.appendChild(cipherValue);
274 encryptedData.appendChild(cipherData);
275
276 Cipher cipher = createCipher(encryptionAlgorithm, secretKey);
277
278
279 AbstractSerializer serializer = new TransformSerializer(true);
280
281 byte[] serializedOctets = null;
282 if (type.equals(EncryptionConstants.TYPE_CONTENT)) {
283 NodeList children = elementToEncrypt.getChildNodes();
284 if (null != children) {
285 serializedOctets = serializer.serializeToByteArray(children);
286 } else {
287 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION,
288 "Element has no content.");
289 }
290 } else {
291 serializedOctets = serializer.serializeToByteArray(elementToEncrypt);
292 }
293
294 byte[] encryptedBytes = null;
295 try {
296 encryptedBytes = cipher.doFinal(serializedOctets);
297 } catch (IllegalBlockSizeException ibse) {
298 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ibse);
299 } catch (BadPaddingException bpe) {
300 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, bpe);
301 }
302
303
304 byte[] iv = cipher.getIV();
305 byte[] finalEncryptedBytes = new byte[iv.length + encryptedBytes.length];
306 System.arraycopy(iv, 0, finalEncryptedBytes, 0, iv.length);
307 System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, iv.length, encryptedBytes.length);
308
309 if ("Content".equals(encryptionPart.getEncModifier())) {
310 Node child = elementToEncrypt.getFirstChild();
311 while (child != null) {
312 Node sibling = child.getNextSibling();
313 elementToEncrypt.removeChild(child);
314 child = sibling;
315 }
316 elementToEncrypt.appendChild(encryptedData);
317 } else {
318 elementToEncrypt.getParentNode().replaceChild(encryptedData, elementToEncrypt);
319 }
320
321 AttachmentUtils.storeBytesInAttachment(cipherValue, doc, attachmentId,
322 finalEncryptedBytes, attachmentCallbackHandler);
323
324 return encEncryptedDataId;
325 }
326
327 private void encryptAttachment(
328 KeyInfo keyInfo,
329 SecretKey secretKey,
330 String encryptionAlgorithm,
331 WSEncryptionPart attachmentEncryptionPart,
332 List<String> encDataRef,
333 List<Element> attachmentEncryptedDataElements
334 ) throws WSSecurityException {
335 if (attachmentCallbackHandler == null) {
336 throw new WSSecurityException(
337 WSSecurityException.ErrorCode.FAILURE,
338 "empty", new Object[] {"no attachment callbackhandler supplied"}
339 );
340 }
341
342 AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
343 String id = AttachmentUtils.getAttachmentId(attachmentEncryptionPart.getId());
344 attachmentRequestCallback.setAttachmentId(id);
345 try {
346 attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
347 } catch (Exception e) {
348 throw new WSSecurityException(
349 WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e
350 );
351 }
352 String attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_CONTENT_ONLY;
353 if ("Element".equals(attachmentEncryptionPart.getEncModifier())) {
354 attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE;
355 }
356
357 for (Attachment attachment : attachmentRequestCallback.getAttachments()) {
358
359 final String attachmentId = attachment.getId();
360 String encEncryptedDataId = idAllocator.createId("ED-", attachmentId);
361 encDataRef.add("#" + encEncryptedDataId);
362
363 Element encryptedData =
364 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedData");
365 encryptedData.setAttributeNS(null, "Id", encEncryptedDataId);
366 encryptedData.setAttributeNS(null, "MimeType", attachment.getMimeType());
367 encryptedData.setAttributeNS(null, "Type", attachmentEncryptedDataType);
368
369 Element encryptionMethod =
370 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod");
371 encryptionMethod.setAttributeNS(null, "Algorithm", encryptionAlgorithm);
372
373 encryptedData.appendChild(encryptionMethod);
374 encryptedData.appendChild(WSSecurityUtil.cloneElement(doc, keyInfo.getElement()));
375
376 Element cipherData =
377 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData");
378 Element cipherReference =
379 doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherReference");
380 cipherReference.setAttributeNS(null, "URI", "cid:" + attachmentId);
381
382 Element transforms = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":Transforms");
383 Element transform = doc.createElementNS(WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":Transform");
384 transform.setAttributeNS(null, "Algorithm", WSConstants.SWA_ATTACHMENT_CIPHERTEXT_TRANS);
385 transforms.appendChild(transform);
386
387 cipherReference.appendChild(transforms);
388 cipherData.appendChild(cipherReference);
389 encryptedData.appendChild(cipherData);
390
391 attachmentEncryptedDataElements.add(encryptedData);
392
393 Attachment resultAttachment = new Attachment();
394 resultAttachment.setId(attachmentId);
395 resultAttachment.setMimeType("application/octet-stream");
396
397 Cipher cipher = createCipher(encryptionAlgorithm, secretKey);
398
399 Map<String, String> headers = new HashMap<>(attachment.getHeaders());
400 resultAttachment.setSourceStream(
401 AttachmentUtils.setupAttachmentEncryptionStream(
402 cipher, "Element".equals(attachmentEncryptionPart.getEncModifier()),
403 attachment, headers
404 )
405 );
406 resultAttachment.addHeaders(headers);
407
408 AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
409 attachmentResultCallback.setAttachmentId(attachmentId);
410 attachmentResultCallback.setAttachment(resultAttachment);
411 try {
412 attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
413 } catch (Exception e) {
414 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
415 }
416 }
417 }
418
419 private Cipher createCipher(String encryptionAlgorithm, SecretKey secretKey)
420 throws WSSecurityException {
421 String jceAlgorithm = JCEMapper.translateURItoJCEID(encryptionAlgorithm);
422 try {
423 Cipher cipher = Cipher.getInstance(jceAlgorithm);
424
425 int ivLen = JCEMapper.getIVLengthFromURI(encryptionAlgorithm) / 8;
426 byte[] iv = XMLSecurityConstants.generateBytes(ivLen);
427 AlgorithmParameterSpec paramSpec =
428 XMLCipherUtil.constructBlockCipherParameters(encryptionAlgorithm, iv);
429 cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
430
431 return cipher;
432 } catch (Exception e) {
433 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
434 }
435 }
436
437
438
439
440 private String encryptElement(
441 Element elementToEncrypt,
442 String modifier,
443 XMLCipher xmlCipher,
444 SecretKey secretKey,
445 KeyInfo keyInfo
446 ) throws WSSecurityException {
447
448 boolean content = "Content".equals(modifier);
449
450
451
452 String xencEncryptedDataId = idAllocator.createId("ED-", elementToEncrypt);
453 try {
454 if ("Header".equals(modifier)) {
455 String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
456 if (elementToEncrypt.getParentNode().getNamespaceURI().equals(soapNamespace)
457 && WSConstants.ELEM_HEADER.equals(elementToEncrypt.getParentNode().getLocalName())) {
458 createEncryptedHeaderElement(securityHeader, elementToEncrypt, idAllocator);
459 }
460 }
461
462 xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
463 EncryptedData encData = xmlCipher.getEncryptedData();
464 encData.setId(xencEncryptedDataId);
465 encData.setKeyInfo(keyInfo);
466 xmlCipher.doFinal(doc, elementToEncrypt, content);
467 return xencEncryptedDataId;
468 } catch (Exception ex) {
469 throw new WSSecurityException(
470 WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex
471 );
472 }
473 }
474
475 private static void createEncryptedHeaderElement(
476 WSSecHeader securityHeader,
477 Element elementToEncrypt,
478 WsuIdAllocator idAllocator
479 ) {
480 Element elem =
481 elementToEncrypt.getOwnerDocument().createElementNS(
482 WSConstants.WSSE11_NS, "wsse11:" + WSConstants.ENCRYPTED_HEADER
483 );
484 XMLUtils.setNamespace(elem, WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX);
485 String wsuPrefix =
486 XMLUtils.setNamespace(elem, WSConstants.WSU_NS, WSConstants.WSU_PREFIX);
487 String headerId = idAllocator.createId("EH-", elementToEncrypt);
488 elem.setAttributeNS(
489 WSConstants.WSU_NS, wsuPrefix + ":Id", headerId
490 );
491
492
493
494
495
496
497 Node parent = elementToEncrypt.getParentNode();
498 elementToEncrypt = (Element)parent.replaceChild(elem, elementToEncrypt);
499 elem.appendChild(elementToEncrypt);
500
501 if (securityHeader != null) {
502 NamedNodeMap map = securityHeader.getSecurityHeaderElement().getAttributes();
503 for (int i = 0; i < map.getLength(); i++) {
504 Attr attr = (Attr)map.item(i);
505 if (WSConstants.URI_SOAP11_ENV.equals(attr.getNamespaceURI())
506 || WSConstants.URI_SOAP12_ENV.equals(attr.getNamespaceURI())) {
507 String soapEnvPrefix =
508 XMLUtils.setNamespace(
509 elem, attr.getNamespaceURI(), WSConstants.DEFAULT_SOAP_PREFIX
510 );
511 elem.setAttributeNS(
512 attr.getNamespaceURI(),
513 soapEnvPrefix + ":" + attr.getLocalName(),
514 attr.getValue()
515 );
516 }
517 }
518 }
519
520 }
521
522 public Document getDoc() {
523 return doc;
524 }
525
526 public void setDoc(Document doc) {
527 this.doc = doc;
528 }
529
530 public WSSecHeader getSecurityHeader() {
531 return securityHeader;
532 }
533
534 public void setSecurityHeader(WSSecHeader securityHeader) {
535 this.securityHeader = securityHeader;
536 }
537
538 public WsuIdAllocator getIdAllocator() {
539 return idAllocator;
540 }
541
542 public void setIdAllocator(WsuIdAllocator idAllocator) {
543 this.idAllocator = idAllocator;
544 }
545
546 public CallbackLookup getCallbackLookup() {
547 return callbackLookup;
548 }
549
550 public void setCallbackLookup(CallbackLookup callbackLookup) {
551 this.callbackLookup = callbackLookup;
552 }
553
554 public CallbackHandler getAttachmentCallbackHandler() {
555 return attachmentCallbackHandler;
556 }
557
558 public void setAttachmentCallbackHandler(CallbackHandler attachmentCallbackHandler) {
559 this.attachmentCallbackHandler = attachmentCallbackHandler;
560 }
561
562 public boolean isStoreBytesInAttachment() {
563 return storeBytesInAttachment;
564 }
565
566 public void setStoreBytesInAttachment(boolean storeBytesInAttachment) {
567 this.storeBytesInAttachment = storeBytesInAttachment;
568 }
569
570 public Serializer getEncryptionSerializer() {
571 return encryptionSerializer;
572 }
573
574 public void setEncryptionSerializer(Serializer encryptionSerializer) {
575 this.encryptionSerializer = encryptionSerializer;
576 }
577
578 public boolean isExpandXopInclude() {
579 return expandXopInclude;
580 }
581
582 public void setExpandXopInclude(boolean expandXopInclude) {
583 this.expandXopInclude = expandXopInclude;
584 }
585
586 public WSDocInfo getWsDocInfo() {
587 return wsDocInfo;
588 }
589
590 public void setWsDocInfo(WSDocInfo wsDocInfo) {
591 this.wsDocInfo = wsDocInfo;
592 }
593
594 }