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.wss4j.common.saml;
21  
22  import java.security.PrivateKey;
23  import java.security.cert.X509Certificate;
24  import java.time.Instant;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.wss4j.common.crypto.Crypto;
29  import org.apache.wss4j.common.crypto.CryptoType;
30  import org.apache.wss4j.common.ext.WSSecurityException;
31  import org.apache.wss4j.common.saml.builder.SAML1ComponentBuilder;
32  import org.apache.wss4j.common.saml.builder.SAML2ComponentBuilder;
33  import org.apache.wss4j.common.util.DOM2Writer;
34  import org.apache.wss4j.common.util.InetAddressUtils;
35  import org.apache.xml.security.stax.impl.util.IDGenerator;
36  import org.apache.xml.security.utils.XMLUtils;
37  import org.opensaml.core.xml.XMLObject;
38  import org.opensaml.saml.common.SAMLObject;
39  import org.opensaml.saml.common.SAMLObjectContentReference;
40  import org.opensaml.saml.common.SAMLVersion;
41  import org.opensaml.saml.common.SignableSAMLObject;
42  import org.opensaml.saml.saml1.core.AttributeStatement;
43  import org.opensaml.saml.saml1.core.AuthenticationStatement;
44  import org.opensaml.saml.saml1.core.AuthorizationDecisionStatement;
45  import org.opensaml.saml.saml1.core.ConfirmationMethod;
46  import org.opensaml.saml.saml1.core.Statement;
47  import org.opensaml.saml.saml1.core.Subject;
48  import org.opensaml.saml.saml1.core.SubjectConfirmation;
49  import org.opensaml.saml.saml1.core.SubjectStatement;
50  import org.opensaml.saml.saml2.core.AuthnStatement;
51  import org.opensaml.saml.saml2.core.AuthzDecisionStatement;
52  import org.opensaml.saml.saml2.core.Issuer;
53  import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
54  import org.opensaml.security.credential.BasicCredential;
55  import org.opensaml.security.x509.BasicX509Credential;
56  import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
57  import org.opensaml.xmlsec.signature.KeyInfo;
58  import org.opensaml.xmlsec.signature.Signature;
59  import org.opensaml.xmlsec.signature.support.SignatureConstants;
60  import org.opensaml.xmlsec.signature.support.SignatureException;
61  import org.opensaml.xmlsec.signature.support.SignatureValidator;
62  import org.opensaml.xmlsec.signature.support.SignerProvider;
63  import org.w3c.dom.Document;
64  import org.w3c.dom.Element;
65  
66  /**
67   * Class SamlAssertionWrapper can generate, sign, and validate both SAML v1.1
68   * and SAML v2.0 assertions.
69   */
70  public class SamlAssertionWrapper {
71      /**
72       * Field LOG
73       */
74      private static final org.slf4j.Logger LOG =
75          org.slf4j.LoggerFactory.getLogger(SamlAssertionWrapper.class);
76  
77      /**
78       * Raw SAML Object
79       */
80      private SAMLObject samlObject;
81  
82      /**
83       * Which SAML specification to use (currently, only v1.1 and v2.0 are supported)
84       */
85      private SAMLVersion samlVersion;
86  
87      /**
88       * The Assertion as a DOM element
89       */
90      private Element assertionElement;
91  
92      /**
93       * The SAMLKeyInfo object associated with the Subject KeyInfo
94       */
95      private SAMLKeyInfo subjectKeyInfo;
96  
97      /**
98       * The SAMLKeyInfo object associated with the Signature on the Assertion
99       */
100     private SAMLKeyInfo signatureKeyInfo;
101 
102     /**
103      * Default Canonicalization algorithm used for signing.
104      */
105     private final String defaultCanonicalizationAlgorithm = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
106 
107     /**
108      * Default RSA Signature algorithm used for signing.
109      */
110     private final String defaultRSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
111 
112     /**
113      * Default DSA Signature algorithm used for signing.
114      */
115     private final String defaultDSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
116 
117     /**
118      * Default ECDSA Signature algorithm used for signing.
119      */
120     private final String defaultECDSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1;
121 
122     /**
123      * Default Signature Digest algorithm
124      */
125     private final String defaultSignatureDigestAlgorithm = SignatureConstants.ALGO_ID_DIGEST_SHA1;
126 
127     /**
128      * Whether this object was instantiated with a DOM Element or an XMLObject initially
129      */
130     private final boolean fromDOM;
131 
132     /**
133      * Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
134      *
135      * @param element of type Element
136      * @throws WSSecurityException
137      */
138     public SamlAssertionWrapper(Element element) throws WSSecurityException {
139         OpenSAMLUtil.initSamlEngine();
140 
141         parseElement(element);
142         fromDOM = true;
143     }
144 
145     /**
146      * Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
147      * This is the primary constructor.  All other constructor calls should
148      * be routed to this method to ensure that the wrapper is initialized
149      * correctly.
150      *
151      * @param samlObject of type SAMLObject
152      */
153     public SamlAssertionWrapper(SAMLObject samlObject) throws WSSecurityException {
154         OpenSAMLUtil.initSamlEngine();
155 
156         this.samlObject = samlObject;
157         if (samlObject instanceof org.opensaml.saml.saml1.core.Assertion) {
158             samlVersion = SAMLVersion.VERSION_11;
159         } else if (samlObject instanceof org.opensaml.saml.saml2.core.Assertion) {
160             samlVersion = SAMLVersion.VERSION_20;
161         } else {
162             LOG.error(
163                 "SamlAssertionWrapper: found unexpected type "
164                 + (samlObject != null ? samlObject.getClass().getName() : null)
165             );
166             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
167                                           new Object[] {"A SAML 2.0 or 1.1 Assertion can only be used with "
168                                                         + "SamlAssertionWrapper"});
169         }
170         fromDOM = false;
171     }
172 
173     /**
174      * Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
175      * This constructor is primarily called on the client side to initialize
176      * the wrapper from a configuration file. <br>
177      *
178      * @param samlCallback of type SAMLCallback
179      */
180     public SamlAssertionWrapper(SAMLCallback samlCallback) throws WSSecurityException {
181         OpenSAMLUtil.initSamlEngine();
182 
183         if (samlCallback.getAssertionElement() != null) {
184             parseElement(samlCallback.getAssertionElement());
185             fromDOM = true;
186         } else {
187             // If not then parse the SAMLCallback object
188             parseCallback(samlCallback);
189             fromDOM = false;
190         }
191     }
192 
193     /**
194      * Method getSaml1 returns the saml1 of this SamlAssertionWrapper object.
195      *
196      * @return the saml1 (type Assertion) of this SamlAssertionWrapper object.
197      */
198     public org.opensaml.saml.saml1.core.Assertion getSaml1() {
199         if (samlVersion == SAMLVersion.VERSION_11) {
200             return (org.opensaml.saml.saml1.core.Assertion)samlObject;
201         }
202         return null;
203     }
204 
205     /**
206      * Method getSaml2 returns the saml2 of this SamlAssertionWrapper object.
207      *
208      * @return the saml2 (type Assertion) of this SamlAssertionWrapper object.
209      */
210     public org.opensaml.saml.saml2.core.Assertion getSaml2() {
211         if (samlVersion == SAMLVersion.VERSION_20) {
212             return (org.opensaml.saml.saml2.core.Assertion)samlObject;
213         }
214         return null;
215     }
216 
217     /**
218      * Method isCreated returns the created of this SamlAssertionWrapper object.
219      *
220      * @return the created (type boolean) of this SamlAssertionWrapper object.
221      */
222     public boolean isCreated() {
223         return samlObject != null;
224     }
225 
226 
227     /**
228      * Create a DOM from the current XMLObject content. If the user-supplied doc is not null,
229      * reparent the returned Element so that it is compatible with the user-supplied document.
230      *
231      * @param doc of type Document
232      * @return Element
233      */
234     public Element toDOM(Document doc) throws WSSecurityException {
235         if (fromDOM && assertionElement != null) {
236             parseElement(assertionElement);
237             if (doc != null) {
238                 return (Element)doc.importNode(assertionElement, true);
239             }
240             return assertionElement;
241         }
242         assertionElement = OpenSAMLUtil.toDom(samlObject, doc);
243         return assertionElement;
244     }
245 
246     /**
247      * Method assertionToString ...
248      *
249      * @return String
250      */
251     public String assertionToString() throws WSSecurityException {
252         if (assertionElement == null) {
253             Element element = toDOM(null);
254             return DOM2Writer.nodeToString(element);
255         }
256         return DOM2Writer.nodeToString(assertionElement);
257     }
258 
259     public Instant getNotBefore() {
260         if (getSamlVersion().equals(SAMLVersion.VERSION_20)) {
261             return getSaml2().getConditions().getNotBefore();
262         } else {
263             return getSaml1().getConditions().getNotBefore();
264         }
265     }
266 
267     public Instant getNotOnOrAfter() {
268         if (getSamlVersion().equals(SAMLVersion.VERSION_20)) {
269             return getSaml2().getConditions().getNotOnOrAfter();
270         } else {
271             return getSaml1().getConditions().getNotOnOrAfter();
272         }
273     }
274 
275     /**
276      * Method getId returns the id of this SamlAssertionWrapper object.
277      *
278      * @return the id (type String) of this SamlAssertionWrapper object.
279      */
280     public String getId() {
281         String id = null;
282         if (samlVersion == SAMLVersion.VERSION_20) {
283             id = ((org.opensaml.saml.saml2.core.Assertion)samlObject).getID();
284             if (id == null || id.length() == 0) {
285                 LOG.error("SamlAssertionWrapper: ID was null, seeting a new ID value");
286                 id = IDGenerator.generateID("_");
287                 ((org.opensaml.saml.saml2.core.Assertion)samlObject).setID(id);
288             }
289         } else if (samlVersion == SAMLVersion.VERSION_11) {
290             id = ((org.opensaml.saml.saml1.core.Assertion)samlObject).getID();
291             if (id == null || id.length() == 0) {
292                 LOG.error("SamlAssertionWrapper: ID was null, seeting a new ID value");
293                 id = IDGenerator.generateID("_");
294                 ((org.opensaml.saml.saml1.core.Assertion)samlObject).setID(id);
295             }
296         } else {
297             LOG.error("SamlAssertionWrapper: unable to return ID - no saml assertion object");
298         }
299         return id;
300     }
301 
302     /**
303      * Method getIssuerString returns the issuerString of this SamlAssertionWrapper object.
304      *
305      * @return the issuerString (type String) of this SamlAssertionWrapper object.
306      */
307     public String getIssuerString() {
308         if (samlVersion == SAMLVersion.VERSION_20
309             && ((org.opensaml.saml.saml2.core.Assertion)samlObject).getIssuer() != null) {
310             return ((org.opensaml.saml.saml2.core.Assertion)samlObject).getIssuer().getValue();
311         } else if (samlVersion == SAMLVersion.VERSION_11
312             && ((org.opensaml.saml.saml1.core.Assertion)samlObject).getIssuer() != null) {
313             return ((org.opensaml.saml.saml1.core.Assertion)samlObject).getIssuer();
314         }
315         LOG.error(
316             "SamlAssertionWrapper: unable to return Issuer string - no saml assertion "
317             + "object or issuer is null"
318         );
319         return null;
320     }
321 
322     /**
323      * Method getSubjectName returns the Subject name value
324      * @return the subjectName of this SamlAssertionWrapper object
325      */
326     public String getSubjectName() {
327         if (samlVersion == SAMLVersion.VERSION_20) {
328             org.opensaml.saml.saml2.core.Subject subject =
329                 ((org.opensaml.saml.saml2.core.Assertion)samlObject).getSubject();
330             if (subject != null && subject.getNameID() != null) {
331                 return subject.getNameID().getValue();
332             }
333         } else if (samlVersion == SAMLVersion.VERSION_11) {
334             Subject samlSubject = null;
335             for (Statement stmt : ((org.opensaml.saml.saml1.core.Assertion)samlObject).getStatements()) {
336                 if (stmt instanceof AttributeStatement) {
337                     AttributeStatement attrStmt = (AttributeStatement) stmt;
338                     samlSubject = attrStmt.getSubject();
339                 } else if (stmt instanceof AuthenticationStatement) {
340                     AuthenticationStatement authStmt = (AuthenticationStatement) stmt;
341                     samlSubject = authStmt.getSubject();
342                 } else {
343                     AuthorizationDecisionStatement authzStmt =
344                         (AuthorizationDecisionStatement)stmt;
345                     samlSubject = authzStmt.getSubject();
346                 }
347                 if (samlSubject != null) {
348                     break;
349                 }
350             }
351             if (samlSubject != null && samlSubject.getNameIdentifier() != null) {
352                 return samlSubject.getNameIdentifier().getValue();
353             }
354         }
355         LOG.error(
356                 "SamlAssertionWrapper: unable to return SubjectName - no saml assertion "
357                         + "object or subject is null"
358         );
359         return null;
360     }
361 
362     /**
363      * Method getConfirmationMethods returns the confirmationMethods of this
364      * SamlAssertionWrapper object.
365      *
366      * @return the confirmationMethods of this SamlAssertionWrapper object.
367      */
368     public List<String> getConfirmationMethods() {
369         List<String> methods = new ArrayList<>();
370         if (samlVersion == SAMLVersion.VERSION_20) {
371             org.opensaml.saml.saml2.core.Subject subject =
372                 ((org.opensaml.saml.saml2.core.Assertion)samlObject).getSubject();
373             List<org.opensaml.saml.saml2.core.SubjectConfirmation> confirmations =
374                 subject.getSubjectConfirmations();
375             for (org.opensaml.saml.saml2.core.SubjectConfirmation confirmation : confirmations) {
376                 methods.add(confirmation.getMethod());
377             }
378         } else if (samlVersion == SAMLVersion.VERSION_11) {
379             List<SubjectStatement> subjectStatements = new ArrayList<>();
380             org.opensaml.saml.saml1.core.Assertion saml1 =
381                 (org.opensaml.saml.saml1.core.Assertion)samlObject;
382             subjectStatements.addAll(saml1.getSubjectStatements());
383             subjectStatements.addAll(saml1.getAuthenticationStatements());
384             subjectStatements.addAll(saml1.getAttributeStatements());
385             subjectStatements.addAll(saml1.getAuthorizationDecisionStatements());
386             for (SubjectStatement subjectStatement : subjectStatements) {
387                 Subject subject = subjectStatement.getSubject();
388                 if (subject != null) {
389                     SubjectConfirmation confirmation = subject.getSubjectConfirmation();
390                     if (confirmation != null) {
391                         XMLObject data = confirmation.getSubjectConfirmationData();
392                         if (data instanceof ConfirmationMethod) {
393                             ConfirmationMethod method = (ConfirmationMethod) data;
394                             methods.add(method.getURI());
395                         }
396                         List<ConfirmationMethod> confirmationMethods =
397                             confirmation.getConfirmationMethods();
398                         for (ConfirmationMethod confirmationMethod : confirmationMethods) {
399                             methods.add(confirmationMethod.getURI());
400                         }
401                     }
402                 }
403             }
404         }
405         return methods;
406     }
407 
408     /**
409      * Method isSigned returns the signed of this SamlAssertionWrapper object.
410      *
411      * @return the signed (type boolean) of this SamlAssertionWrapper object.
412      */
413     public boolean isSigned() {
414         return samlObject instanceof SignableSAMLObject
415             && (((SignableSAMLObject)samlObject).isSigned()
416                 || ((SignableSAMLObject)samlObject).getSignature() != null);
417     }
418 
419     /**
420      * Method setSignature sets the signature of this SamlAssertionWrapper object.
421      *
422      * @param signature the signature of this SamlAssertionWrapper object.
423      */
424     public void setSignature(Signature signature) {
425         setSignature(signature, defaultSignatureDigestAlgorithm);
426     }
427 
428     /**
429      * Method setSignature sets the signature of this SamlAssertionWrapper object.
430      *
431      * @param signature the signature of this SamlAssertionWrapper object.
432      * @param signatureDigestAlgorithm the signature digest algorithm to use
433      */
434     public void setSignature(Signature signature, String signatureDigestAlgorithm) {
435         if (samlObject instanceof SignableSAMLObject) {
436             SignableSAMLObject signableObject = (SignableSAMLObject) samlObject;
437             signableObject.setSignature(signature);
438             String digestAlg = signatureDigestAlgorithm;
439             if (digestAlg == null) {
440                 digestAlg = defaultSignatureDigestAlgorithm;
441             }
442             SAMLObjectContentReference contentRef =
443                 (SAMLObjectContentReference)signature.getContentReferences().get(0);
444             contentRef.setDigestAlgorithm(digestAlg);
445             signableObject.releaseDOM();
446             signableObject.releaseChildrenDOM(true);
447         } else {
448             LOG.error("Attempt to sign an unsignable object " + samlObject.getClass().getName());
449         }
450     }
451 
452     /**
453      * Create an enveloped signature on the assertion that has been created.
454      *
455      * @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
456      * @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
457      * @param issuerCrypto the Issuer Crypto instance
458      * @param sendKeyValue whether to send the key value or not
459      * @throws WSSecurityException
460      */
461     public void signAssertion(String issuerKeyName, String issuerKeyPassword,
462             Crypto issuerCrypto, boolean sendKeyValue)
463             throws WSSecurityException {
464 
465         signAssertion(issuerKeyName, issuerKeyPassword, issuerCrypto,
466                 sendKeyValue, defaultCanonicalizationAlgorithm,
467                 null, defaultSignatureDigestAlgorithm);
468     }
469 
470     /**
471      * Create an enveloped signature on the assertion that has been created.
472      *
473      * @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
474      * @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
475      * @param issuerCrypto the Issuer Crypto instance
476      * @param sendKeyValue whether to send the key value or not
477      * @param canonicalizationAlgorithm the canonicalization algorithm to be used for signing
478      * @param signatureAlgorithm the signature algorithm to be used for signing
479      * @throws WSSecurityException
480      */
481     public void signAssertion(String issuerKeyName, String issuerKeyPassword,
482             Crypto issuerCrypto, boolean sendKeyValue,
483             String canonicalizationAlgorithm, String signatureAlgorithm)
484             throws WSSecurityException {
485         signAssertion(issuerKeyName, issuerKeyPassword, issuerCrypto, sendKeyValue,
486                 canonicalizationAlgorithm, signatureAlgorithm, defaultSignatureDigestAlgorithm);
487     }
488 
489     /**
490      * Create an enveloped signature on the assertion that has been created.
491      *
492      * @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
493      * @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
494      * @param issuerCrypto the Issuer Crypto instance
495      * @param sendKeyValue whether to send the key value or not
496      * @param canonicalizationAlgorithm the canonicalization algorithm to be used for signing
497      * @param signatureAlgorithm the signature algorithm to be used for signing
498      * @param signatureDigestAlgorithm the signature Digest algorithm to use
499      * @throws WSSecurityException
500      */
501     public void signAssertion(String issuerKeyName, String issuerKeyPassword,
502             Crypto issuerCrypto, boolean sendKeyValue,
503             String canonicalizationAlgorithm, String signatureAlgorithm,
504             String signatureDigestAlgorithm)
505             throws WSSecurityException {
506         //
507         // Create the signature
508         //
509         Signature signature = OpenSAMLUtil.buildSignature();
510         String c14nAlgo = canonicalizationAlgorithm;
511         if (c14nAlgo == null) {
512             c14nAlgo = defaultCanonicalizationAlgorithm;
513         }
514         signature.setCanonicalizationAlgorithm(c14nAlgo);
515         LOG.debug("Using Canonicalization algorithm {}", c14nAlgo);
516 
517         // prepare to sign the SAML token
518         CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
519         cryptoType.setAlias(issuerKeyName);
520         X509Certificate[] issuerCerts = null;
521         if (issuerCrypto != null) {
522             issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
523         }
524         if (issuerCerts == null || issuerCerts.length == 0) {
525             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
526                 new Object[] {"No issuer certs were found to sign the SAML Assertion using issuer name: "
527                                               + issuerKeyName});
528         }
529 
530         String sigAlgo = signatureAlgorithm;
531         if (sigAlgo == null) {
532             sigAlgo = defaultRSASignatureAlgorithm;
533             String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
534             LOG.debug("automatic sig algo detection: {}", pubKeyAlgo);
535             if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
536                 sigAlgo = defaultDSASignatureAlgorithm;
537             } else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
538                 sigAlgo = defaultECDSASignatureAlgorithm;
539             }
540         }
541         LOG.debug("Using Signature algorithm {}", sigAlgo);
542         PrivateKey privateKey;
543         try {
544             privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
545         } catch (Exception ex) {
546             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
547         }
548         if (privateKey == null) {
549             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
550                 new Object[] {"No private key was found using issuer name: " + issuerKeyName});
551         }
552 
553         signature.setSignatureAlgorithm(sigAlgo);
554 
555         BasicX509Credential signingCredential =
556             new BasicX509Credential(issuerCerts[0], privateKey);
557 
558         signature.setSigningCredential(signingCredential);
559 
560         X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
561         if (sendKeyValue) {
562             kiFactory.setEmitPublicKeyValue(true);
563         } else {
564             kiFactory.setEmitEntityCertificate(true);
565         }
566         try {
567             KeyInfo keyInfo = kiFactory.newInstance().generate(signingCredential);
568             signature.setKeyInfo(keyInfo);
569         } catch (org.opensaml.security.SecurityException ex) {
570             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
571                 new Object[] {"Error generating KeyInfo from signing credential"});
572         }
573 
574         // add the signature to the assertion
575         setSignature(signature, signatureDigestAlgorithm);
576     }
577 
578     /**
579      * Verify the signature of this assertion
580      *
581      * @throws WSSecurityException
582      */
583     public void verifySignature(
584         SAMLKeyInfoProcessor keyInfoProcessor, Crypto sigCrypto
585     ) throws WSSecurityException {
586         Signature sig = getSignature();
587         if (sig != null) {
588             KeyInfo keyInfo = sig.getKeyInfo();
589             if (keyInfo == null) {
590                 throw new WSSecurityException(
591                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
592                     new Object[] {"cannot get certificate or key"}
593                 );
594             }
595             SAMLKeyInfo samlKeyInfo =
596                 SAMLUtil.getCredentialFromKeyInfo(keyInfo.getDOM(), keyInfoProcessor, sigCrypto);
597             verifySignature(samlKeyInfo);
598         } else {
599             LOG.debug("SamlAssertionWrapper: no signature to validate");
600         }
601 
602     }
603 
604     /**
605      * Verify the signature of this assertion
606      *
607      * @throws WSSecurityException
608      */
609     public void verifySignature(SAMLKeyInfo samlKeyInfo) throws WSSecurityException {
610         Signature sig = getSignature();
611         if (sig != null) {
612             if (samlKeyInfo == null) {
613                 throw new WSSecurityException(
614                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
615                     new Object[] {"cannot get certificate or key"}
616                 );
617             }
618 
619             BasicCredential credential = null;
620             if (samlKeyInfo.getCerts() != null) {
621                 credential = new BasicX509Credential(samlKeyInfo.getCerts()[0]);
622             } else if (samlKeyInfo.getPublicKey() != null) {
623                 credential = new BasicCredential(samlKeyInfo.getPublicKey());
624             } else {
625                 throw new WSSecurityException(
626                     WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
627                     new Object[] {"cannot get certificate or key"}
628                 );
629             }
630             ClassLoader loader = Thread.currentThread().getContextClassLoader();
631             try {
632                 Thread.currentThread().setContextClassLoader(SignerProvider.class.getClassLoader());
633                 SignatureValidator.validate(sig, credential);
634             } catch (SignatureException ex) {
635                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex,
636                         "empty", new Object[] {"SAML signature validation failed"});
637             } finally {
638                 Thread.currentThread().setContextClassLoader(loader);
639             }
640             signatureKeyInfo = samlKeyInfo;
641         } else {
642             LOG.debug("SamlAssertionWrapper: no signature to validate");
643         }
644     }
645 
646     /**
647      * Validate the signature of the Assertion against the Profile. This does not actually
648      * verify the signature itself (see the verifySignature method for this)
649      * @throws WSSecurityException
650      */
651     public void validateSignatureAgainstProfile() throws WSSecurityException {
652         Signature sig = getSignature();
653         if (sig != null) {
654             SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
655             try {
656                 validator.validate(sig);
657             } catch (SignatureException ex) {
658                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex,
659                     "empty", new Object[] {"SAML signature validation failed"});
660             }
661         }
662     }
663 
664     /**
665      * This method parses the KeyInfo of the Subject. It then stores the SAMLKeyInfo object that
666      * has been obtained for future processing.
667      * @throws WSSecurityException
668      */
669     public void parseSubject(
670         SAMLKeyInfoProcessor keyInfoProcessor,
671         Crypto sigCrypto
672     ) throws WSSecurityException {
673         if (samlVersion == SAMLVersion.VERSION_11) {
674             subjectKeyInfo =
675                 SAMLUtil.getCredentialFromSubject(
676                     (org.opensaml.saml.saml1.core.Assertion)samlObject, keyInfoProcessor, sigCrypto
677                 );
678         } else if (samlVersion == SAMLVersion.VERSION_20) {
679             subjectKeyInfo =
680                 SAMLUtil.getCredentialFromSubject(
681                     (org.opensaml.saml.saml2.core.Assertion)samlObject, keyInfoProcessor, sigCrypto
682                 );
683         }
684     }
685 
686 
687     /**
688      * Method getSamlVersion returns the samlVersion of this SamlAssertionWrapper object.
689      *
690      * @return the samlVersion (type SAMLVersion) of this SamlAssertionWrapper object.
691      */
692     public SAMLVersion getSamlVersion() {
693         if (samlVersion == null) {
694             // Try to set the version.
695             LOG.debug(
696                 "The SAML version was null in getSamlVersion(). Recomputing SAML version...");
697             if (samlObject instanceof org.opensaml.saml.saml1.core.Assertion) {
698                 samlVersion = SAMLVersion.VERSION_11;
699             } else if (samlObject instanceof org.opensaml.saml.saml2.core.Assertion) {
700                 samlVersion = SAMLVersion.VERSION_20;
701             } else {
702                 // We are only supporting SAML v1.1 or SAML v2.0 at this time.
703                 throw new IllegalStateException(
704                     "Could not determine the SAML version number. Check your "
705                     + "configuration and try again."
706                 );
707             }
708         }
709         return samlVersion;
710     }
711 
712     /**
713      * Get the Assertion as a DOM Element.
714      * @return the assertion as a DOM Element
715      */
716     public Element getElement() {
717         return assertionElement;
718     }
719 
720     /**
721      * Get the SAMLKeyInfo associated with the signature of the assertion
722      * @return the SAMLKeyInfo associated with the signature of the assertion
723      */
724     public SAMLKeyInfo getSignatureKeyInfo() {
725         return signatureKeyInfo;
726     }
727 
728     /**
729      * Get the SAMLKeyInfo associated with the Subject KeyInfo
730      * @return the SAMLKeyInfo associated with the Subject KeyInfo
731      */
732     public SAMLKeyInfo getSubjectKeyInfo() {
733         return subjectKeyInfo;
734     }
735 
736     /**
737      * Get the SignatureValue bytes of the signed SAML Assertion
738      * @return the SignatureValue bytes of the signed SAML Assertion
739      * @throws WSSecurityException
740      */
741     public byte[] getSignatureValue() throws WSSecurityException {
742         Signature sig = null;
743         if (samlObject instanceof SignableSAMLObject) {
744             sig = ((SignableSAMLObject)samlObject).getSignature();
745         }
746         if (sig != null) {
747             return getSignatureValue(sig);
748         }
749         return new byte[0];
750     }
751 
752     private byte[] getSignatureValue(Signature signature) throws WSSecurityException {
753         Element signatureElement = signature.getDOM();
754 
755         if (signatureElement != null) {
756             Element signedInfoElem = XMLUtils.getNextElement(signatureElement.getFirstChild());
757             if (signedInfoElem != null) {
758                 Element signatureValueElement =
759                     XMLUtils.getNextElement(signedInfoElem.getNextSibling());
760                 if (signatureValueElement != null) {
761                     String base64Input = XMLUtils.getFullTextChildrenFromNode(signatureValueElement);
762                     return XMLUtils.decode(base64Input);
763                 }
764             }
765         }
766 
767         return new byte[0];
768     }
769 
770     public Signature getSignature() throws WSSecurityException {
771         if (samlObject instanceof SignableSAMLObject) {
772             return ((SignableSAMLObject)samlObject).getSignature();
773         }
774         return null;
775     }
776 
777     public SAMLObject getSamlObject() {
778         return samlObject;
779     }
780 
781     /**
782      * Check the Conditions of the Assertion.
783      */
784     public void checkConditions(int futureTTL) throws WSSecurityException {
785         Instant validFrom = null;
786         Instant validTill = null;
787 
788         if (getSamlVersion().equals(SAMLVersion.VERSION_20)
789             && getSaml2().getConditions() != null) {
790             validFrom = getSaml2().getConditions().getNotBefore();
791             validTill = getSaml2().getConditions().getNotOnOrAfter();
792         } else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
793             && getSaml1().getConditions() != null) {
794             validFrom = getSaml1().getConditions().getNotBefore();
795             validTill = getSaml1().getConditions().getNotOnOrAfter();
796         }
797 
798         if (validFrom != null) {
799             Instant currentTime = Instant.now();
800             currentTime = currentTime.plusSeconds(futureTTL);
801             if (validFrom.isAfter(currentTime)) {
802                 LOG.warn("SAML Token condition (Not Before) not met");
803                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
804             }
805         }
806 
807         if (validTill != null && validTill.isBefore(Instant.now())) {
808             LOG.warn("SAML Token condition (Not On Or After) not met");
809             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
810         }
811     }
812 
813     /**
814      * Check the IssueInstant value of the Assertion.
815      */
816     public void checkIssueInstant(int futureTTL, int ttl) throws WSSecurityException {
817         Instant issueInstant = null;
818         Instant validTill = null;
819 
820         if (getSamlVersion().equals(SAMLVersion.VERSION_20)
821             && getSaml2().getConditions() != null) {
822             validTill = getSaml2().getConditions().getNotOnOrAfter();
823             issueInstant = getSaml2().getIssueInstant();
824         } else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
825             && getSaml1().getConditions() != null) {
826             validTill = getSaml1().getConditions().getNotOnOrAfter();
827             issueInstant = getSaml1().getIssueInstant();
828         }
829 
830         // Check the IssueInstant is not in the future, subject to the future TTL
831         if (issueInstant != null) {
832             Instant currentTime = Instant.now().plusSeconds(futureTTL);
833             if (issueInstant.isAfter(currentTime)) {
834                 LOG.warn("SAML Token IssueInstant not met");
835                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
836             }
837 
838             // If there is no NotOnOrAfter, then impose a TTL on the IssueInstant.
839             if (validTill == null) {
840                 currentTime = currentTime.minusSeconds(ttl);
841 
842                 if (issueInstant.isBefore(currentTime)) {
843                     LOG.warn("SAML Token IssueInstant not met. The assertion was created too long ago.");
844                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
845                 }
846             }
847         }
848 
849     }
850 
851     /**
852      * Check the AudienceRestrictions of the Assertion
853      */
854     public void checkAudienceRestrictions(List<String> audienceRestrictions) throws WSSecurityException {
855         // Now check the audience restriction conditions
856         if (audienceRestrictions == null || audienceRestrictions.isEmpty()) {
857             return;
858         }
859 
860         if (getSamlVersion().equals(SAMLVersion.VERSION_20) && getSaml2().getConditions() != null) {
861             org.opensaml.saml.saml2.core.Conditions conditions = getSaml2().getConditions();
862             if (conditions != null && conditions.getAudienceRestrictions() != null
863                 && !conditions.getAudienceRestrictions().isEmpty()) {
864                 boolean foundAddress = false;
865                 for (org.opensaml.saml.saml2.core.AudienceRestriction audienceRestriction
866                     : conditions.getAudienceRestrictions()) {
867                     if (audienceRestriction.getAudiences() != null) {
868                         List<org.opensaml.saml.saml2.core.Audience> audiences =
869                             audienceRestriction.getAudiences();
870                         for (org.opensaml.saml.saml2.core.Audience audience : audiences) {
871                             String audienceURI = audience.getURI();
872                             if (audienceRestrictions.contains(audienceURI)) {
873                                 foundAddress = true;
874                                 break;
875                             }
876                         }
877                     }
878                 }
879 
880                 if (!foundAddress) {
881                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
882                 }
883             }
884         } else if (getSamlVersion().equals(SAMLVersion.VERSION_11) && getSaml1().getConditions() != null) {
885             org.opensaml.saml.saml1.core.Conditions conditions = getSaml1().getConditions();
886             if (conditions != null && conditions.getAudienceRestrictionConditions() != null
887                 && !conditions.getAudienceRestrictionConditions().isEmpty()) {
888                 boolean foundAddress = false;
889                 for (org.opensaml.saml.saml1.core.AudienceRestrictionCondition audienceRestriction
890                     : conditions.getAudienceRestrictionConditions()) {
891                     if (audienceRestriction.getAudiences() != null) {
892                         List<org.opensaml.saml.saml1.core.Audience> audiences =
893                             audienceRestriction.getAudiences();
894                         for (org.opensaml.saml.saml1.core.Audience audience : audiences) {
895                             String audienceURI = audience.getURI();
896                             if (audienceRestrictions.contains(audienceURI)) {
897                                 foundAddress = true;
898                                 break;
899                             }
900                         }
901                     }
902                 }
903 
904                 if (!foundAddress) {
905                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
906                 }
907             }
908         }
909     }
910 
911     /**
912      * Check the various attributes of the AuthnStatements of the assertion (if any)
913      */
914     public void checkAuthnStatements(int futureTTL) throws WSSecurityException {
915         if (getSamlVersion().equals(SAMLVersion.VERSION_20)
916             && getSaml2().getAuthnStatements() != null) {
917             List<AuthnStatement> authnStatements = getSaml2().getAuthnStatements();
918 
919             for (AuthnStatement authnStatement : authnStatements) {
920                 Instant authnInstant = authnStatement.getAuthnInstant();
921                 Instant sessionNotOnOrAfter = authnStatement.getSessionNotOnOrAfter();
922                 String subjectLocalityAddress = null;
923 
924                 if (authnStatement.getSubjectLocality() != null
925                     && authnStatement.getSubjectLocality().getAddress() != null) {
926                     subjectLocalityAddress = authnStatement.getSubjectLocality().getAddress();
927                 }
928 
929                 validateAuthnStatement(authnInstant, sessionNotOnOrAfter,
930                                        subjectLocalityAddress, futureTTL);
931             }
932         } else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
933             && getSaml1().getAuthenticationStatements() != null) {
934             List<AuthenticationStatement> authnStatements =
935                 getSaml1().getAuthenticationStatements();
936 
937             for (AuthenticationStatement authnStatement : authnStatements) {
938                 Instant authnInstant = authnStatement.getAuthenticationInstant();
939                 String subjectLocalityAddress = null;
940 
941                 if (authnStatement.getSubjectLocality() != null
942                     && authnStatement.getSubjectLocality().getIPAddress() != null) {
943                     subjectLocalityAddress = authnStatement.getSubjectLocality().getIPAddress();
944                 }
945 
946                 validateAuthnStatement(authnInstant, null,
947                                        subjectLocalityAddress, futureTTL);
948             }
949         }
950     }
951 
952     private void validateAuthnStatement(
953         Instant authnInstant, Instant sessionNotOnOrAfter, String subjectLocalityAddress,
954         int futureTTL
955     ) throws WSSecurityException {
956         // AuthnInstant in the future
957         Instant currentTime = Instant.now();
958         currentTime = currentTime.plusSeconds(futureTTL);
959         if (authnInstant.isAfter(currentTime)) {
960             LOG.warn("SAML Token AuthnInstant not met");
961             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
962         }
963 
964         // Stale SessionNotOnOrAfter
965         if (sessionNotOnOrAfter != null && sessionNotOnOrAfter.isBefore(Instant.now())) {
966             LOG.warn("SAML Token SessionNotOnOrAfter not met");
967             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
968         }
969 
970         // Check that the SubjectLocality address is an IP address
971         if (subjectLocalityAddress != null
972             && !(InetAddressUtils.isIPv4Address(subjectLocalityAddress)
973                 || InetAddressUtils.isIPv6Address(subjectLocalityAddress))) {
974             LOG.warn("SAML Token SubjectLocality address is not valid: " + subjectLocalityAddress);
975             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
976         }
977     }
978 
979     /**
980      * Parse the DOM Element into Opensaml objects.
981      */
982     private void parseElement(Element element) throws WSSecurityException {
983         XMLObject xmlObject = OpenSAMLUtil.fromDom(element);
984         if (xmlObject instanceof org.opensaml.saml.saml1.core.Assertion) {
985             this.samlObject = (SAMLObject)xmlObject;
986             samlVersion = SAMLVersion.VERSION_11;
987         } else if (xmlObject instanceof org.opensaml.saml.saml2.core.Assertion) {
988             this.samlObject = (SAMLObject)xmlObject;
989             samlVersion = SAMLVersion.VERSION_20;
990         } else {
991             LOG.error(
992                 "SamlAssertionWrapper: found unexpected type " + xmlObject.getClass().getName()
993             );
994         }
995 
996         assertionElement = element;
997     }
998 
999     /**
1000      * Parse a SAMLCallback object to create a SAML Assertion
1001      */
1002     private void parseCallback(
1003         SAMLCallback samlCallback
1004     ) throws WSSecurityException {
1005         samlVersion = samlCallback.getSamlVersion();
1006         if (samlVersion == null) {
1007             samlVersion = SAMLVersion.VERSION_20;
1008         }
1009         String issuer = samlCallback.getIssuer();
1010         String issuerFormat = samlCallback.getIssuerFormat();
1011         String issuerQualifier = samlCallback.getIssuerQualifier();
1012 
1013         if (samlVersion.equals(SAMLVersion.VERSION_11)) {
1014             // Build a SAML v1.1 assertion
1015             org.opensaml.saml.saml1.core.Assertion saml1 =
1016                 SAML1ComponentBuilder.createSamlv1Assertion(issuer);
1017 
1018             try {
1019                 // Process the SAML authentication statement(s)
1020                 List<AuthenticationStatement> authenticationStatements =
1021                     SAML1ComponentBuilder.createSamlv1AuthenticationStatement(
1022                         samlCallback.getAuthenticationStatementData()
1023                     );
1024                 saml1.getAuthenticationStatements().addAll(authenticationStatements);
1025 
1026                 // Process the SAML attribute statement(s)
1027                 List<AttributeStatement> attributeStatements =
1028                         SAML1ComponentBuilder.createSamlv1AttributeStatement(
1029                             samlCallback.getAttributeStatementData()
1030                         );
1031                 saml1.getAttributeStatements().addAll(attributeStatements);
1032 
1033                 // Process the SAML authorization decision statement(s)
1034                 List<AuthorizationDecisionStatement> authDecisionStatements =
1035                         SAML1ComponentBuilder.createSamlv1AuthorizationDecisionStatement(
1036                             samlCallback.getAuthDecisionStatementData()
1037                         );
1038                 saml1.getAuthorizationDecisionStatements().addAll(authDecisionStatements);
1039 
1040                 // Build the complete assertion
1041                 org.opensaml.saml.saml1.core.Conditions conditions =
1042                     SAML1ComponentBuilder.createSamlv1Conditions(samlCallback.getConditions());
1043                 saml1.setConditions(conditions);
1044 
1045                 if (samlCallback.getAdvice() != null) {
1046                     org.opensaml.saml.saml1.core.Advice advice =
1047                         SAML1ComponentBuilder.createAdvice(samlCallback.getAdvice());
1048                     saml1.setAdvice(advice);
1049                 }
1050             } catch (org.opensaml.security.SecurityException ex) {
1051                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
1052                     new Object[] {"Error generating KeyInfo from signing credential"}
1053                 );
1054             }
1055 
1056             // Set the OpenSaml2 XMLObject instance
1057             samlObject = saml1;
1058 
1059         } else if (samlVersion.equals(SAMLVersion.VERSION_20)) {
1060             // Build a SAML v2.0 assertion
1061             org.opensaml.saml.saml2.core.Assertion saml2 = SAML2ComponentBuilder.createAssertion();
1062             Issuer samlIssuer = SAML2ComponentBuilder.createIssuer(issuer, issuerFormat, issuerQualifier);
1063 
1064             // Authn Statement(s)
1065             List<AuthnStatement> authnStatements =
1066                 SAML2ComponentBuilder.createAuthnStatement(
1067                     samlCallback.getAuthenticationStatementData()
1068                 );
1069             saml2.getAuthnStatements().addAll(authnStatements);
1070 
1071             // Attribute statement(s)
1072             List<org.opensaml.saml.saml2.core.AttributeStatement> attributeStatements =
1073                 SAML2ComponentBuilder.createAttributeStatement(
1074                     samlCallback.getAttributeStatementData()
1075                 );
1076             saml2.getAttributeStatements().addAll(attributeStatements);
1077 
1078             // AuthzDecisionStatement(s)
1079             List<AuthzDecisionStatement> authDecisionStatements =
1080                     SAML2ComponentBuilder.createAuthorizationDecisionStatement(
1081                         samlCallback.getAuthDecisionStatementData()
1082                     );
1083             saml2.getAuthzDecisionStatements().addAll(authDecisionStatements);
1084 
1085             // Build the SAML v2.0 assertion
1086             saml2.setIssuer(samlIssuer);
1087 
1088             try {
1089                 org.opensaml.saml.saml2.core.Subject subject =
1090                     SAML2ComponentBuilder.createSaml2Subject(samlCallback.getSubject());
1091                 saml2.setSubject(subject);
1092             } catch (org.opensaml.security.SecurityException ex) {
1093                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
1094                     new Object[] {"Error generating KeyInfo from signing credential"}
1095                 );
1096             }
1097 
1098             org.opensaml.saml.saml2.core.Conditions conditions =
1099                 SAML2ComponentBuilder.createConditions(samlCallback.getConditions());
1100             saml2.setConditions(conditions);
1101 
1102             if (samlCallback.getAdvice() != null) {
1103                 org.opensaml.saml.saml2.core.Advice advice =
1104                     SAML2ComponentBuilder.createAdvice(samlCallback.getAdvice());
1105                 saml2.setAdvice(advice);
1106             }
1107 
1108             // Set the OpenSaml2 XMLObject instance
1109             samlObject = saml2;
1110         }
1111     }
1112 
1113 }