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 = getSignature();
534 if (sig != null) {
535 KeyInfo keyInfo = sig.getKeyInfo();
536 SAMLKeyInfo samlKeyInfo =
537 SAMLUtil.getCredentialDirectlyFromKeyInfo(keyInfo.getDOM(), data);
538 verifySignature(samlKeyInfo);
539 } else {
540 LOG.debug("AssertionWrapper: no signature to validate");
541 }
542
543 }
544
545
546
547
548
549
550 public void verifySignature(SAMLKeyInfo samlKeyInfo) throws WSSecurityException {
551 Signature sig = getSignature();
552 if (sig != null) {
553 if (samlKeyInfo == null) {
554 throw new WSSecurityException(
555 WSSecurityException.FAILURE, "invalidSAMLsecurity",
556 new Object[]{"cannot get certificate or key"}
557 );
558 }
559
560 BasicX509Credential credential = new BasicX509Credential();
561 if (samlKeyInfo.getCerts() != null) {
562 credential.setEntityCertificate(samlKeyInfo.getCerts()[0]);
563 } else if (samlKeyInfo.getPublicKey() != null) {
564 credential.setPublicKey(samlKeyInfo.getPublicKey());
565 } else {
566 throw new WSSecurityException(
567 WSSecurityException.FAILURE, "invalidSAMLsecurity",
568 new Object[]{"cannot get certificate or key"}
569 );
570 }
571 SignatureValidator sigValidator = new SignatureValidator(credential);
572 try {
573 sigValidator.validate(sig);
574 } catch (ValidationException ex) {
575 throw new WSSecurityException("SAML signature validation failed", ex);
576 }
577 signatureKeyInfo = samlKeyInfo;
578 } else {
579 LOG.debug("AssertionWrapper: no signature to validate");
580 }
581 }
582
583
584
585
586
587
588 public void validateSignatureAgainstProfile() throws WSSecurityException {
589 Signature sig = getSignature();
590 if (sig != null) {
591 SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
592 try {
593 validator.validate(sig);
594 } catch (ValidationException ex) {
595 throw new WSSecurityException("SAML signature validation failed", ex);
596 }
597 }
598 }
599
600 public Signature getSignature() {
601 Signature sig = null;
602 if (saml2 != null && saml2.getSignature() != null) {
603 sig = saml2.getSignature();
604 } else if (saml1 != null && saml1.getSignature() != null) {
605 sig = saml1.getSignature();
606 }
607 return sig;
608 }
609
610
611
612
613
614
615
616
617 public void parseHOKSubject(
618 RequestData data, WSDocInfo docInfo
619 ) throws WSSecurityException {
620 String confirmMethod = null;
621 List<String> methods = getConfirmationMethods();
622 if (methods != null && methods.size() > 0) {
623 confirmMethod = methods.get(0);
624 }
625 if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)) {
626 if (saml1 != null) {
627 subjectKeyInfo =
628 SAMLUtil.getCredentialFromSubject(saml1, data, docInfo,
629 data.getWssConfig().isWsiBSPCompliant());
630 } else if (saml2 != null) {
631 subjectKeyInfo =
632 SAMLUtil.getCredentialFromSubject(saml2, data, docInfo,
633 data.getWssConfig().isWsiBSPCompliant());
634 }
635 }
636 }
637
638
639
640
641
642
643
644 public SAMLVersion getSamlVersion() {
645 if (samlVersion == null) {
646
647 if (LOG.isDebugEnabled()) {
648 LOG.debug(
649 "The SAML version was null in getSamlVersion(). Recomputing SAML version..."
650 );
651 }
652 if (saml1 != null && saml2 == null) {
653 samlVersion = SAMLVersion.VERSION_11;
654 } else if (saml1 == null && saml2 != null) {
655 samlVersion = SAMLVersion.VERSION_20;
656 } else {
657
658 throw new IllegalStateException(
659 "Could not determine the SAML version number. Check your "
660 + "configuration and try again."
661 );
662 }
663 }
664 return samlVersion;
665 }
666
667
668
669
670
671 public Element getElement() {
672 return assertionElement;
673 }
674
675
676
677
678
679 public SAMLKeyInfo getSignatureKeyInfo() {
680 return signatureKeyInfo;
681 }
682
683
684
685
686
687 public SAMLKeyInfo getSubjectKeyInfo() {
688 return subjectKeyInfo;
689 }
690
691
692
693
694
695
696 public byte[] getSignatureValue() throws WSSecurityException {
697 Signature sig = null;
698 if (saml2 != null && saml2.getSignature() != null) {
699 sig = saml2.getSignature();
700 } else if (saml1 != null && saml1.getSignature() != null) {
701 sig = saml1.getSignature();
702 }
703 if (sig != null) {
704 Element signatureElement = sig.getDOM();
705
706 try {
707
708 XMLSignature xmlSignature = new XMLSignature(signatureElement, "");
709 return xmlSignature.getSignatureValue();
710 } catch (XMLSignatureException e) {
711 throw new WSSecurityException(
712 WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
713 );
714 } catch (XMLSecurityException e) {
715 throw new WSSecurityException(
716 WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
717 );
718 }
719 }
720 return null;
721 }
722
723
724
725
726 private void parseElement(Element element) throws WSSecurityException {
727 this.xmlObject = OpenSAMLUtil.fromDom(element);
728 if (xmlObject instanceof org.opensaml.saml1.core.Assertion) {
729 this.saml1 = (org.opensaml.saml1.core.Assertion) xmlObject;
730 samlVersion = SAMLVersion.VERSION_11;
731 } else if (xmlObject instanceof org.opensaml.saml2.core.Assertion) {
732 this.saml2 = (org.opensaml.saml2.core.Assertion) xmlObject;
733 samlVersion = SAMLVersion.VERSION_20;
734 } else {
735 LOG.error(
736 "AssertionWrapper: found unexpected type "
737 + (xmlObject != null ? xmlObject.getClass().getName() : xmlObject)
738 );
739 }
740
741 assertionElement = element;
742 }
743
744
745
746
747 private void parseCallback(
748 SAMLCallback samlCallback, SAMLParms parms
749 ) throws WSSecurityException {
750 samlVersion = samlCallback.getSamlVersion();
751 if (samlVersion == null) {
752 samlVersion = parms.getSAMLVersion();
753 }
754 String issuer = samlCallback.getIssuer();
755 if (issuer == null && parms.getIssuer() != null) {
756 issuer = parms.getIssuer();
757 }
758 if (samlVersion.equals(SAMLVersion.VERSION_11)) {
759
760 saml1 = SAML1ComponentBuilder.createSamlv1Assertion(issuer);
761
762 try {
763
764 List<AuthenticationStatement> authenticationStatements =
765 SAML1ComponentBuilder.createSamlv1AuthenticationStatement(
766 samlCallback.getAuthenticationStatementData()
767 );
768 saml1.getAuthenticationStatements().addAll(authenticationStatements);
769
770
771 List<AttributeStatement> attributeStatements =
772 SAML1ComponentBuilder.createSamlv1AttributeStatement(
773 samlCallback.getAttributeStatementData()
774 );
775 saml1.getAttributeStatements().addAll(attributeStatements);
776
777
778 List<AuthorizationDecisionStatement> authDecisionStatements =
779 SAML1ComponentBuilder.createSamlv1AuthorizationDecisionStatement(
780 samlCallback.getAuthDecisionStatementData()
781 );
782 saml1.getAuthorizationDecisionStatements().addAll(authDecisionStatements);
783
784
785 org.opensaml.saml1.core.Conditions conditions =
786 SAML1ComponentBuilder.createSamlv1Conditions(samlCallback.getConditions());
787 saml1.setConditions(conditions);
788 } catch (org.opensaml.xml.security.SecurityException ex) {
789 throw new WSSecurityException(
790 "Error generating KeyInfo from signing credential", ex
791 );
792 }
793
794
795 xmlObject = saml1;
796
797 } else if (samlVersion.equals(SAMLVersion.VERSION_20)) {
798
799 saml2 = SAML2ComponentBuilder.createAssertion();
800 Issuer samlIssuer = SAML2ComponentBuilder.createIssuer(issuer);
801
802
803 List<AuthnStatement> authnStatements =
804 SAML2ComponentBuilder.createAuthnStatement(
805 samlCallback.getAuthenticationStatementData()
806 );
807 saml2.getAuthnStatements().addAll(authnStatements);
808
809
810 List<org.opensaml.saml2.core.AttributeStatement> attributeStatements =
811 SAML2ComponentBuilder.createAttributeStatement(
812 samlCallback.getAttributeStatementData()
813 );
814 saml2.getAttributeStatements().addAll(attributeStatements);
815
816
817 List<AuthzDecisionStatement> authDecisionStatements =
818 SAML2ComponentBuilder.createAuthorizationDecisionStatement(
819 samlCallback.getAuthDecisionStatementData()
820 );
821 saml2.getAuthzDecisionStatements().addAll(authDecisionStatements);
822
823
824 saml2.setIssuer(samlIssuer);
825
826 try {
827 org.opensaml.saml2.core.Subject subject =
828 SAML2ComponentBuilder.createSaml2Subject(samlCallback.getSubject());
829 saml2.setSubject(subject);
830 } catch (org.opensaml.xml.security.SecurityException ex) {
831 throw new WSSecurityException(
832 "Error generating KeyInfo from signing credential", ex
833 );
834 }
835
836 org.opensaml.saml2.core.Conditions conditions =
837 SAML2ComponentBuilder.createConditions(samlCallback.getConditions());
838 saml2.setConditions(conditions);
839
840
841 xmlObject = saml2;
842 }
843 }
844
845 }