1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.saml.ext;
21
22 import org.apache.ws.security.WSDocInfo;
23 import org.apache.ws.security.WSSecurityException;
24 import org.apache.ws.security.components.crypto.Crypto;
25 import org.apache.ws.security.components.crypto.CryptoType;
26 import org.apache.ws.security.handler.RequestData;
27 import org.apache.ws.security.saml.SAMLKeyInfo;
28 import org.apache.ws.security.saml.SAMLUtil;
29 import org.apache.ws.security.saml.ext.builder.SAML1ComponentBuilder;
30 import org.apache.ws.security.saml.ext.builder.SAML2ComponentBuilder;
31
32 import org.apache.ws.security.util.DOM2Writer;
33 import org.apache.ws.security.util.UUIDGenerator;
34 import org.apache.xml.security.exceptions.XMLSecurityException;
35 import org.apache.xml.security.signature.XMLSignature;
36 import org.apache.xml.security.signature.XMLSignatureException;
37
38 import org.opensaml.common.SAMLVersion;
39 import org.opensaml.common.SignableSAMLObject;
40 import org.opensaml.saml1.core.AttributeStatement;
41 import org.opensaml.saml1.core.AuthenticationStatement;
42 import org.opensaml.saml1.core.AuthorizationDecisionStatement;
43 import org.opensaml.saml1.core.ConfirmationMethod;
44 import org.opensaml.saml1.core.Subject;
45 import org.opensaml.saml1.core.SubjectConfirmation;
46 import org.opensaml.saml1.core.SubjectStatement;
47 import org.opensaml.saml2.core.AuthnStatement;
48 import org.opensaml.saml2.core.AuthzDecisionStatement;
49 import org.opensaml.saml2.core.Issuer;
50 import org.opensaml.security.SAMLSignatureProfileValidator;
51 import org.opensaml.xml.XMLObject;
52 import org.opensaml.xml.security.x509.BasicX509Credential;
53 import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
54 import org.opensaml.xml.signature.KeyInfo;
55 import org.opensaml.xml.signature.Signature;
56 import org.opensaml.xml.signature.SignatureConstants;
57 import org.opensaml.xml.signature.SignatureValidator;
58 import org.opensaml.xml.validation.ValidationException;
59
60 import org.w3c.dom.Document;
61 import org.w3c.dom.Element;
62
63 import javax.security.auth.callback.CallbackHandler;
64 import javax.security.auth.callback.UnsupportedCallbackException;
65
66 import java.io.IOException;
67 import java.security.PrivateKey;
68 import java.security.cert.X509Certificate;
69 import java.util.ArrayList;
70 import java.util.List;
71
72
73
74
75
76
77
78 public class AssertionWrapper {
79
80
81
82 private static final org.apache.commons.logging.Log LOG =
83 org.apache.commons.logging.LogFactory.getLog(AssertionWrapper.class);
84
85
86
87
88 private XMLObject xmlObject = null;
89
90
91
92
93 private org.opensaml.saml1.core.Assertion saml1 = null;
94
95
96
97
98 private org.opensaml.saml2.core.Assertion saml2 = null;
99
100
101
102
103 private SAMLVersion samlVersion;
104
105
106
107
108
109
110
111 private CallbackHandler samlCallbackHandler = null;
112
113
114
115
116 private Element assertionElement;
117
118
119
120
121 private SAMLKeyInfo subjectKeyInfo;
122
123
124
125
126 private SAMLKeyInfo signatureKeyInfo;
127
128
129
130
131 private final String defaultCanonicalizationAlgorithm = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
132
133
134
135
136 private final String defaultRSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
137
138
139
140
141 private final String defaultDSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
142
143
144
145
146 private final boolean fromDOM;
147
148
149
150
151
152
153
154 public AssertionWrapper(Element element) throws WSSecurityException {
155 OpenSAMLUtil.initSamlEngine();
156
157 parseElement(element);
158 fromDOM = true;
159 }
160
161
162
163
164
165
166 public AssertionWrapper(org.opensaml.saml2.core.Assertion saml2) {
167 this((XMLObject)saml2);
168 }
169
170
171
172
173
174
175 public AssertionWrapper(org.opensaml.saml1.core.Assertion saml1) {
176 this((XMLObject)saml1);
177 }
178
179
180
181
182
183
184
185
186
187 public AssertionWrapper(XMLObject xmlObject) {
188 OpenSAMLUtil.initSamlEngine();
189
190 this.xmlObject = xmlObject;
191 if (xmlObject instanceof org.opensaml.saml1.core.Assertion) {
192 this.saml1 = (org.opensaml.saml1.core.Assertion) xmlObject;
193 samlVersion = SAMLVersion.VERSION_11;
194 } else if (xmlObject instanceof org.opensaml.saml2.core.Assertion) {
195 this.saml2 = (org.opensaml.saml2.core.Assertion) xmlObject;
196 samlVersion = SAMLVersion.VERSION_20;
197 } else {
198 LOG.error(
199 "AssertionWrapper: found unexpected type "
200 + (xmlObject != null ? xmlObject.getClass().getName() : null)
201 );
202 }
203 fromDOM = false;
204 }
205
206
207
208
209
210
211
212
213 public AssertionWrapper(SAMLParms parms) throws WSSecurityException {
214 OpenSAMLUtil.initSamlEngine();
215
216
217
218
219
220 SAMLCallback[] samlCallbacks = new SAMLCallback[] { new SAMLCallback() };
221
222 try {
223
224 samlCallbackHandler = parms.getCallbackHandler();
225 samlCallbackHandler.handle(samlCallbacks);
226 } catch (IOException e) {
227 throw new IllegalStateException(
228 "IOException while creating SAML assertion wrapper", e
229 );
230 } catch (UnsupportedCallbackException e) {
231 throw new IllegalStateException(
232 "UnsupportedCallbackException while creating SAML assertion wrapper", e
233 );
234 }
235
236
237 if (samlCallbacks[0].getAssertionElement() != null) {
238 parseElement(samlCallbacks[0].getAssertionElement());
239 fromDOM = true;
240 } else {
241
242 parseCallback(samlCallbacks[0], parms);
243 fromDOM = false;
244 }
245 }
246
247
248
249
250
251
252 public org.opensaml.saml1.core.Assertion getSaml1() {
253 return saml1;
254 }
255
256
257
258
259
260
261 public org.opensaml.saml2.core.Assertion getSaml2() {
262 return saml2;
263 }
264
265
266
267
268
269
270 public XMLObject getXmlObject() {
271 return xmlObject;
272 }
273
274
275
276
277
278
279 public boolean isCreated() {
280 return saml1 != null || saml2 != null;
281 }
282
283
284
285
286
287
288
289
290
291 public Element toDOM(Document doc) throws WSSecurityException {
292 if (fromDOM && assertionElement != null) {
293 parseElement(assertionElement);
294 if (doc != null) {
295 return (Element)doc.importNode(assertionElement, true);
296 }
297 return assertionElement;
298 }
299 assertionElement = OpenSAMLUtil.toDom(xmlObject, doc);
300 return assertionElement;
301 }
302
303
304
305
306
307
308 public String assertionToString() throws WSSecurityException {
309 if (assertionElement == null) {
310 Element element = toDOM(null);
311 return DOM2Writer.nodeToString(element);
312 }
313 return DOM2Writer.nodeToString(assertionElement);
314 }
315
316
317
318
319
320
321 public String getId() {
322 String id = null;
323 if (saml2 != null) {
324 id = saml2.getID();
325 } else if (saml1 != null) {
326 id = saml1.getID();
327 } else {
328 LOG.error("AssertionWrapper: unable to return ID - no saml assertion object");
329 }
330 if (id == null || id.length() == 0) {
331 LOG.error("AssertionWrapper: ID was null, seeting a new ID value");
332 id = "_" + UUIDGenerator.getUUID();
333 if (saml2 != null) {
334 saml2.setID(id);
335 } else if (saml1 != null) {
336 saml1.setID(id);
337 }
338 }
339 return id;
340 }
341
342
343
344
345
346
347 public String getIssuerString() {
348 if (saml2 != null && saml2.getIssuer() != null) {
349 return saml2.getIssuer().getValue();
350 } else if (saml1 != null) {
351 return saml1.getIssuer();
352 }
353 LOG.error(
354 "AssertionWrapper: unable to return Issuer string - no saml assertion "
355 + "object or issuer is null"
356 );
357 return null;
358 }
359
360
361
362
363
364
365
366 public List<String> getConfirmationMethods() {
367 List<String> methods = new ArrayList<String>();
368 if (saml2 != null) {
369 org.opensaml.saml2.core.Subject subject = saml2.getSubject();
370 List<org.opensaml.saml2.core.SubjectConfirmation> confirmations =
371 subject.getSubjectConfirmations();
372 for (org.opensaml.saml2.core.SubjectConfirmation confirmation : confirmations) {
373 methods.add(confirmation.getMethod());
374 }
375 } else if (saml1 != null) {
376 List<SubjectStatement> subjectStatements = new ArrayList<SubjectStatement>();
377 subjectStatements.addAll(saml1.getSubjectStatements());
378 subjectStatements.addAll(saml1.getAuthenticationStatements());
379 subjectStatements.addAll(saml1.getAttributeStatements());
380 subjectStatements.addAll(saml1.getAuthorizationDecisionStatements());
381 for (SubjectStatement subjectStatement : subjectStatements) {
382 Subject subject = subjectStatement.getSubject();
383 if (subject != null) {
384 SubjectConfirmation confirmation = subject.getSubjectConfirmation();
385 if (confirmation != null) {
386 XMLObject data = confirmation.getSubjectConfirmationData();
387 if (data instanceof ConfirmationMethod) {
388 ConfirmationMethod method = (ConfirmationMethod) data;
389 methods.add(method.getConfirmationMethod());
390 }
391 List<ConfirmationMethod> confirmationMethods =
392 confirmation.getConfirmationMethods();
393 for (ConfirmationMethod confirmationMethod : confirmationMethods) {
394 methods.add(confirmationMethod.getConfirmationMethod());
395 }
396 }
397 }
398 }
399 }
400 return methods;
401 }
402
403
404
405
406
407
408 public boolean isSigned() {
409 if (saml2 != null) {
410 return saml2.isSigned() || saml2.getSignature() != null;
411 } else if (saml1 != null) {
412 return saml1.isSigned() || saml1.getSignature() != null;
413 }
414 return false;
415 }
416
417
418
419
420
421
422 public void setSignature(Signature signature) {
423 if (xmlObject instanceof SignableSAMLObject) {
424 SignableSAMLObject signableObject = (SignableSAMLObject) xmlObject;
425 signableObject.setSignature(signature);
426 signableObject.releaseDOM();
427 signableObject.releaseChildrenDOM(true);
428 } else {
429 LOG.error("Attempt to sign an unsignable object " + xmlObject.getClass().getName());
430 }
431 }
432
433
434
435
436
437
438
439
440
441
442 public void signAssertion(String issuerKeyName, String issuerKeyPassword,
443 Crypto issuerCrypto, boolean sendKeyValue)
444 throws WSSecurityException {
445
446 signAssertion(issuerKeyName, issuerKeyPassword, issuerCrypto,
447 sendKeyValue, defaultCanonicalizationAlgorithm,
448 defaultRSASignatureAlgorithm);
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462 public void signAssertion(String issuerKeyName, String issuerKeyPassword,
463 Crypto issuerCrypto, boolean sendKeyValue,
464 String canonicalizationAlgorithm, String signatureAlgorithm)
465 throws WSSecurityException {
466
467
468
469 Signature signature = OpenSAMLUtil.buildSignature();
470 signature.setCanonicalizationAlgorithm(canonicalizationAlgorithm);
471 LOG.debug("Using Canonicalization algorithm " + canonicalizationAlgorithm);
472
473 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
474 cryptoType.setAlias(issuerKeyName);
475 X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
476 if (issuerCerts == null) {
477 throw new WSSecurityException(
478 "No issuer certs were found to sign the SAML Assertion using issuer name: "
479 + issuerKeyName);
480 }
481
482 String sigAlgo = signatureAlgorithm;
483 String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
484 if (LOG.isDebugEnabled()) {
485 LOG.debug("automatic sig algo detection: " + pubKeyAlgo);
486 }
487 if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
488 sigAlgo = defaultDSASignatureAlgorithm;
489 }
490 LOG.debug("Using Signature algorithm " + sigAlgo);
491 PrivateKey privateKey = null;
492 try {
493 privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
494 } catch (Exception ex) {
495 throw new WSSecurityException(ex.getMessage(), ex);
496 }
497
498 signature.setSignatureAlgorithm(sigAlgo);
499
500 BasicX509Credential signingCredential = new BasicX509Credential();
501 signingCredential.setEntityCertificate(issuerCerts[0]);
502 signingCredential.setPrivateKey(privateKey);
503
504 signature.setSigningCredential(signingCredential);
505
506 X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
507 if (sendKeyValue) {
508 kiFactory.setEmitPublicKeyValue(true);
509 } else {
510 kiFactory.setEmitEntityCertificate(true);
511 }
512 try {
513 KeyInfo keyInfo = kiFactory.newInstance().generate(
514 signingCredential);
515 signature.setKeyInfo(keyInfo);
516 } catch (org.opensaml.xml.security.SecurityException ex) {
517 throw new WSSecurityException(
518 "Error generating KeyInfo from signing credential", ex);
519 }
520
521
522 setSignature(signature);
523 }
524
525
526
527
528
529
530 public void verifySignature(
531 RequestData data, WSDocInfo docInfo
532 ) throws WSSecurityException {
533 Signature sig = null;
534 if (saml2 != null && saml2.getSignature() != null) {
535 sig = saml2.getSignature();
536 } else if (saml1 != null && saml1.getSignature() != null) {
537 sig = saml1.getSignature();
538 }
539 if (sig != null) {
540 KeyInfo keyInfo = sig.getKeyInfo();
541 SAMLKeyInfo samlKeyInfo =
542 SAMLUtil.getCredentialDirectlyFromKeyInfo(keyInfo.getDOM(), data);
543 verifySignature(samlKeyInfo);
544 } else {
545 LOG.debug("AssertionWrapper: no signature to validate");
546 }
547
548 }
549
550
551
552
553
554
555 public void verifySignature(SAMLKeyInfo samlKeyInfo) throws WSSecurityException {
556 Signature sig = getSignature();
557 if (sig != null) {
558 if (samlKeyInfo == null) {
559 throw new WSSecurityException(
560 WSSecurityException.FAILURE, "invalidSAMLsecurity",
561 new Object[]{"cannot get certificate or key"}
562 );
563 }
564 SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
565 try {
566 validator.validate(sig);
567 } catch (ValidationException ex) {
568 throw new WSSecurityException("SAML signature validation failed", ex);
569 }
570
571 BasicX509Credential credential = new BasicX509Credential();
572 if (samlKeyInfo.getCerts() != null) {
573 credential.setEntityCertificate(samlKeyInfo.getCerts()[0]);
574 } else if (samlKeyInfo.getPublicKey() != null) {
575 credential.setPublicKey(samlKeyInfo.getPublicKey());
576 } else {
577 throw new WSSecurityException(
578 WSSecurityException.FAILURE, "invalidSAMLsecurity",
579 new Object[]{"cannot get certificate or key"}
580 );
581 }
582 SignatureValidator sigValidator = new SignatureValidator(credential);
583 try {
584 sigValidator.validate(sig);
585 } catch (ValidationException ex) {
586 throw new WSSecurityException("SAML signature validation failed", ex);
587 }
588 signatureKeyInfo = samlKeyInfo;
589 } else {
590 LOG.debug("AssertionWrapper: no signature to validate");
591 }
592 }
593
594 public Signature getSignature() {
595 Signature sig = null;
596 if (saml2 != null && saml2.getSignature() != null) {
597 sig = saml2.getSignature();
598 } else if (saml1 != null && saml1.getSignature() != null) {
599 sig = saml1.getSignature();
600 }
601 return sig;
602 }
603
604
605
606
607
608
609
610
611 public void parseHOKSubject(
612 RequestData data, WSDocInfo docInfo
613 ) throws WSSecurityException {
614 String confirmMethod = null;
615 List<String> methods = getConfirmationMethods();
616 if (methods != null && methods.size() > 0) {
617 confirmMethod = methods.get(0);
618 }
619 if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)) {
620 if (saml1 != null) {
621 subjectKeyInfo =
622 SAMLUtil.getCredentialFromSubject(saml1, data, docInfo,
623 data.getWssConfig().isWsiBSPCompliant());
624 } else if (saml2 != null) {
625 subjectKeyInfo =
626 SAMLUtil.getCredentialFromSubject(saml2, data, docInfo,
627 data.getWssConfig().isWsiBSPCompliant());
628 }
629 }
630 }
631
632
633
634
635
636
637
638 public SAMLVersion getSamlVersion() {
639 if (samlVersion == null) {
640
641 if (LOG.isDebugEnabled()) {
642 LOG.debug(
643 "The SAML version was null in getSamlVersion(). Recomputing SAML version..."
644 );
645 }
646 if (saml1 != null && saml2 == null) {
647 samlVersion = SAMLVersion.VERSION_11;
648 } else if (saml1 == null && saml2 != null) {
649 samlVersion = SAMLVersion.VERSION_20;
650 } else {
651
652 throw new IllegalStateException(
653 "Could not determine the SAML version number. Check your "
654 + "configuration and try again."
655 );
656 }
657 }
658 return samlVersion;
659 }
660
661
662
663
664
665 public Element getElement() {
666 return assertionElement;
667 }
668
669
670
671
672
673 public SAMLKeyInfo getSignatureKeyInfo() {
674 return signatureKeyInfo;
675 }
676
677
678
679
680
681 public SAMLKeyInfo getSubjectKeyInfo() {
682 return subjectKeyInfo;
683 }
684
685
686
687
688
689
690 public byte[] getSignatureValue() throws WSSecurityException {
691 Signature sig = null;
692 if (saml2 != null && saml2.getSignature() != null) {
693 sig = saml2.getSignature();
694 } else if (saml1 != null && saml1.getSignature() != null) {
695 sig = saml1.getSignature();
696 }
697 if (sig != null) {
698 Element signatureElement = sig.getDOM();
699
700 try {
701
702 XMLSignature xmlSignature = new XMLSignature(signatureElement, "");
703 return xmlSignature.getSignatureValue();
704 } catch (XMLSignatureException e) {
705 throw new WSSecurityException(
706 WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
707 );
708 } catch (XMLSecurityException e) {
709 throw new WSSecurityException(
710 WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
711 );
712 }
713 }
714 return null;
715 }
716
717
718
719
720 private void parseElement(Element element) throws WSSecurityException {
721 this.xmlObject = OpenSAMLUtil.fromDom(element);
722 if (xmlObject instanceof org.opensaml.saml1.core.Assertion) {
723 this.saml1 = (org.opensaml.saml1.core.Assertion) xmlObject;
724 samlVersion = SAMLVersion.VERSION_11;
725 } else if (xmlObject instanceof org.opensaml.saml2.core.Assertion) {
726 this.saml2 = (org.opensaml.saml2.core.Assertion) xmlObject;
727 samlVersion = SAMLVersion.VERSION_20;
728 } else {
729 LOG.error(
730 "AssertionWrapper: found unexpected type "
731 + (xmlObject != null ? xmlObject.getClass().getName() : xmlObject)
732 );
733 }
734
735 assertionElement = element;
736 }
737
738
739
740
741 private void parseCallback(
742 SAMLCallback samlCallback, SAMLParms parms
743 ) throws WSSecurityException {
744 samlVersion = samlCallback.getSamlVersion();
745 if (samlVersion == null) {
746 samlVersion = parms.getSAMLVersion();
747 }
748 String issuer = samlCallback.getIssuer();
749 if (issuer == null && parms.getIssuer() != null) {
750 issuer = parms.getIssuer();
751 }
752 if (samlVersion.equals(SAMLVersion.VERSION_11)) {
753
754 saml1 = SAML1ComponentBuilder.createSamlv1Assertion(issuer);
755
756 try {
757
758 List<AuthenticationStatement> authenticationStatements =
759 SAML1ComponentBuilder.createSamlv1AuthenticationStatement(
760 samlCallback.getAuthenticationStatementData()
761 );
762 saml1.getAuthenticationStatements().addAll(authenticationStatements);
763
764
765 List<AttributeStatement> attributeStatements =
766 SAML1ComponentBuilder.createSamlv1AttributeStatement(
767 samlCallback.getAttributeStatementData()
768 );
769 saml1.getAttributeStatements().addAll(attributeStatements);
770
771
772 List<AuthorizationDecisionStatement> authDecisionStatements =
773 SAML1ComponentBuilder.createSamlv1AuthorizationDecisionStatement(
774 samlCallback.getAuthDecisionStatementData()
775 );
776 saml1.getAuthorizationDecisionStatements().addAll(authDecisionStatements);
777
778
779 org.opensaml.saml1.core.Conditions conditions =
780 SAML1ComponentBuilder.createSamlv1Conditions(samlCallback.getConditions());
781 saml1.setConditions(conditions);
782 } catch (org.opensaml.xml.security.SecurityException ex) {
783 throw new WSSecurityException(
784 "Error generating KeyInfo from signing credential", ex
785 );
786 }
787
788
789 xmlObject = saml1;
790
791 } else if (samlVersion.equals(SAMLVersion.VERSION_20)) {
792
793 saml2 = SAML2ComponentBuilder.createAssertion();
794 Issuer samlIssuer = SAML2ComponentBuilder.createIssuer(issuer);
795
796
797 List<AuthnStatement> authnStatements =
798 SAML2ComponentBuilder.createAuthnStatement(
799 samlCallback.getAuthenticationStatementData()
800 );
801 saml2.getAuthnStatements().addAll(authnStatements);
802
803
804 List<org.opensaml.saml2.core.AttributeStatement> attributeStatements =
805 SAML2ComponentBuilder.createAttributeStatement(
806 samlCallback.getAttributeStatementData()
807 );
808 saml2.getAttributeStatements().addAll(attributeStatements);
809
810
811 List<AuthzDecisionStatement> authDecisionStatements =
812 SAML2ComponentBuilder.createAuthorizationDecisionStatement(
813 samlCallback.getAuthDecisionStatementData()
814 );
815 saml2.getAuthzDecisionStatements().addAll(authDecisionStatements);
816
817
818 saml2.setIssuer(samlIssuer);
819
820 try {
821 org.opensaml.saml2.core.Subject subject =
822 SAML2ComponentBuilder.createSaml2Subject(samlCallback.getSubject());
823 saml2.setSubject(subject);
824 } catch (org.opensaml.xml.security.SecurityException ex) {
825 throw new WSSecurityException(
826 "Error generating KeyInfo from signing credential", ex
827 );
828 }
829
830 org.opensaml.saml2.core.Conditions conditions =
831 SAML2ComponentBuilder.createConditions(samlCallback.getConditions());
832 saml2.setConditions(conditions);
833
834
835 xmlObject = saml2;
836 }
837 }
838
839 }