View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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   * Class AssertionWrapper can generate, sign, and validate both SAML v1.1
74   * and SAML v2.0 assertions.
75   * <p/>
76   * Created on May 18, 2009
77   */
78  public class AssertionWrapper {
79      /**
80       * Field log
81       */
82      private static final org.apache.commons.logging.Log LOG = 
83          org.apache.commons.logging.LogFactory.getLog(AssertionWrapper.class);
84  
85      /**
86       * Raw SAML assertion data
87       */
88      private XMLObject xmlObject = null;
89  
90      /**
91       * Typed SAML v1.1 assertion
92       */
93      private org.opensaml.saml1.core.Assertion saml1 = null;
94  
95      /**
96       * Typed SAML v2.0 assertion
97       */
98      private org.opensaml.saml2.core.Assertion saml2 = null;
99  
100     /**
101      * Which SAML specification to use (currently, only v1.1 and v2.0 are supported)
102      */
103     private SAMLVersion samlVersion;
104 
105     /**
106      * Fully qualified class name of the SAML callback handler implementation to use.
107      * NOTE: Each application should provide a unique implementation of this 
108      * <code>CallbackHandler</code> that is able to extract any dynamic data from the
109      * local environment that should be included in the generated SAML statements.
110      */
111     private CallbackHandler samlCallbackHandler = null;
112     
113     /**
114      * The Assertion as a DOM element
115      */
116     private Element assertionElement;
117     
118     /**
119      * The SAMLKeyInfo object associated with the Subject KeyInfo
120      */
121     private SAMLKeyInfo subjectKeyInfo;
122     
123     /**
124      * The SAMLKeyInfo object associated with the Signature on the Assertion
125      */
126     private SAMLKeyInfo signatureKeyInfo;
127 
128     /**
129      * Default Canonicalization algorithm used for signing.
130      */
131     private final String defaultCanonicalizationAlgorithm = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
132 
133     /**
134      * Default RSA Signature algorithm used for signing.
135      */
136     private final String defaultRSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
137 
138     /**
139      * Default DSA Signature algorithm used for signing.
140      */
141     private final String defaultDSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
142     
143     /**
144      * Whether this object was instantiated with a DOM Element or an XMLObject initially
145      */
146     private final boolean fromDOM;
147     
148     /**
149      * Constructor AssertionWrapper creates a new AssertionWrapper instance.
150      *
151      * @param element of type Element
152      * @throws UnmarshallingException when
153      */
154     public AssertionWrapper(Element element) throws WSSecurityException {
155         OpenSAMLUtil.initSamlEngine();
156         
157         parseElement(element);
158         fromDOM = true;
159     }
160 
161     /**
162      * Constructor AssertionWrapper creates a new AssertionWrapper instance.
163      *
164      * @param saml2 of type Assertion
165      */
166     public AssertionWrapper(org.opensaml.saml2.core.Assertion saml2) {
167         this((XMLObject)saml2);
168     }
169 
170     /**
171      * Constructor AssertionWrapper creates a new AssertionWrapper instance.
172      *
173      * @param saml1 of type Assertion
174      */
175     public AssertionWrapper(org.opensaml.saml1.core.Assertion saml1) {
176         this((XMLObject)saml1);
177     }
178 
179     /**
180      * Constructor AssertionWrapper creates a new AssertionWrapper instance.
181      * This is the primary constructor.  All other constructor calls should
182      * be routed to this method to ensure that the wrapper is initialized
183      * correctly.
184      *
185      * @param xmlObject of type XMLObject
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      * Constructor AssertionWrapper creates a new AssertionWrapper instance.
208      * This constructor is primarily called on the client side to initialize
209      * the wrapper from a configuration file. <br>
210      *
211      * @param parms of type SAMLParms
212      */
213     public AssertionWrapper(SAMLParms parms) throws WSSecurityException {
214         OpenSAMLUtil.initSamlEngine();
215         
216         //
217         // Create the SAML callback that the handler will use to get the required data from the 
218         // client application.
219         //
220         SAMLCallback[] samlCallbacks = new SAMLCallback[] { new SAMLCallback() };
221 
222         try {
223             // Get the SAML source data using the currently configured callback implementation.
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         // See if we already have a DOM element in SAMLCallback
237         if (samlCallbacks[0].getAssertionElement() != null) {
238             parseElement(samlCallbacks[0].getAssertionElement());
239             fromDOM = true;
240         } else {
241             // If not then parse the SAMLCallback object
242             parseCallback(samlCallbacks[0], parms);
243             fromDOM = false;
244         }
245     }
246 
247     /**
248      * Method getSaml1 returns the saml1 of this AssertionWrapper object.
249      *
250      * @return the saml1 (type Assertion) of this AssertionWrapper object.
251      */
252     public org.opensaml.saml1.core.Assertion getSaml1() {
253         return saml1;
254     }
255 
256     /**
257      * Method getSaml2 returns the saml2 of this AssertionWrapper object.
258      *
259      * @return the saml2 (type Assertion) of this AssertionWrapper object.
260      */
261     public org.opensaml.saml2.core.Assertion getSaml2() {
262         return saml2;
263     }
264 
265     /**
266      * Method getXmlObject returns the xmlObject of this AssertionWrapper object.
267      *
268      * @return the xmlObject (type XMLObject) of this AssertionWrapper object.
269      */
270     public XMLObject getXmlObject() {
271         return xmlObject;
272     }
273 
274     /**
275      * Method isCreated returns the created of this AssertionWrapper object.
276      *
277      * @return the created (type boolean) of this AssertionWrapper object.
278      */
279     public boolean isCreated() {
280         return saml1 != null || saml2 != null;
281     }
282 
283 
284     /**
285      * Create a DOM from the current XMLObject content. If the user-supplied doc is not null,
286      * reparent the returned Element so that it is compatible with the user-supplied document.
287      *
288      * @param doc of type Document
289      * @return Element
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      * Method assertionToString ...
305      *
306      * @return String
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      * Method getId returns the id of this AssertionWrapper object.
318      *
319      * @return the id (type String) of this AssertionWrapper object.
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      * Method getIssuerString returns the issuerString of this AssertionWrapper object.
344      *
345      * @return the issuerString (type String) of this AssertionWrapper object.
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      * Method getConfirmationMethods returns the confirmationMethods of this 
362      * AssertionWrapper object.
363      *
364      * @return the confirmationMethods of this AssertionWrapper object.
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      * Method isSigned returns the signed of this AssertionWrapper object.
405      *
406      * @return the signed (type boolean) of this AssertionWrapper object.
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      * Method setSignature sets the signature of this AssertionWrapper object.
419      *
420      * @param signature the signature of this AssertionWrapper object.
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      * Create an enveloped signature on the assertion that has been created.
435      * 
436      * @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
437      * @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
438      * @param issuerCrypto the Issuer Crypto instance
439      * @param sendKeyValue whether to send the key value or not
440      * @throws WSSecurityException
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      * Create an enveloped signature on the assertion that has been created.
453      * 
454      * @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
455      * @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
456      * @param issuerCrypto the Issuer Crypto instance
457      * @param sendKeyValue whether to send the key value or not
458      * @param canonicalizationAlgorithm the canonicalization algorithm to be used for signing
459      * @param signatureAlgorithm the signature algorithm to be used for signing
460      * @throws WSSecurityException
461      */
462     public void signAssertion(String issuerKeyName, String issuerKeyPassword,
463             Crypto issuerCrypto, boolean sendKeyValue,
464             String canonicalizationAlgorithm, String signatureAlgorithm)
465             throws WSSecurityException {
466         //
467         // Create the signature
468         //
469         Signature signature = OpenSAMLUtil.buildSignature();
470         signature.setCanonicalizationAlgorithm(canonicalizationAlgorithm);
471         LOG.debug("Using Canonicalization algorithm " + canonicalizationAlgorithm);
472         // prepare to sign the SAML token
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         // add the signature to the assertion
522         setSignature(signature);
523     }
524 
525     /**
526      * Verify the signature of this assertion
527      *
528      * @throws ValidationException
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      * Verify the signature of this assertion
547      *
548      * @throws ValidationException
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      * Validate the signature of the Assertion against the Profile. This does not actually
585      * verify the signature itself (see the verifySignature method for this)
586      * @throws WSSecurityException
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      * This method parses the KeyInfo of the Subject for the holder-of-key confirmation
613      * method, as required by the SAML Token spec. It then stores the SAMLKeyInfo object that
614      * has been obtained for future processing by the SignatureProcessor.
615      * @throws WSSecurityException
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      * Method getSamlVersion returns the samlVersion of this AssertionWrapper object.
641      *
642      * @return the samlVersion (type SAMLVersion) of this AssertionWrapper object.
643      */
644     public SAMLVersion getSamlVersion() {
645         if (samlVersion == null) {
646             // Try to set the version.
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                 // We are only supporting SAML v1.1 or SAML v2.0 at this time.
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      * Get the Assertion as a DOM Element.
669      * @return the assertion as a DOM Element
670      */
671     public Element getElement() {
672         return assertionElement;
673     }
674     
675     /**
676      * Get the SAMLKeyInfo associated with the signature of the assertion
677      * @return the SAMLKeyInfo associated with the signature of the assertion
678      */
679     public SAMLKeyInfo getSignatureKeyInfo() {
680         return signatureKeyInfo;
681     }
682     
683     /**
684      * Get the SAMLKeyInfo associated with the Subject KeyInfo
685      * @return the SAMLKeyInfo associated with the Subject KeyInfo
686      */
687     public SAMLKeyInfo getSubjectKeyInfo() {
688         return subjectKeyInfo;
689     }
690     
691     /**
692      * Get the SignatureValue bytes of the signed SAML Assertion 
693      * @return the SignatureValue bytes of the signed SAML Assertion 
694      * @throws WSSecurityException
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                 // Use XML-Security class to obtain SignatureValue
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      * Parse the DOM Element into Opensaml objects.
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      * Parse a SAMLCallback object to create a SAML Assertion
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             // Build a SAML v1.1 assertion
760             saml1 = SAML1ComponentBuilder.createSamlv1Assertion(issuer);
761 
762             try {
763                 // Process the SAML authentication statement(s)
764                 List<AuthenticationStatement> authenticationStatements = 
765                     SAML1ComponentBuilder.createSamlv1AuthenticationStatement(
766                         samlCallback.getAuthenticationStatementData()
767                     );
768                 saml1.getAuthenticationStatements().addAll(authenticationStatements);
769     
770                 // Process the SAML attribute statement(s)            
771                 List<AttributeStatement> attributeStatements =
772                         SAML1ComponentBuilder.createSamlv1AttributeStatement(
773                             samlCallback.getAttributeStatementData()
774                         );
775                 saml1.getAttributeStatements().addAll(attributeStatements);
776     
777                 // Process the SAML authorization decision statement(s)
778                 List<AuthorizationDecisionStatement> authDecisionStatements =
779                         SAML1ComponentBuilder.createSamlv1AuthorizationDecisionStatement(
780                             samlCallback.getAuthDecisionStatementData()
781                         );
782                 saml1.getAuthorizationDecisionStatements().addAll(authDecisionStatements);
783     
784                 // Build the complete assertion
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             // Set the OpenSaml2 XMLObject instance
795             xmlObject = saml1;
796 
797         } else if (samlVersion.equals(SAMLVersion.VERSION_20)) {
798             // Build a SAML v2.0 assertion
799             saml2 = SAML2ComponentBuilder.createAssertion();
800             Issuer samlIssuer = SAML2ComponentBuilder.createIssuer(issuer);
801 
802             // Authn Statement(s)
803             List<AuthnStatement> authnStatements = 
804                 SAML2ComponentBuilder.createAuthnStatement(
805                     samlCallback.getAuthenticationStatementData()
806                 );
807             saml2.getAuthnStatements().addAll(authnStatements);
808 
809             // Attribute statement(s)
810             List<org.opensaml.saml2.core.AttributeStatement> attributeStatements = 
811                 SAML2ComponentBuilder.createAttributeStatement(
812                     samlCallback.getAttributeStatementData()
813                 );
814             saml2.getAttributeStatements().addAll(attributeStatements);
815 
816             // AuthzDecisionStatement(s)
817             List<AuthzDecisionStatement> authDecisionStatements =
818                     SAML2ComponentBuilder.createAuthorizationDecisionStatement(
819                         samlCallback.getAuthDecisionStatementData()
820                     );
821             saml2.getAuthzDecisionStatements().addAll(authDecisionStatements);
822 
823             // Build the SAML v2.0 assertion
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             // Set the OpenSaml2 XMLObject instance
841             xmlObject = saml2;
842         }
843     }
844 
845 }