/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: AssertionImpl.java,v 1.8 2009/05/09 15:43:59 mallas Exp $ * * Portions Copyrighted 2015-2016 ForgeRock AS. */ package com.sun.identity.saml2.assertion.impl; import static org.forgerock.openam.utils.Time.*; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.ArrayList; import java.text.ParseException; import java.security.Key; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Set; import com.sun.identity.shared.xml.XMLUtils; import com.sun.identity.shared.DateUtils; import com.sun.identity.saml2.common.SAML2Constants; import com.sun.identity.saml2.common.SAML2Exception; import com.sun.identity.saml2.common.SAML2SDKUtils; import com.sun.identity.saml2.assertion.Assertion; import com.sun.identity.saml2.assertion.AssertionFactory; import com.sun.identity.saml2.assertion.AttributeStatement; import com.sun.identity.saml2.assertion.AuthnStatement; import com.sun.identity.saml2.assertion.AuthzDecisionStatement; import com.sun.identity.saml2.assertion.EncryptedAssertion; import com.sun.identity.saml2.assertion.Subject; import com.sun.identity.saml2.assertion.Advice; import com.sun.identity.saml2.assertion.Conditions; import com.sun.identity.saml2.assertion.Issuer; import com.sun.identity.saml2.xmlenc.EncManager; import com.sun.identity.saml2.xmlsig.SigManager; /** * The Assertion element is a package of information * that supplies one or more Statement made by an issuer. * There are three kinds of assertions: Authentication, Authorization Decision, * and Attribute assertions. */ public class AssertionImpl implements Assertion { private String version; private Date issueInstant; private Subject subject; private Advice advice; private String signature; private Conditions conditions; private String id; private List statements = new ArrayList(); private List authnStatements = new ArrayList(); private List authzDecisionStatements = new ArrayList(); private List attributeStatements = new ArrayList(); private Issuer issuer; private boolean isMutable = true; private String signedXMLString = null; private Boolean isSignatureValid = null; public static String ASSERTION_ELEMENT = "Assertion"; public static String ASSERTION_VERSION_ATTR = "Version"; public static String ASSERTION_ID_ATTR = "ID"; public static String ASSERTION_ISSUEINSTANT_ATTR = "IssueInstant"; public static String XSI_TYPE_ATTR = "xsi:type"; public static String ASSERTION_ISSUER = "Issuer"; public static String ASSERTION_SIGNATURE = "Signature"; public static String ASSERTION_SUBJECT = "Subject"; public static String ASSERTION_CONDITIONS = "Conditions"; public static String ASSERTION_ADVICE = "Advice"; public static String ASSERTION_STATEMENT = "Statement"; public static String ASSERTION_AUTHNSTATEMENT = "AuthnStatement"; public static String ASSERTION_AUTHZDECISIONSTATEMENT = "AuthzDecisionStatement"; public static String ASSERTION_ATTRIBUTESTATEMENT = "AttributeStatement"; /** * Default constructor */ public AssertionImpl() { } /** * This constructor is used to build Assertion object from a * XML string. * * @param xml A java.lang.String representing * a Assertion object * @exception SAML2Exception if it could not process the XML string */ public AssertionImpl(String xml) throws SAML2Exception { Document document = XMLUtils.toDOMDocument(xml, SAML2SDKUtils.debug); if (document != null) { Element rootElement = document.getDocumentElement(); processElement(rootElement); makeImmutable(); } else { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): invalid XML input"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "errorObtainingElement")); } if (signature != null) { signedXMLString = xml; } } /** * This constructor is used to build Assertion object from a * block of existing XML that has already been built into a DOM. * * @param element A org.w3c.dom.Element representing * DOM tree for Assertion object * @exception SAML2Exception if it could not process the Element */ public AssertionImpl(Element element) throws SAML2Exception { processElement(element); makeImmutable(); if (signature != null) { signedXMLString = XMLUtils.print(element,"UTF-8"); } } private void processElement(Element element) throws SAML2Exception { if (element == null) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): invalid root element"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "invalid_element")); } String elemName = element.getLocalName(); if (elemName == null) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): local name missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_local_name")); } if (!elemName.equals(ASSERTION_ELEMENT)) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): invalid local name " + elemName); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "invalid_local_name")); } // starts processing attributes String attrValue = element.getAttribute(ASSERTION_VERSION_ATTR); if ((attrValue == null) || (attrValue.length() == 0)) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): version missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_assertion_version")); } version = attrValue; attrValue = element.getAttribute(ASSERTION_ID_ATTR); if ((attrValue == null) || (attrValue.length() == 0)) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): assertion id missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_assertion_id")); } id = attrValue; attrValue = element.getAttribute(ASSERTION_ISSUEINSTANT_ATTR); if ((attrValue == null) || (attrValue.length() == 0)) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): issue instant missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_issue_instant")); } try { issueInstant = DateUtils.stringToDate(attrValue); } catch (ParseException pe) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): invalid issue instant"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "invalid_date_format")); } // starts processing subelements NodeList nodes = element.getChildNodes(); int numOfNodes = nodes.getLength(); if (numOfNodes < 1) { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): assertion has no subelements"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_subelements")); } AssertionFactory factory = AssertionFactory.getInstance(); int nextElem = 0; Node child = (Node)nodes.item(nextElem); while (child.getNodeType() != Node.ELEMENT_NODE) { if (++nextElem >= numOfNodes) { SAML2SDKUtils.debug.error("AssertionImpl.processElement():" + " assertion has no subelements"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_subelements")); } child = (Node)nodes.item(nextElem); } // The first subelement should be String childName = child.getLocalName(); if ((childName == null) || (!childName.equals(ASSERTION_ISSUER))) { SAML2SDKUtils.debug.error("AssertionImpl.processElement():"+ " the first element is not "); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_subelement_issuer")); } issuer = factory.getInstance().createIssuer((Element)child); if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); while (child.getNodeType() != Node.ELEMENT_NODE) { if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); } // The next subelement may be childName = child.getLocalName(); if ((childName != null) && childName.equals(ASSERTION_SIGNATURE)) { signature = XMLUtils.print((Element)child); if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); while (child.getNodeType() != Node.ELEMENT_NODE) { if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); } childName = child.getLocalName(); } else { signature = null; } // The next subelement may be if ((childName != null) && childName.equals(ASSERTION_SUBJECT)) { subject = factory.createSubject((Element)child); if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); while (child.getNodeType() != Node.ELEMENT_NODE) { if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); } childName = child.getLocalName(); } else { subject = null; } // The next subelement may be if ((childName != null) && childName.equals(ASSERTION_CONDITIONS)) { conditions = factory.createConditions((Element)child); if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); while (child.getNodeType() != Node.ELEMENT_NODE) { if (++nextElem >= numOfNodes) { return; } child = (Node)nodes.item(nextElem); } childName = child.getLocalName(); } else { conditions = null; } // The next subelement may be if ((childName != null) && childName.equals(ASSERTION_ADVICE)) { advice = factory.createAdvice((Element)child); nextElem++; } else { advice = null; } // The next subelements are all statements while (nextElem < numOfNodes) { child = (Node)nodes.item(nextElem); if (child.getNodeType() == Node.ELEMENT_NODE) { childName = child.getLocalName(); if (childName != null) { if (childName.equals(ASSERTION_AUTHNSTATEMENT)) { authnStatements.add( factory.createAuthnStatement((Element)child)); } else if (childName.equals( ASSERTION_AUTHZDECISIONSTATEMENT)) { authzDecisionStatements.add(factory. createAuthzDecisionStatement((Element)child)); } else if (childName.equals( ASSERTION_ATTRIBUTESTATEMENT)) { attributeStatements.add(factory. createAttributeStatement((Element)child)); } else if ((childName != null) && childName.equals(ASSERTION_SIGNATURE)) { signature = XMLUtils.print((Element)child); } else { String type = ((Element)child).getAttribute( XSI_TYPE_ATTR); if (childName.equals(ASSERTION_STATEMENT) && (type != null && type.length() > 0)) { statements.add(XMLUtils.print((Element)child)); } else { SAML2SDKUtils.debug.error( "AssertionImpl.processElement(): " + "unexpected subelement " + childName); throw new SAML2Exception(SAML2SDKUtils.bundle. getString("unexpected_subelement")); } } } } nextElem++; } } /** * Returns the version number of the assertion. * * @return The version number of the assertion. */ @Override public String getVersion() { return version; } /** * Sets the version number of the assertion. * * @param version the version number. * @exception SAML2Exception if the object is immutable */ @Override public void setVersion(String version) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.version = version; } /** * Returns the time when the assertion was issued * * @return the time of the assertion issued */ @Override public Date getIssueInstant() { return issueInstant; } /** * Set the time when the assertion was issued * * @param issueInstant the issue time of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setIssueInstant(Date issueInstant) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.issueInstant = issueInstant; } /** * Returns the subject of the assertion * * @return the subject of the assertion */ @Override public Subject getSubject() { return subject; } /** * Sets the subject of the assertion * * @param subject the subject of the assertion * @exception SAML2Exception if the object is immutable */ public void setSubject(Subject subject) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.subject = subject; } /** * Returns the advice of the assertion * * @return the advice of the assertion */ @Override public Advice getAdvice() { return advice; } /** * Sets the advice of the assertion * * @param advice the advice of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setAdvice(Advice advice) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.advice = advice; } /** * Returns the signature of the assertion * * @return the signature of the assertion */ @Override public String getSignature() { return signature; } /** * Returns the conditions of the assertion * * @return the conditions of the assertion */ @Override public Conditions getConditions() { return conditions; } /** * Sets the conditions of the assertion * * @param conditions the conditions of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setConditions(Conditions conditions) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.conditions = conditions; } /** * Returns the id of the assertion * * @return the id of the assertion */ @Override public String getID() { return id; } /** * Sets the id of the assertion * * @param id the id of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setID(String id) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.id = id; } /** * Returns the statements of the assertion * * @return the statements of the assertion */ @Override public List getStatements() { return statements; } /** * Returns the Authn statements of the assertion * * @return the Authn statements of the assertion */ @Override public List getAuthnStatements() { return authnStatements; } /** * Returns the AuthzDecisionStatements of the assertion * * @return the AuthzDecisionStatements of the assertion */ @Override public List getAuthzDecisionStatements() { return authzDecisionStatements; } /** * Returns the attribute statements of the assertion * * @return the attribute statements of the assertion */ @Override public List getAttributeStatements() { return attributeStatements; } /** * Sets the statements of the assertion * * @param statements the statements of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setStatements(List statements) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.statements = statements; } /** * Sets the AuthnStatements of the assertion * * @param statements the AuthnStatements of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setAuthnStatements(List statements) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } authnStatements = statements; } /** * Sets the AuthzDecisionStatements of the assertion * * @param statements the AuthzDecisionStatements of * the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setAuthzDecisionStatements(List statements) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } authzDecisionStatements = statements; } /** * Sets the attribute statements of the assertion * * @param statements the attribute statements of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setAttributeStatements(List statements) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } attributeStatements = statements; } /** * Returns the issuer of the assertion * * @return the issuer of the assertion */ @Override public Issuer getIssuer() { return issuer; } /** * Sets the issuer of the assertion * * @param issuer the issuer of the assertion * @exception SAML2Exception if the object is immutable */ @Override public void setIssuer(Issuer issuer) throws SAML2Exception { if (!isMutable) { throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "objectImmutable")); } this.issuer = issuer; } /** * Return whether the assertion is signed * * @return true if the assertion is signed; false otherwise. */ @Override public boolean isSigned() { return (signature != null); } @Override public boolean isSignatureValid(Set verificationCerts) throws SAML2Exception { if (isSignatureValid == null) { if (signedXMLString == null) { signedXMLString = toXMLString(true, true); } isSignatureValid = SigManager.getSigInstance().verify(signedXMLString, getID(), verificationCerts); } return isSignatureValid.booleanValue(); } /** * Sign the Assertion. * * @param privateKey Signing key * @param cert Certificate which contain the public key correlated to * the signing key; It if is not null, then the signature * will include the certificate; Otherwise, the signature * will not include any certificate * @exception SAML2Exception if it could not sign the assertion. */ @Override public void sign( PrivateKey privateKey, X509Certificate cert ) throws SAML2Exception { Element signatureElement = SigManager.getSigInstance().sign( toXMLString(true, true), getID(), privateKey, cert ); signature = XMLUtils.print(signatureElement); signedXMLString = XMLUtils.print( signatureElement.getOwnerDocument(). getDocumentElement(), "UTF-8"); makeImmutable(); } /** * Returns an EncryptedAssertion object. * * @param recipientPublicKey Public key used to encrypt the data encryption * (secret) key, it is the public key of the * recipient of the XML document to be encrypted. * @param dataEncAlgorithm Data encryption algorithm. * @param dataEncStrength Data encryption strength. * @param recipientEntityID Unique identifier of the recipient, it is used * as the index to the cached secret key so that * the key can be reused for the same recipient; * It can be null in which case the secret key will * be generated every time and will not be cached * and reused. Note that the generation of a secret * key is a relatively expensive operation. * @return EncryptedAssertion object * @throws SAML2Exception if error occurs during the encryption process. */ @Override public EncryptedAssertion encrypt( Key recipientPublicKey, String dataEncAlgorithm, int dataEncStrength, String recipientEntityID ) throws SAML2Exception { Element el = EncManager.getEncInstance().encrypt( toXMLString(true, true), recipientPublicKey, dataEncAlgorithm, dataEncStrength, recipientEntityID, "EncryptedAssertion" ); return AssertionFactory.getInstance(). createEncryptedAssertion(el); } /** * Gets the validity of the assertion evaluating its conditions if * specified. * * @return false if conditions is invalid based on it lying between * NotBefore (current time inclusive) and * NotOnOrAfter (current time exclusive) values * and true otherwise or if no conditions specified. */ @Override public boolean isTimeValid() { if (conditions == null) { return true; } else { return conditions.checkDateValidity(currentTimeMillis()); } } /** * Returns a String representation * @param includeNSPrefix Determines whether or not the namespace * qualifier is prepended to the Element when converted * @param declareNS Determines whether or not the namespace is declared * within the Element. * @return A String representation * @exception SAML2Exception if something is wrong during conversion */ @Override public String toXMLString(boolean includeNSPrefix, boolean declareNS) throws SAML2Exception { if ((signature != null) && (signedXMLString != null)) { return signedXMLString; } StringBuffer sb = new StringBuffer(2000); String NS = ""; String appendNS = ""; if (declareNS) { NS = SAML2Constants.ASSERTION_DECLARE_STR; } if (includeNSPrefix) { appendNS = SAML2Constants.ASSERTION_PREFIX; } sb.append("<").append(appendNS).append(ASSERTION_ELEMENT).append(NS); if ((version == null) || (version.length() == 0)) { SAML2SDKUtils.debug.error( "AssertionImpl.toXMLString(): version missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_assertion_version")); } sb.append(" ").append(ASSERTION_VERSION_ATTR).append("=\""). append(version).append("\""); if ((id == null) || (id.length() == 0)) { SAML2SDKUtils.debug.error( "AssertionImpl.toXMLString(): assertion id missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_assertion_id")); } sb.append(" ").append(ASSERTION_ID_ATTR).append("=\""). append(id).append("\""); if (issueInstant == null) { SAML2SDKUtils.debug.error( "AssertionImpl.toXMLString(): issue instant missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_issue_instant")); } String instantStr = DateUtils.toUTCDateFormat(issueInstant); sb.append(" ").append(ASSERTION_ISSUEINSTANT_ATTR).append("=\""). append(instantStr).append("\"").append(">\n"); if (issuer == null) { SAML2SDKUtils.debug.error( "AssertionImpl.toXMLString(): issuer missing"); throw new SAML2Exception(SAML2SDKUtils.bundle.getString( "missing_subelement_issuer")); } sb.append(issuer.toXMLString(includeNSPrefix, false)); if (signature != null) { sb.append(signature); } if (subject != null) { sb.append(subject.toXMLString(includeNSPrefix, false)); } if (conditions != null) { sb.append(conditions.toXMLString(includeNSPrefix, false)); } if (advice != null) { sb.append(advice.toXMLString(includeNSPrefix, false)); } int length = 0; if (statements != null) { length = statements.size(); for (int i = 0; i < length; i++) { String str = (String)statements.get(i); sb.append(str); } } if (authnStatements != null) { length = authnStatements.size(); for (int i = 0; i < length; i++) { AuthnStatement st = (AuthnStatement)authnStatements.get(i); sb.append(st.toXMLString(includeNSPrefix, false)); } } if (authzDecisionStatements != null) { length = authzDecisionStatements.size(); for (int i = 0; i < length; i++) { AuthzDecisionStatement st = (AuthzDecisionStatement)authzDecisionStatements.get(i); sb.append(st.toXMLString(includeNSPrefix, false)); } } if (attributeStatements != null) { length = attributeStatements.size(); for (int i = 0; i < length; i++) { AttributeStatement st = (AttributeStatement)attributeStatements.get(i); sb.append(st.toXMLString(includeNSPrefix, false)); } } sb.append("\n"); //return SAML2Utils.removeNewLineChars(sb.toString()); return sb.toString(); } /** * Returns a String representation * * @return A String representation * @exception SAML2Exception if something is wrong during conversion */ @Override public String toXMLString() throws SAML2Exception { return this.toXMLString(true, false); } /** * Makes the object immutable */ @Override public void makeImmutable() { if (isMutable) { if (authnStatements != null) { int length = authnStatements.size(); for (int i = 0; i < length; i++) { AuthnStatement authn = (AuthnStatement)authnStatements.get(i); authn.makeImmutable(); } authnStatements = Collections.unmodifiableList( authnStatements); } if (authzDecisionStatements != null) { int length = authzDecisionStatements.size(); for (int i = 0; i < length; i++) { AuthzDecisionStatement authz = (AuthzDecisionStatement)authzDecisionStatements.get(i); authz.makeImmutable(); } authzDecisionStatements = Collections.unmodifiableList( authzDecisionStatements); } if (attributeStatements != null) { int length = attributeStatements.size(); for (int i = 0; i < length; i++) { AttributeStatement attr = (AttributeStatement)attributeStatements.get(i); attr.makeImmutable(); } attributeStatements = Collections.unmodifiableList( attributeStatements); } if (statements != null) { statements = Collections.unmodifiableList(statements); } if (conditions != null) { conditions.makeImmutable(); } if (issuer != null) { issuer.makeImmutable(); } if (subject != null) { subject.makeImmutable(); } if (advice != null) { advice.makeImmutable(); } isMutable = false; } } /** * Returns true if the object is mutable * * @return true if the object is mutable */ @Override public boolean isMutable() { return isMutable; } }