SAMLUtils.java revision a688bcbb4bcff5398fdd29b86f83450257dc0df4
/**
* 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
* 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: SAMLUtils.java,v 1.16 2010/01/09 19:41:06 qcheng Exp $
*
*/
//import com.sun.org.apache.xml.internal.security.Init;
/**
* This class contains some utility methods for processing SAML protocols.
*
* @supported.api
*/
public class SAMLUtils extends SAMLUtilsCommon {
/**
* Attribute that specifies maximum content length for SAML request in
* <code>AMConfig.properties</code> file.
*/
public static final String HTTP_MAX_CONTENT_LENGTH =
"com.sun.identity.saml.request.maxContentLength";
/**
* Default maximum content length is set to 16k.
*/
public static final int defaultMaxLength = 16384;
/**
* Default maximum content length in string format.
*/
public static final String DEFAULT_CONTENT_LENGTH =
private static int maxContentLength = 0;
static {
if (SystemConfigurationUtil.isServerMode()) {
}
};
180000, true);
}
try {
} catch (NumberFormatException ne) {
+ "length. Take default value.");
}
}
/**
* Constructor
* iPlanet-PRIVATE-DEFAULT-CONSTRUCTOR
*/
private SAMLUtils() {
}
/**
* Generates an ID String with length of SAMLConstants.ID_LENGTH.
* @return string the ID String; or null if it fails.
*/
public static String generateAssertionID() {
return null;
}
try {
+ "exception obtain serverID:", ex);
}
}
return encodedID;
} else {
}
}
/**
* Verifies if an element is a type of a specific query.
* Currently, this method is used by class AuthenticationQuery,
* AuthorizationDecisionQuery, and AttributeQuery.
* @param element a DOM Element which needs to be verified.
* @param queryname A specific name of a query, for example,
* AuthenticationQuery, AuthorizationDecisionQuery, or
* AttributeQuery.
* @return true if the element is a type of the specified query; false
* otherwise.
*/
return false;
boolean found = false;
for (int j = 0; j < len; j++) {
found = true;
break;
}
}
if (!found) {
return false;
}
return false;
}
return true;
}
/**
* Generates sourceID of a site.
* @param siteURL a String that uniquely identifies a site.
* @return <code>Base64</code> encoded SHA digest of siteURL.
*/
return null;
}
try {
} catch (Exception e) {
+ " generating digest:",e);
return null;
}
try {
} catch (Exception e) {
}
return result;
}
/**
* Generates assertion handle.
* @return 20-byte random string to be used to form an artifact.
*/
public static String generateAssertionHandle() {
return null;
}
try {
+ "exception obtain serverID:", ex);
}
}
// TODO: should we check if idBytes.length == 2 ?
}
}
}
return byteArrayToString(bytes);
}
/**
* Converts a HEX encoded string to a byte array.
* @param hexString HEX encoded string
* @return byte array.
*/
byteArray[j] =
byteValue();
i++;
}
return byteArray;
}
/**
* Converts HEX encoded string to Base64 encoded string.
* @param hexString HEX encoded string.
* @return Base64 encoded string.
*/
byteArray[j] =
byteValue();
i++;
}
try {
} catch (Exception e) {
+ "exception encode input:", e);
}
}
}
return encodedID;
}
/**
* Gets sourceSite corresponding to an issuer from the partner URL list.
* @param issuer The issuer string.
* @return SAMLServiceManager.SOAPEntry of the issuer if it's on the list;
* null otherwise.
*/
return null;
}
+ "list is null.");
return null;
}
boolean found = false;
found = true;
break;
}
}
if (found) {
return srcSite;
} else {
return null;
}
}
/**
* Returns site ID based on the host name. The site ID
* will be in Base64 encoded format. This method will print out site ID
* to the standard output
* @param args host name
*/
return;
}
}
/**
* Checks if a <code>SubjectConfirmation</code> is correct.
* @param sc <code>SubjectConfirmation</code> instance to be checked.
* @return true if the <code>SubjectConfirmation</code> instance passed in
* has only one <code>ConfirmationMethod</code>, and this
* <code>ConfirmationMethod</code> is set to
* <code>SAMLConstants.CONFIRMATION_METHOD_IS</code>.
*/
return false;
}
+ " missing ConfirmationMethod in the Subject.");
}
return false;
}
+ " wrong ConfirmationMethod value.");
}
return false;
}
return true;
}
/**
* Returns true if the assertion is valid both time wise and
* signature wise, and contains at least one AuthenticationStatement.
* @param assertion <code>Assertion</code> instance to be checked.
* @return <code>true</code> if the assertion is valid both time wise and
* signature wise, and contains at least one AuthenticationStatement.
*/
return false;
}
return false;
}
if (statement.getStatementType() ==
return true;
}
} // loop through statements
return false;
}
/**
* Converts a string to a byte array.
* @param input a String to be converted.
* @return result byte array.
*/
}
return bytes;
}
/**
* Returns server ID.
* @param idTypeString An ID string
* @return server ID part of the id.
*/
if (idTypeString == null) {
return null;
}
len);
return id;
} else {
return null;
}
}
/**
* Returns server url of a site.
* @param str Server ID.
* @return Server url corresponding to the server id.
*/
return null;
}
}
try {
} catch (SystemConfigurationException se) {
"NotFoundException for " + id);
}
return null;
}
}
return null;
} else {
return remoteUrl;
}
}
/**
* Returns full service url.
* @param shortUrl short URL of the service.
* @return full service url.
*/
try {
"full remote URL is: " + result);
}
} catch (Exception e) {
"Exception:", e);
}
}
return result;
}
/**
* Returns attributes included in <code>AttributeStatement</code> of the
* assertion.
* @param envParameters return map which includes name value pairs of
* attributes included in <code>AttributeStatement</code> of the assertion
* @param assertion an <code>Assertion</code> object which contains
* <code>AttributeStatement</code>
* @param subject the <code>Subject</code> instance from
* <code>AuthenticationStatement</code>. The <code>Subject</code>
* included in <code>AttributeStatement</code> must match this
* <code>Subject</code> instance.
*/
if (statement.getStatementType() ==
// check for subject
continue;
}
try {
} catch (Exception e) {
" cannot obtain attribute value:", e);
continue;
}
if (attrValueList == null) {
attrValueList = new ArrayList();
}
}
}
if (attrValueList != null) {
if (debug.messageEnabled()) {
"SAMLUtils.addEnvParamsFromAssertion:" +
" attrName = " + attrName +
" attrValue = " + attrValueList);
}
try {
if (debug.messageEnabled()) {
"SAMLUtils.addEnvParamsFromAssertion:",
ex);
}
}
} else if (debug.messageEnabled()) {
if (debug.messageEnabled()) {
"SAMLUtils.addEnvParamsFromAssertion:" +
" attrName = " + attrName +
" has no value");
}
}
}
} // if it's an attribute statement
}
}
}
/**
* Returns maximum content length of a SAML request.
* @return maximum content length of a SAML request.
*/
public static int getMaxContentLength() {
return maxContentLength;
}
// ************************************************************************
// Methods used by SAML Servlets
// ************************************************************************
/**
* Checks content length of a http request to avoid dos attack.
* In case SAML inter-op with other SAML vendor who may not provide content
* length in HttpServletRequest. We decide to support no length restriction
* for Http communication. Here, we use a special value (e.g. 0) to
* indicate that no enforcement is required.
* @param request <code>HttpServletRequest</code> instance to be checked.
* @exception ServletException if context length of the request exceeds
* maximum content length allowed.
*/
throws ServletException {
if (maxContentLength != 0) {
}
if (length > maxContentLength) {
"content length too large" + length);
}
throw new ServletException(
}
}
}
/**
* Post assertions and attributes to the target url.
* This method opens a URL connection to the target specified and POSTs
* assertions to it using the passed HttpServletResponse object. It POSTs
* multiple parameter names "assertion" with value being each of the
* <code>Assertion</code> in the passed Set.
* @param response <code>HttpServletResponse</code> object
* @param assertion List of <code>Assertion</code>s to be posted.
* @param targeturl target url
* @param attrMap Map of attributes to be posted to the target
*/
"</STRONG> according to the assertion shown "
+"below. \n");
}
}
}
}
}
}
}
attrNamesSB + "\">");
}
}
/**
* Returns true of false based on whether the target passed as parameter
* accepts form POST.
* @param targetIn url to be checked
* @return true if it should post assertion to the target passed in; false
* otherwise.
*/
return false;
}
return false;
}
try {
} catch (MalformedURLException me ) {
return false;
}
// making target string without protocol
return true;
} else {
return false;
}
}
/**
* Replaces every occurence of ch with
* "&#<ascii code of ch>;"
* @param srcStr orginal string to to be encoded.
* @param ch the charactor needs to be encoded.
* @return encoded string
*/
return null;
}
int fromIndex = 0;
int toIndex;
}
}
/**
* Displays an XML string.
* This is a utility function used to hack up an HTML display of an XML
* string.
* @param input original string
* @return encoded string so it can be displayed properly by html.
*/
if (c=='>') {
} else if (c=='<') {
} else if (c=='\n'){
} else {
}
}
}
/**
* Gets the list of <code>Assertion</code> objects from a list of
* 'String' assertions.
* @param assertions List of assertions in string format
* @return List of <code>Assertion</code> objects
*/
try {
if (assertions != null) {
debug);
}
}
}
} catch (Exception e) {
if (debug.messageEnabled()) {
"Exception : ", e);
}
}
return returnAssertions;
}
// ************************************************************************
// Methods used / shared by SAML Authentication Module and SAML Servlets
// ************************************************************************
/**
* Returns byte array from a SAML <code>Response</code>.
* @param samlResponse <code>Response</code> object
* @return byte array
* @exception SAMLException if error occurrs during the process.
*/
throws SAMLException
{
try {
} catch (UnsupportedEncodingException ue) {
if (debug.messageEnabled()) {
}
}
return ret;
}
/**
* Returns <code>Response</code> object from byte array.
* @param bytes byte array
* @return <code>Response</code> object
*/
return null;
}
try {
} catch (SAMLException se) {
}
return temp;
}
/**
* Verifies a <code>Response</code>.
* @param response SAML <code>Response</code> object
* @param requestUrl this server's POST profile URL
* @param request <code>HttpServletRequest</code> object
* @return true if the response is valid; false otherwise.
*/
if (!response.isSignatureValid()) {
return false;
}
// check Recipient == this server's POST profile URL(requestURL)
return false;
}
// check status of the Response
return false;
}
return true;
}
{
return requestUrl;
}
if (index == -1) {
return requestUrl;
}
}
if (debug.messageEnabled()) {
}
}
// ************************************************************************
// Methods used by SAML Authentication Module
// ************************************************************************
/**
* Gets List of assertions in String format from a list of
* <code>Assertion</code> objects.
* @param assertions List of <code>Assertion</code> objects.
* @return List of assertions in String format
*/
if (assertions != null) {
}
}
return returnAssertions;
}
/**
* Verifies Signature for Post response.
* @param samlResponse <code>Response</code> object from post profile.
* @return true if the signature on the reponse is valid; false otherwise.
*/
if ((samlResponse != null) &&
return false;
}
return true;
}
/**
* Gets Attribute Map to be set in the Session.
* @param partnerdest <code>SOAPEntry</code> object
* @param assertions List of <code>Assertion</code>s
* @param subject <code>Subject</code> object
* @param target target of final SSO
* @return Map which contains name and attributes.
* @exception Exception if an error occurrs.
*/
public static Map getAttributeMap(
throws Exception {
}
}
} else {
}
if (debug.messageEnabled()) {
}
return attrMap;
}
/**
* Checks response and get back a Map of relevant data including,
* Subject, SOAPEntry for the partner and the List of Assertions.
* @param response <code>Response</code> object
* @return Map of data including Subject, SOAPEntry, and list of assertions.
*/
// loop to check assertions
// make sure it's not being used
return null;
}
// check issuer of the assertions
+ "onAndGetSSMap: issuer is not on the Partner list.");
return null;
}
if (!assertion.isSignatureValid()) {
+ "AndGetSSMap: assertion's signature is not valid.");
return null;
}
// must be valid (timewise)
if (!assertion.isTimeValid()) {
+ "AndGetSSMap: assertion's time is not valid.");
return null;
}
// TODO: IssuerInstant of the assertion is within a few minutes
// This is a MAY in spec. Which number to use for the few minutes?
// TODO: check AudienceRestrictionCondition
//for each assertion, loop to check each statement
// ConfirmationMethod of each subject must be set to bearer
== null) ||
+ "AssertionAndGetSSMap: missing or extra "
+ "ConfirmationMethod.");
return null;
}
== null) ||
(!confMethod.equals(
+ "AssertionAndGetSSMap:wrong ConfirmationMethod.");
return null;
}
//TODO: must contain same Subject for all statements?
//TODO: if it has SubjectLocality,its IP must == sender
// browser IP. This is a MAY item in the spec.
}
}
}
}
// add the assertion to idTimeMap
if (debug.messageEnabled()) {
}
} else {
// it doesn't matter what we store for the value.
}
}
// must have at least one SSO assertion
return null;
}
return ssMap;
}
/**
* Checks if the Assertion is time valid and
* if the Assertion is allowed by AudienceRestrictionCondition.
*
* @param assertion an Assertion object
* @return true if the operation is successful otherwise, return false
* @exception IOException IOException
*/
throws IOException
{
return false;
}
if (!assertion.isSignatureValid()) {
return false;
}
// check if the Assertion is time valid
if (!(assertion.isTimeValid())) {
return false;
}
// check the Assertion is allowed by AudienceRestrictionCondition
if (audienceCnd != null) {
if (!audienceCnd.isEmpty()) {
if (debug.messageEnabled()) {
"RestrictionConditions is indeterminate.");
}
} else {
return false;
}
}
}
}
return true;
}
/**
* Determines if there is a valid SSO Assertion
* inside of SAML Response.
*
* @param assertions a List of <code>Assertion</code> objects
* @return a Subject object
* @exception IOException IOException
*/
if (assertions == null) {
return null;
}
boolean validation = false;
if (!checkCondition(assertion)) {
return null;
}
// exam the Statement inside the Assertion
return null;
}
return null;
}
// add checking artifact confirmation method identifier based
// on Assertion version number
(((assertion.getMinorVersion() ==
||
((assertion.getMinorVersion() ==
if (debug.messageEnabled()) {
}
} else {
return null;
}
if (statement instanceof AuthenticationStatement) {
//found an SSO Assertion
validation = true;
}
} // end of while (iterator.hasNext()) for Statements
} // end of while (iter.hasNext()) for Assertions
if (!validation) {
return null;
}
return subject;
}
/**
* Return whether the signature on the object is valid or not.
* @param xmlString input XML String
* @param idAttribute ASSERTION_ID_ATTRIBUTE or RESPONSE_ID_ATTRIBUTE
* @param issuer the issuer of the Assertion
* @return true if the signature on the object is valid; false otherwise.
*/
{
boolean valid = true;
}
}
try {
} catch (Exception e) {
" signature validation exception", e);
valid = false;
}
if (!valid) {
" Couldn't verify signature.");
}
}
return valid;
}
/**
* Sets the given <code>HttpServletResponse</code> object with the
* headers in the given <code>MimeHeaders</code> object.
* @param headers the <code>MimeHeaders</code> object
* @param response the <code>HttpServletResponse</code> object to which the
* headers are to be written.
*/
public static void setMimeHeaders(
return;
}
} else {
int i = 0;
if (i != 0) {
}
}
}
}
return;
}
/**
* Returns a <code>MimeHeaders</code> object that contains the headers
* in the given <code>HttpServletRequest</code> object.
*
* @param req the <code>HttpServletRequest</code> object.
* @return a new <code>MimeHeaders</code> object containing the headers.
*/
return headers;
}
while(enumerator.hasMoreElements()) {
while(values.hasMoreTokens()) {
}
}
return headers;
}
/**
* Returns the authenticaion login url with goto parameter
* in the given <code>HttpServletRequest</code> object.
*
* @param req the <code>HttpServletRequest</code> object.
* @return a new authenticaion login url with goto parameter.
*/
int startIdx = -1;
int endIdx = -1;
}
if (startIdx == 0) {
} else {
}
}
}
} else {
}
}
return redirectUrl;
}
/**
* Processes SAML Artifact
* @param artifact SAML Artifact
* @param target Target URL
* @return Attribute Map
* @exception SAMLException if failed to get the Assertions or
* Attribute Map.
*/
throws SAMLException {
// Call SAMLClient to do the Single-sign-on
try {
//exam the SAML response
return null;
}
("nullPartnerUrl"));
}
if (partnerdest == null) {
("failedAccountMapping"));
}
throw new SAMLException(
}
return sessMap;
}
/**
* Creates Session
* @param request HttpServletRequest
* @param response HttpServletResponse
* @param attrMap Attribute Map
* @exception if failed to create Session
*/
realm = "/";
}
if (principalName == null) {
}
//TODO: sessionInfoMap.put(SessionProvider.AUTH_LEVEL, "0");
try {
} catch (SessionException se) {
if (debug.messageEnabled()) {
}
throw new SAMLException(se);
}
return session;
}
/**
* Processes SAML Response
* @param samlResponse SAML Response object
* @param target Target URL
* @return Attribute Map
* @exception SAMLException if failed to get Attribute Map.
*/
throws SAMLException {
if (samlResponse.isSigned()) {
// verify the signature
if (!isSignedandValid) {
}
}
// check Assertion and get back a Map of relevant data including,
// Subject, SOAPEntry for the partner and the List of Assertions.
if (debug.messageEnabled()) {
}
}
if (assertionSubject == null) {
}
if (partnerdest == null) {
}
try {
throw new SAMLException(
}
return sessMap;
}
/**
*Sets the attribute map in the session
*
*@param attrMap, the Attribute Map
*@param session, the valid session object
*@exception SessionException if failed to set Attribute in the
* Session.
*/
private static void setAttrMapInSession(
throws SessionException {
// ignore
continue;
} else {
}
if (debug.messageEnabled()) {
attrName);
}
}
}
}
/**
* Compares two URLs to see if they are equal. Two URLs are equal if
* they have same protocol, host, port and path (case ignored).
* Note : the method is provided to avoid URL.equals() call which requires
* name lookup. Name lookup is a blocking operation and very expensive
* if the hostname could not be resolved.
*
* @return true if the URLs are equal, false otherwise.
*/
try {
if (port1 == -1) {
}
if (port2 == -1) {
}
return true;
} else {
return false;
}
} catch (MalformedURLException m) {
return false;
}
}
/**
* Gets input Node Canonicalized
*
* @param node Node
* @return Canonical element if the operation succeeded.
* Otherwise, return null.
*/
try {
dfactory.setNamespaceAware(true);
new ByteArrayInputStream(outputBytes));
return result;
} catch (Exception e) {
"Error while performing canonicalization on " +
"the input Node.");
return null;
}
}
/**
* Sends to error page URL for SAML protocols. If the error page is
* hosted in the same web application, forward is used with
* parameters. Otherwise, redirection or HTTP POST is used with
* parameters.
* Three parameters are passed to the error URL:
* -- errorcode : Error key, this is the I18n key of the error message.
* -- httpstatuscode : Http status code for the error
* -- message : detailed I18n'd error message
* @param request HttpServletRequest object
* @param response HttpServletResponse object
* @param httpStatusCode Http Status code
* @param errorCode Error code
* @param errorMsg Detailed error message
*/
if(debug.messageEnabled()) {
}
// use forward
jointString = "&";
}
try {
} catch (ServletException e) {
+ "occured while trying to forward to resource:"
+ newUrl , e);
try {
e.getMessage());
} catch (IOException ioe) {
}
} catch (IOException e) {
+ "occured while trying to forward to resource:"
+ newUrl , e);
try {
e.getMessage());
} catch (IOException ioe) {
}
}
} else {
// use FSUtils, this may be redirection or forward
jointString = "&";
}
} else {
try {
+ errorUrl + "\">");
httpStatusCode + "\">");
"\"/></CENTER></NOSCRIPT>");
return;
} catch (IOException ie) {
}
}
}
}
}