1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.encryption;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.InputStream;
21 import java.security.Key;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Set;
28
29 import org.apache.xml.security.Init;
30 import org.apache.xml.security.encryption.XMLCipher;
31 import org.apache.xml.security.encryption.XMLEncryptionException;
32 import org.opensaml.xml.Configuration;
33 import org.opensaml.xml.XMLObject;
34 import org.opensaml.xml.io.Marshaller;
35 import org.opensaml.xml.io.MarshallingException;
36 import org.opensaml.xml.io.UnmarshallerFactory;
37 import org.opensaml.xml.io.UnmarshallingException;
38 import org.opensaml.xml.parse.BasicParserPool;
39 import org.opensaml.xml.parse.XMLParserException;
40 import org.opensaml.xml.security.Criteria;
41 import org.opensaml.xml.security.CriteriaSet;
42 import org.opensaml.xml.security.SecurityException;
43 import org.opensaml.xml.security.SecurityHelper;
44 import org.opensaml.xml.security.credential.Credential;
45 import org.opensaml.xml.security.credential.UsageType;
46 import org.opensaml.xml.security.criteria.KeyAlgorithmCriteria;
47 import org.opensaml.xml.security.criteria.KeyLengthCriteria;
48 import org.opensaml.xml.security.criteria.UsageCriteria;
49 import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
50 import org.opensaml.xml.security.keyinfo.KeyInfoCriteria;
51 import org.opensaml.xml.signature.DigestMethod;
52 import org.opensaml.xml.signature.SignatureConstants;
53 import org.opensaml.xml.util.DatatypeHelper;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.DocumentFragment;
58 import org.w3c.dom.Element;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public class Decrypter {
168
169
170 private final BasicParserPool parserPool;
171
172
173 private UnmarshallerFactory unmarshallerFactory;
174
175
176
177
178 private final Logger log = LoggerFactory.getLogger(Decrypter.class);
179
180
181 private KeyInfoCredentialResolver resolver;
182
183
184 private KeyInfoCredentialResolver kekResolver;
185
186
187 private EncryptedKeyResolver encKeyResolver;
188
189
190 private CriteriaSet resolverCriteria;
191
192
193 private CriteriaSet kekResolverCriteria;
194
195
196 private String jcaProviderName;
197
198
199
200 private boolean defaultRootInNewDocument;
201
202
203
204
205
206
207
208
209
210 public Decrypter(KeyInfoCredentialResolver newResolver, KeyInfoCredentialResolver newKEKResolver,
211 EncryptedKeyResolver newEncKeyResolver) {
212 resolver = newResolver;
213 kekResolver = newKEKResolver;
214 encKeyResolver = newEncKeyResolver;
215
216 resolverCriteria = null;
217 kekResolverCriteria = null;
218
219
220 parserPool = new BasicParserPool();
221 parserPool.setNamespaceAware(true);
222
223
224 HashMap<String, Boolean> features = new HashMap<String, Boolean>();
225 features.put("http://apache.org/xml/features/dom/defer-node-expansion", Boolean.FALSE);
226 parserPool.setBuilderFeatures(features);
227
228 unmarshallerFactory = Configuration.getUnmarshallerFactory();
229
230 defaultRootInNewDocument = false;
231 }
232
233
234
235
236
237
238
239 public boolean isRootInNewDocument() {
240 return defaultRootInNewDocument;
241 }
242
243
244
245
246
247
248
249 public void setRootInNewDocument(boolean flag) {
250 defaultRootInNewDocument = flag;
251 }
252
253
254
255
256
257
258
259
260
261
262 public String getJCAProviderName() {
263 return jcaProviderName;
264 }
265
266
267
268
269
270
271
272
273
274
275 public void setJCAProviderName(String providerName) {
276 jcaProviderName = providerName;
277 }
278
279
280
281
282
283
284 public KeyInfoCredentialResolver getKeyResolver() {
285 return resolver;
286 }
287
288
289
290
291
292
293 public void setKeyResolver(KeyInfoCredentialResolver newResolver) {
294 resolver = newResolver;
295 }
296
297
298
299
300
301
302 public KeyInfoCredentialResolver getKEKResolver() {
303 return kekResolver;
304 }
305
306
307
308
309
310
311 public void setKEKResolver(KeyInfoCredentialResolver newKEKResolver) {
312 kekResolver = newKEKResolver;
313 }
314
315
316
317
318
319
320 public EncryptedKeyResolver getEncryptedKeyResolver() {
321 return encKeyResolver;
322 }
323
324
325
326
327
328
329 public void setEncryptedKeyResolver(EncryptedKeyResolver newResolver) {
330 encKeyResolver = newResolver;
331 }
332
333
334
335
336
337
338
339 public CriteriaSet setKeyResolverCriteria() {
340 return resolverCriteria;
341 }
342
343
344
345
346
347
348
349 public void setKeyResolverCriteria(CriteriaSet newCriteria) {
350 resolverCriteria = newCriteria;
351 }
352
353
354
355
356
357
358
359 public CriteriaSet getKEKResolverCriteria() {
360 return kekResolverCriteria;
361 }
362
363
364
365
366
367
368
369 public void setKEKResolverCriteria(CriteriaSet newCriteria) {
370 kekResolverCriteria = newCriteria;
371 }
372
373
374
375
376
377
378
379
380
381
382 public XMLObject decryptData(EncryptedData encryptedData) throws DecryptionException {
383 return decryptData(encryptedData, isRootInNewDocument());
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397
398 public XMLObject decryptData(EncryptedData encryptedData, boolean rootInNewDocument) throws DecryptionException {
399
400 List<XMLObject> xmlObjects = decryptDataToList(encryptedData, rootInNewDocument);
401 if (xmlObjects.size() != 1) {
402 log.error("The decrypted data contained more than one top-level XMLObject child");
403 throw new DecryptionException("The decrypted data contained more than one XMLObject child");
404 }
405
406 return xmlObjects.get(0);
407 }
408
409
410
411
412
413
414
415
416
417
418 public List<XMLObject> decryptDataToList(EncryptedData encryptedData) throws DecryptionException {
419 return decryptDataToList(encryptedData, isRootInNewDocument());
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 public List<XMLObject> decryptDataToList(EncryptedData encryptedData, boolean rootInNewDocument)
436 throws DecryptionException {
437 List<XMLObject> xmlObjects = new LinkedList<XMLObject>();
438
439 DocumentFragment docFragment = decryptDataToDOM(encryptedData);
440
441 XMLObject xmlObject;
442 Node node;
443 Element element;
444
445 NodeList children = docFragment.getChildNodes();
446 for (int i = 0; i < children.getLength(); i++) {
447 node = children.item(i);
448 if (node.getNodeType() != Node.ELEMENT_NODE) {
449 log.error("Decryption returned a top-level node that was not of type Element: " + node.getNodeType());
450 throw new DecryptionException("Top-level node was not of type Element");
451 } else {
452 element = (Element) node;
453 if (rootInNewDocument) {
454 Document newDoc = null;
455 try {
456 newDoc = parserPool.newDocument();
457 } catch (XMLParserException e) {
458 log.error("There was an error creating a new DOM Document", e);
459 throw new DecryptionException("Error creating new DOM Document", e);
460 }
461 newDoc.adoptNode(element);
462 newDoc.appendChild(element);
463 }
464 }
465
466 try {
467 xmlObject = unmarshallerFactory.getUnmarshaller(element).unmarshall(element);
468 } catch (UnmarshallingException e) {
469 log.error("There was an error during unmarshalling of the decrypted element", e);
470 throw new DecryptionException("Unmarshalling error during decryption", e);
471 }
472
473 xmlObjects.add(xmlObject);
474 }
475
476 return xmlObjects;
477 }
478
479
480
481
482
483
484
485
486 public DocumentFragment decryptDataToDOM(EncryptedData encryptedData) throws DecryptionException {
487 if (resolver == null && encKeyResolver == null) {
488 log.error("Decryption can not be attempted, required resolvers are not available");
489 throw new DecryptionException("Unable to decrypt EncryptedData, required resolvers are not available");
490 }
491
492 DocumentFragment docFrag = null;
493
494 if (resolver != null) {
495 docFrag = decryptUsingResolvedKey(encryptedData);
496 if (docFrag != null) {
497 return docFrag;
498 } else {
499 log.debug("Failed to decrypt EncryptedData using standard KeyInfo resolver");
500 }
501 }
502
503 String algorithm = encryptedData.getEncryptionMethod().getAlgorithm();
504 if (DatatypeHelper.isEmpty(algorithm)) {
505 String msg = "EncryptedData's EncryptionMethod Algorithm attribute was empty, "
506 + "key decryption could not be attempted";
507 log.error(msg);
508 throw new DecryptionException(msg);
509 }
510
511 if (encKeyResolver != null) {
512 docFrag = decryptUsingResolvedEncryptedKey(encryptedData, algorithm);
513 if (docFrag != null) {
514 return docFrag;
515 } else {
516 log.debug("Failed to decrypt EncryptedData using EncryptedKeyResolver");
517 }
518 }
519
520 log.error("Failed to decrypt EncryptedData using either EncryptedData KeyInfoCredentialResolver "
521 + "or EncryptedKeyResolver + EncryptedKey KeyInfoCredentialResolver");
522
523 throw new DecryptionException("Failed to decrypt EncryptedData");
524 }
525
526
527
528
529
530
531
532
533
534
535 public DocumentFragment decryptDataToDOM(EncryptedData encryptedData, Key dataEncKey) throws DecryptionException {
536
537
538
539
540 if (!EncryptionConstants.TYPE_ELEMENT.equals(encryptedData.getType())) {
541 log.error("EncryptedData was of unsupported type '" + encryptedData.getType()
542 + "', could not attempt decryption");
543 throw new DecryptionException("EncryptedData of unsupported type was encountered");
544 }
545 if (dataEncKey == null) {
546 log.error("Data decryption key was null");
547 throw new IllegalArgumentException("Data decryption key may not be null");
548 }
549
550 try {
551 checkAndMarshall(encryptedData);
552 } catch (DecryptionException e) {
553 log.error("Error marshalling EncryptedData for decryption", e);
554 throw e;
555 }
556 Element targetElement = encryptedData.getDOM();
557
558 XMLCipher xmlCipher;
559 try {
560 if (getJCAProviderName() != null) {
561 xmlCipher = XMLCipher.getProviderInstance(getJCAProviderName());
562 } else {
563 xmlCipher = XMLCipher.getInstance();
564 }
565 xmlCipher.init(XMLCipher.DECRYPT_MODE, dataEncKey);
566 } catch (XMLEncryptionException e) {
567 log.error("Error initialzing cipher instance on data decryption", e);
568 throw new DecryptionException("Error initialzing cipher instance on data decryption", e);
569 }
570
571 byte[] bytes = null;
572 try {
573 bytes = xmlCipher.decryptToByteArray(targetElement);
574 } catch (XMLEncryptionException e) {
575 log.error("Error decrypting the encrypted data element", e);
576 throw new DecryptionException("Error decrypting the encrypted data element", e);
577 }
578 if (bytes == null) {
579 throw new DecryptionException("EncryptedData could not be decrypted");
580 }
581 ByteArrayInputStream input = new ByteArrayInputStream(bytes);
582 DocumentFragment docFragment = parseInputStream(input, encryptedData.getDOM().getOwnerDocument());
583 return docFragment;
584 }
585
586
587
588
589
590
591
592
593
594
595
596 public Key decryptKey(EncryptedKey encryptedKey, String algorithm) throws DecryptionException {
597 if (kekResolver == null) {
598 log.warn("No KEK KeyInfo credential resolver is available, can not attempt EncryptedKey decryption");
599 throw new DecryptionException("No KEK KeyInfo resolver is available for EncryptedKey decryption");
600 }
601
602 if (DatatypeHelper.isEmpty(algorithm)) {
603 log.error("Algorithm of encrypted key not supplied, key decryption cannot proceed.");
604 throw new DecryptionException("Algorithm of encrypted key not supplied, key decryption cannot proceed.");
605 }
606
607 CriteriaSet criteriaSet = buildCredentialCriteria(encryptedKey, kekResolverCriteria);
608 try {
609 for (Credential cred : kekResolver.resolve(criteriaSet)) {
610 try {
611 return decryptKey(encryptedKey, algorithm, SecurityHelper.extractDecryptionKey(cred));
612 } catch (DecryptionException e) {
613 String msg = "Attempt to decrypt EncryptedKey using credential from KEK KeyInfo resolver failed: ";
614 log.debug(msg, e);
615 continue;
616 }
617 }
618 } catch (SecurityException e) {
619 log.error("Error resolving credentials from EncryptedKey KeyInfo", e);
620 }
621
622 log.error("Failed to decrypt EncryptedKey, valid decryption key could not be resolved");
623 throw new DecryptionException("Valid decryption key for EncryptedKey could not be resolved");
624 }
625
626
627
628
629
630
631
632
633
634
635
636 public Key decryptKey(EncryptedKey encryptedKey, String algorithm, Key kek) throws DecryptionException {
637 if (kek == null) {
638 log.error("Data encryption key was null");
639 throw new IllegalArgumentException("Data encryption key may not be null");
640 }
641 if (DatatypeHelper.isEmpty(algorithm)) {
642 log.error("Algorithm of encrypted key not supplied, key decryption cannot proceed.");
643 throw new DecryptionException("Algorithm of encrypted key not supplied, key decryption cannot proceed.");
644 }
645
646 try {
647 checkAndMarshall(encryptedKey);
648 } catch (DecryptionException e) {
649 log.error("Error marshalling EncryptedKey for decryption", e);
650 throw e;
651 }
652
653 preProcessEncryptedKey(encryptedKey, algorithm, kek);
654
655 Element targetElement = encryptedKey.getDOM();
656
657 XMLCipher xmlCipher;
658 try {
659 if (getJCAProviderName() != null) {
660 xmlCipher = XMLCipher.getProviderInstance(getJCAProviderName());
661 } else {
662 xmlCipher = XMLCipher.getInstance();
663 }
664 xmlCipher.init(XMLCipher.UNWRAP_MODE, kek);
665 } catch (XMLEncryptionException e) {
666 log.error("Error initialzing cipher instance on key decryption", e);
667 throw new DecryptionException("Error initialzing cipher instance on key decryption", e);
668 }
669
670 org.apache.xml.security.encryption.EncryptedKey encKey;
671 try {
672 encKey = xmlCipher.loadEncryptedKey(targetElement.getOwnerDocument(), targetElement);
673 } catch (XMLEncryptionException e) {
674 log.error("Error when loading library native encrypted key representation", e);
675 throw new DecryptionException("Error when loading library native encrypted key representation", e);
676 }
677
678 Key key = null;
679 try {
680 key = xmlCipher.decryptKey(encKey, algorithm);
681 } catch (XMLEncryptionException e) {
682 log.error("Error decrypting encrypted key", e);
683 throw new DecryptionException("Error decrypting encrypted key", e);
684 }
685 if (key == null) {
686 throw new DecryptionException("Key could not be decrypted");
687 }
688 return key;
689 }
690
691
692
693
694
695
696
697
698
699
700 protected void preProcessEncryptedKey(EncryptedKey encryptedKey, String algorithm, Key kek)
701 throws DecryptionException {
702
703
704
705 String keyTransportAlgorithm = encryptedKey.getEncryptionMethod().getAlgorithm();
706 if (EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP.equals(keyTransportAlgorithm)) {
707 List<XMLObject> digestMethods =
708 encryptedKey.getEncryptionMethod().getUnknownXMLObjects(DigestMethod.DEFAULT_ELEMENT_NAME);
709 if (!digestMethods.isEmpty()) {
710 DigestMethod dm = (DigestMethod) digestMethods.get(0);
711 if (! SignatureConstants.ALGO_ID_DIGEST_SHA1
712 .equals(DatatypeHelper.safeTrimOrNullString(dm.getAlgorithm())) ) {
713 log.error("EncryptedKey/EncryptionMethod/DigestMethod contains unsupported algorithm URI: {}",
714 dm.getAlgorithm());
715 throw new DecryptionException(
716 "EncryptedKey/EncryptionMethod/DigestMethod contains unsupported algorithm URI");
717 }
718 }
719 }
720
721 }
722
723
724
725
726
727
728
729 private DocumentFragment decryptUsingResolvedKey(EncryptedData encryptedData) {
730 if (resolver != null) {
731 CriteriaSet criteriaSet = buildCredentialCriteria(encryptedData, resolverCriteria);
732 try {
733 for (Credential cred : resolver.resolve(criteriaSet)) {
734 try {
735 return decryptDataToDOM(encryptedData, SecurityHelper.extractDecryptionKey(cred));
736 } catch (DecryptionException e) {
737 String msg = "Decryption attempt using credential from standard KeyInfo resolver failed: ";
738 log.debug(msg, e);
739 continue;
740 }
741 }
742 } catch (SecurityException e) {
743 log.error("Error resolving credentials from EncryptedData KeyInfo", e);
744 }
745 }
746 return null;
747 }
748
749
750
751
752
753
754
755
756
757 private DocumentFragment decryptUsingResolvedEncryptedKey(EncryptedData encryptedData, String algorithm) {
758 if (encKeyResolver != null) {
759 for (EncryptedKey encryptedKey : encKeyResolver.resolve(encryptedData)) {
760 try {
761 Key decryptedKey = decryptKey(encryptedKey, algorithm);
762 return decryptDataToDOM(encryptedData, decryptedKey);
763 } catch (DecryptionException e) {
764 String msg = "Attempt to decrypt EncryptedData using key extracted from EncryptedKey failed: ";
765 log.debug(msg, e);
766 continue;
767 }
768 }
769 }
770 return null;
771 }
772
773
774
775
776
777
778
779
780
781 private DocumentFragment parseInputStream(InputStream input, Document owningDocument) throws DecryptionException {
782
783
784
785
786
787 Document newDocument = null;
788 try {
789 newDocument = parserPool.parse(input);
790 } catch (XMLParserException e) {
791 log.error("Error parsing decrypted input stream", e);
792 throw new DecryptionException("Error parsing input stream", e);
793 }
794
795 Element element = newDocument.getDocumentElement();
796 owningDocument.adoptNode(element);
797
798 DocumentFragment container = owningDocument.createDocumentFragment();
799 container.appendChild(element);
800
801 return container;
802 }
803
804
805
806
807
808
809
810
811
812 private CriteriaSet buildCredentialCriteria(EncryptedType encryptedType, CriteriaSet staticCriteria) {
813
814 CriteriaSet newCriteriaSet = new CriteriaSet();
815
816
817 newCriteriaSet.add(new KeyInfoCriteria(encryptedType.getKeyInfo()));
818
819
820
821 Set<Criteria> keyCriteria = buildKeyCriteria(encryptedType);
822 if (keyCriteria != null && !keyCriteria.isEmpty()) {
823 newCriteriaSet.addAll(keyCriteria);
824 }
825
826
827 if (staticCriteria != null && !staticCriteria.isEmpty()) {
828 newCriteriaSet.addAll(staticCriteria);
829 }
830
831
832 if (!newCriteriaSet.contains(UsageCriteria.class)) {
833 newCriteriaSet.add(new UsageCriteria(UsageType.ENCRYPTION));
834 }
835
836 return newCriteriaSet;
837 }
838
839
840
841
842
843
844
845 private Set<Criteria> buildKeyCriteria(EncryptedType encryptedType) {
846 EncryptionMethod encMethod = encryptedType.getEncryptionMethod();
847 if (encMethod == null) {
848
849 return Collections.emptySet();
850 }
851 String encAlgorithmURI = DatatypeHelper.safeTrimOrNullString(encMethod.getAlgorithm());
852 if (encAlgorithmURI == null) {
853 return Collections.emptySet();
854 }
855
856 Set<Criteria> critSet = new HashSet<Criteria>(2);
857
858 KeyAlgorithmCriteria algoCrit = buildKeyAlgorithmCriteria(encAlgorithmURI);
859 if (algoCrit != null) {
860 critSet.add(algoCrit);
861 log.debug("Added decryption key algorithm criteria: {}", algoCrit.getKeyAlgorithm());
862 }
863
864 KeyLengthCriteria lengthCrit = buildKeyLengthCriteria(encAlgorithmURI);
865 if (lengthCrit != null) {
866 critSet.add(lengthCrit);
867 log.debug("Added decryption key length criteria from EncryptionMethod algorithm URI: {}", lengthCrit
868 .getKeyLength());
869 } else {
870 if (encMethod.getKeySize() != null && encMethod.getKeySize().getValue() != null) {
871 lengthCrit = new KeyLengthCriteria(encMethod.getKeySize().getValue());
872 critSet.add(lengthCrit);
873 log.debug("Added decryption key length criteria from EncryptionMethod/KeySize: {}", lengthCrit
874 .getKeyLength());
875 }
876 }
877
878 return critSet;
879 }
880
881
882
883
884
885
886
887 private KeyAlgorithmCriteria buildKeyAlgorithmCriteria(String encAlgorithmURI) {
888 if (DatatypeHelper.isEmpty(encAlgorithmURI)) {
889 return null;
890 }
891
892 String jcaKeyAlgorithm = SecurityHelper.getKeyAlgorithmFromURI(encAlgorithmURI);
893 if (!DatatypeHelper.isEmpty(jcaKeyAlgorithm)) {
894 return new KeyAlgorithmCriteria(jcaKeyAlgorithm);
895 }
896
897 return null;
898 }
899
900
901
902
903
904
905
906 private KeyLengthCriteria buildKeyLengthCriteria(String encAlgorithmURI) {
907 if (!DatatypeHelper.isEmpty(encAlgorithmURI)) {
908 return null;
909 }
910
911 Integer keyLength = SecurityHelper.getKeyLengthFromURI(encAlgorithmURI);
912 if (keyLength != null) {
913 return new KeyLengthCriteria(keyLength);
914 }
915
916 return null;
917 }
918
919
920
921
922
923
924
925 protected void checkAndMarshall(XMLObject xmlObject) throws DecryptionException {
926 Element targetElement = xmlObject.getDOM();
927 if (targetElement == null) {
928 Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
929 try {
930 targetElement = marshaller.marshall(xmlObject);
931 } catch (MarshallingException e) {
932 log.error("Error marshalling target XMLObject", e);
933 throw new DecryptionException("Error marshalling target XMLObject", e);
934 }
935 }
936 }
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983 static {
984 if (!Init.isInitialized()) {
985 Init.init();
986 }
987 }
988
989 }