001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: SAML2SDKUtils.java,v 1.12 2008/08/31 05:49:48 bina Exp $
026 *
027 * Portions copyright 2014 ForgeRock AS.
028 *
029 */
030
031
032package com.sun.identity.saml2.common;
033
034import com.sun.identity.liberty.ws.disco.ResourceOffering;
035import com.sun.identity.liberty.ws.security.SecurityAssertion;
036import com.sun.identity.plugin.session.SessionManager;
037import com.sun.identity.plugin.session.SessionProvider;
038import com.sun.identity.saml.common.SAMLConstants;
039import com.sun.identity.saml.common.SAMLUtilsCommon;
040import com.sun.identity.saml2.jaxb.entityconfig.BaseConfigType;
041import com.sun.identity.saml2.meta.SAML2MetaUtils;
042import com.sun.identity.shared.configuration.SystemPropertiesManager;
043import com.sun.identity.shared.debug.Debug;
044import com.sun.identity.shared.locale.Locale;
045import com.sun.identity.shared.xml.XMLUtils;
046
047import java.security.SecureRandom;
048import java.lang.reflect.Constructor;
049import java.lang.reflect.InvocationTargetException;
050import java.util.ArrayList;
051import java.util.HashMap;
052import java.util.List;
053import java.util.Map;
054import java.util.ResourceBundle;
055import javax.servlet.http.HttpServletRequest;
056import javax.xml.soap.SOAPException;
057import org.w3c.dom.Attr;
058import org.w3c.dom.Element;
059import org.w3c.dom.NamedNodeMap;
060
061/**
062 * The <code>SAML2SDKUtils</code> contains utility methods for SAML 2.0
063 * implementation.
064 *
065 * @supported.all.api
066 */
067public class SAML2SDKUtils {
068    //
069    // This utility class will be run on client side as well,
070    // so DO NOT add any static block which will not run on client side.
071    //
072    // The debugging instances
073    public static Debug debug = Debug.getInstance("libSAML2");
074    private static Debug decryptDebug = Debug.getInstance("SAML2Decrypt");
075    //  SAML2 Resource bundle
076    public static final String BUNDLE_NAME = "libSAML2";
077    // The resource bundle for SAML 2.0 implementation.
078    public static ResourceBundle bundle = Locale.
079        getInstallResourceBundle(BUNDLE_NAME);
080    protected static final String SAML2ID_PREFIX = "s2";
081    public static SecureRandom random = new SecureRandom();
082
083    /**
084     * Defines mapping between interface and implementation class,
085     * the properties are read from AMConfig.properties in following format:
086     * com.sun.identity.saml2.sdk.mapping.<interface>=<implementation_class>
087     * e.g.
088     * com.sun.identity.saml2.sdk.mapping.Assertion=com.xxx.saml2.AssertionImpl
089     */
090    private static Map classMapping = new HashMap();
091
092    // define constants for the interface names
093    public static final String ACTION = "Action"; 
094    public static final String ADVICE = "Advice"; 
095    public static final String ASSERTION = "Assertion";
096    public static final String ASSERTION_ID_REF = "AssertionIDRef";
097    public static final String ASSERTION_ID_REQUEST = "AssertionIDRequest";
098    public static final String ATTRIBUTE = "Attribute"; 
099    public static final String ATTRIBUTE_STATEMENT = "AttributeStatement";
100    public static final String AUDIENCE_RESTRICTION = "AudienceRestriction"; 
101    public static final String AUTHN_CONTEXT = "AuthnContext"; 
102    public static final String AUTHN_STATEMENT = "AuthnStatement"; 
103    public static final String AUTHZ_DECISION_STATEMENT = 
104        "AuthzDecisionStatement"; 
105    public static final String BASEID = "BaseID"; 
106    public static final String CONDITION = "Condition"; 
107    public static final String CONDITIONS = "Conditions"; 
108    public static final String ENCRYPTED_ASSERTION = "EncryptedAssertion";
109    public static final String ENCRYPTED_ATTRIBUTE = "EncryptedAttribute"; 
110    public static final String ENCRYPTED_ELEMENT = "EncryptedElement"; 
111    public static final String ENCRYPTEDID = "EncryptedID"; 
112    public static final String EVIDENCE = "Evidence";
113    public static final String ISSUER = "Issuer"; 
114    public static final String KEYINFO_CONFIRMATION_DATA = 
115        "KeyInfoConfirmationData"; 
116    public static final String NAMEID = "NameID"; 
117    public static final String ONE_TIME_USE = "OneTimeUse"; 
118    public static final String PROXY_RESTRICTION = "ProxyRestriction"; 
119    public static final String STATEMENT = "Statement"; 
120    public static final String SUBJECT_CONFIRMATION_DATA = 
121        "SubjectConfirmationData"; 
122    public static final String SUBJECT_CONFIRMATION = "SubjectConfirmation"; 
123    public static final String SUBJECT = "Subject";
124    public static final String SUBJECT_LOCALITY = "SubjectLocality"; 
125    public static final String ARTIFACT = "Artifact"; 
126    public static final String ARTIFACT_RESOLVE = "ArtifactResolve"; 
127    public static final String ARTIFACT_RESPONSE = "ArtifactResponse";
128    public static final String ATTRIBUTE_QUERY = "AttributeQuery";
129    public static final String AUTHN_QUERY = "AuthnQuery";
130    public static final String AUTHN_REQUEST = "AuthnRequest";
131    public static final String ECP_RELAY_STATE = "ECPRelayState";
132    public static final String ECP_REQUEST = "ECPRequest";
133    public static final String ECP_RESPONSE = "ECPResponse";
134    public static final String EXTENSIONS = "Extensions"; 
135    public static final String GET_COMPLETE = "GetComplete"; 
136    public static final String IDPENTRY = "IDPEntry"; 
137    public static final String IDPLIST = "IDPList";
138    public static final String LOGOUT_REQUEST = "LogoutRequest"; 
139    public static final String LOGOUT_RESPONSE = "LogoutResponse"; 
140    public static final String MANAGE_NAMEID_REQUEST = "ManageNameIDRequest"; 
141    public static final String MANAGE_NAMEID_RESPONSE = "ManageNameIDResponse"; 
142    public static final String NAMEID_POLICY = "NameIDPolicy"; 
143    public static final String NEW_ENCRYPTEDID = "NewEncryptedID"; 
144    public static final String NEWID = "NewID";
145    public static final String REQUESTED_AUTHN_CONTEXT = 
146        "RequestedAuthnContext"; 
147    public static final String REQUESTERID = "RequesterID"; 
148    public static final String RESPONSE = "Response";
149    public static final String SCOPING = "Scoping"; 
150    public static final String SESSION_INDEX = "SessionIndex"; 
151    public static final String STATUS_CODE = "StatusCode"; 
152    public static final String STATUS_DETAIL = "StatusDetail"; 
153    public static final String STATUS = "Status";
154    public static final String STATUS_MESSAGE = "StatusMessage"; 
155    public static final String STATUS_RESPONSE = "StatusResponse"; 
156    public static final String NAMEIDMAPPING_REQ = "NameIDMappingRequest"; 
157    public static final String NAMEIDMAPPING_RES = "NameIDMappingResponse"; 
158
159    /**
160     * List of Interfaces in assertion and protocol packages which could have 
161     * customized implementation
162     */
163    private static String[] interfactNames = {
164        ACTION, ADVICE, ASSERTION, ASSERTION_ID_REF, ASSERTION_ID_REQUEST,
165        ATTRIBUTE, ATTRIBUTE_STATEMENT, AUDIENCE_RESTRICTION, AUTHN_CONTEXT,
166        AUTHN_STATEMENT, AUTHZ_DECISION_STATEMENT, BASEID, 
167        CONDITION, CONDITIONS, ENCRYPTED_ASSERTION,
168        ENCRYPTED_ATTRIBUTE, ENCRYPTED_ELEMENT, ENCRYPTEDID, EVIDENCE,
169        ISSUER, KEYINFO_CONFIRMATION_DATA, NAMEID,
170        ONE_TIME_USE, PROXY_RESTRICTION, STATEMENT, 
171        SUBJECT_CONFIRMATION_DATA, SUBJECT_CONFIRMATION, SUBJECT,
172        SUBJECT_LOCALITY, ARTIFACT, ARTIFACT_RESOLVE, ARTIFACT_RESPONSE,
173        ATTRIBUTE_QUERY, AUTHN_QUERY, AUTHN_REQUEST, EXTENSIONS, GET_COMPLETE,
174        IDPENTRY, IDPLIST, LOGOUT_REQUEST, LOGOUT_RESPONSE,
175        MANAGE_NAMEID_REQUEST, MANAGE_NAMEID_RESPONSE, NAMEID_POLICY,
176        NEW_ENCRYPTEDID, NEWID, REQUESTED_AUTHN_CONTEXT, REQUESTERID, RESPONSE,
177        SCOPING, SESSION_INDEX, STATUS_CODE, STATUS_DETAIL, STATUS,
178        STATUS_MESSAGE, STATUS_RESPONSE, NAMEIDMAPPING_REQ, NAMEIDMAPPING_RES}; 
179
180    /**
181     * Class array for Artifact constructor
182     */
183    private static Class[] artParam = new Class[] { (new byte[2]).getClass(), 
184        int.class, String.class, String.class };
185
186    /**
187     * Class array for String as parameter
188     */
189    private static Class[] stringParam = new Class[] {String.class};
190
191    /**
192     * Class array for Element as parameter
193     */
194    private static Class[] elementParam = new Class[] {Element.class};
195
196    static {
197        // initialize class mapper
198        int len = interfactNames.length;
199        for (int i = 0; i < len; i++) {
200            String iName = interfactNames[i];
201            try {
202                String implClass = SystemPropertiesManager.get(
203                    SAML2Constants.SDK_CLASS_MAPPING + iName);
204                if (implClass != null && implClass.trim().length() != 0) {
205                    // try it out
206                    if (debug.messageEnabled()) {
207                        debug.message("SAML2SDKUtils.init: mapper for " + iName
208                            + "=" + implClass);
209                    }
210                    classMapping.put(iName, Class.forName(implClass.trim()));
211                }
212            } catch (ClassNotFoundException cnfe) {
213                debug.error("SAML2SDKUtils.init: " + iName, cnfe);
214            } 
215        }
216    }
217    
218    /**
219     * Protected contstructor.
220     */
221    protected SAML2SDKUtils() {}
222     
223    /**
224     * Returns default object instance for a given interface. 
225     * @param iName name of the interface.
226     * @return object instance corresponding to the interface implementation. 
227     *         return null if the object instance could not be obtained.
228     */
229    public static Object getObjectInstance(String iName) {
230        Class implClass = (Class) classMapping.get(iName);
231        if (implClass == null) {
232            return null;
233        } else {
234            try {
235                return implClass.newInstance();
236            } catch (InstantiationException ie) {
237                debug.error("SAML2SDKUtils.getDefaultInstance: " + iName, ie);
238            } catch (IllegalAccessException iae) {
239                debug.error("SAML2SDKUtils.getDefaultInstance: " + iName, iae);
240            } 
241            return null;
242        }
243    }
244
245    /**
246     * Returns new object instance taking String parameter in constructor. 
247     * @param iName name of the interface.
248     * @param value String value to be used as parameter in constructor.
249     * @return object instance corresponding to the interface implementation. 
250     *         return null if the object instance could not be obtained.
251     */
252    public static Object getObjectInstance(String iName, String value){
253        Class implClass = (Class) classMapping.get(iName);
254        if (implClass == null) {
255            return null;
256        } else {
257            if (debug.messageEnabled()) {
258                debug.message("SAML2SDKUtils.getObjectInstance: new customized "
259                    + "impl (String) instance for " + iName);
260            }
261            Object[] params = new Object[] { value }; 
262            return getObjectInstance(implClass, stringParam, params);
263        }
264    }
265
266    /**
267     * Returns new object instance taking Element parameter in constructor. 
268     * @param iName name of the interface.
269     * @param value Element value to be used as parameter in constructor.
270     * @return object instance corresponding to the interface implementation. 
271     *         return null if the object instance could not be obtained.
272     */
273    public static Object getObjectInstance(String iName, Element value) {
274        Class implClass = (Class) classMapping.get(iName);
275        if (implClass == null) {
276            return null;
277        } else {
278            if (debug.messageEnabled()) {
279                debug.message("SAML2SDKUtils.getObjectInstance: new customized "
280                    + "impl instance (Element) for " + iName);
281            }
282            Object[] params = new Object[] { value }; 
283            return getObjectInstance(implClass, elementParam, params);
284        }
285    }
286
287    /**
288     * Returns new object instance with given parameters. 
289     * @param iName name of the interface.
290     * @param typecode type code.
291     * @param endpointIndex end point index.
292     * @param sourceID source ID.
293     * @param messageHandle message handler.
294     * @return object instance corresponding to the interface implementation. 
295     *         return null if the object instance could not be obtained.
296     */
297
298    public static Object getObjectInstance(String iName, byte[] typecode,
299        int endpointIndex, String sourceID, String messageHandle) {
300        Class implClass = (Class) classMapping.get(iName);
301        if (implClass == null) {
302            return null;
303        } else {
304            if (debug.messageEnabled()) {
305                debug.message("SAML2SDKUtils.getObjectInstance: new customized "
306                    + "impl (4) instance for " + iName);
307            }
308            Object[] params = new Object[] 
309                { typecode, new Integer(endpointIndex), 
310                  sourceID, messageHandle }; 
311            return getObjectInstance(implClass, artParam, params);
312        }
313    }
314
315
316    /**
317     * Returns new object instance with given parameter in constructor. 
318     * @param impl Class instance.
319     * @param paramObj Class array for constructor parameters.
320     * @param valueObj Object array for values of constructor parameters.
321     * @return object instance corresponding to the interface implementation. 
322     *         return null if the object instance could not be obtained.
323     */
324    private static Object getObjectInstance(Class impl, 
325        Class[] paramObj, Object[] valueObj) {
326        try {
327            Constructor constr = impl.getConstructor(paramObj);
328            return constr.newInstance(valueObj);
329        } catch (NoSuchMethodException nsme) {
330            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
331                nsme);
332        } catch (SecurityException se) {
333            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
334                se);
335        } catch (InstantiationException ie) {
336            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
337                ie);
338        } catch (IllegalAccessException iae) {
339            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
340                iae);
341        } catch (IllegalArgumentException iae) {
342            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
343                iae);
344        } catch (InvocationTargetException ite) {
345            debug.error("SAML2SDKUtils.getObjectInstance: " + impl.getName(), 
346                ite);
347        } 
348        return null;
349    }
350
351    /**
352     * Verifies if an element is a type of a specific statement.
353     * Currently, this method is used by class AuthnStatementImpl,
354     * AuthzDecisionStatement and AttributeStatementImpl.
355     * @param element a DOM Element which needs to be verified.
356     * @param statementname A specific name of a statement, for example,
357     *          AuthnStatement, AuthzStatement or AttributeStatement
358     * @return <code>true</code> if the element is of the specific type;
359     *          <code>false</code> otherwise.
360     */
361    public static boolean checkStatement(Element element, String statementname){
362        if (element == null || statementname == null) {
363            return false;
364        }
365        
366        String tag = element.getLocalName();
367        if (tag == null) {
368            return false;
369        } else if (tag.equals("Statement")) {
370            NamedNodeMap nm = element.getAttributes();
371            int len = nm.getLength();
372            String attrName = null;
373            Attr attr = null;
374            for (int j = 0; j < len; j++) {
375                attr = (Attr) nm.item(j);
376                attrName = attr.getLocalName();
377                if ((attrName != null) && (attrName.equals("type")) &&
378                (attr.getNodeValue().equals(statementname + "Type"))) {
379                    return true;
380                }
381            }
382        } else if (tag.equals(statementname)) {
383            return true;
384        }
385        return false;
386    }   
387
388    /**
389     * Converts byte array to String.
390     *
391     * @param bytes     Byte Array to be converted.
392     * @return          result of the conversion.
393     */
394    public static String byteArrayToString(byte[] bytes) {
395        char chars[] = new char[bytes.length];
396        for (int i = 0; i < bytes.length; i++) {
397            chars[i] = (char) bytes[i];
398        }
399        return new String(chars);
400    }
401
402    /**
403     * Converts integer to byte array.
404     *
405     * @param i         an integer value between 0 and 65535.
406     * @return          a byte array whose length is 2.
407     * @throws SAML2Exception if the input is not between 0 and 65535.
408     */
409    public static byte[] intToTwoBytes(int i)
410    throws SAML2Exception {
411        if (i < 0 || i > 65535) {
412            debug.error("SAML2Utils.intToTwoBytes: wrong index value range.");
413            throw new SAML2Exception(
414            bundle.getString("wrongInput"));
415        }
416        
417        String hexStr = Integer.toHexString(i);
418        
419        //System.out.println("Original="+hexStr);
420        
421        int len = hexStr.length();
422        String norm = null;
423        if (len > 4) {
424            norm = hexStr.substring(0,4);
425        } else {
426            switch (len) {
427                case 1:
428                    norm = "000"+hexStr;
429                    break;
430                case 2:
431                    norm = "00"+hexStr;
432                    break;
433                case 3:
434                    norm = "0"+hexStr;
435                    break;
436                default:
437                    norm = hexStr;
438            }
439        }
440        
441        byte[] bytes = hexStringToByteArray(norm);
442        
443        return bytes;
444    }
445    
446    /**
447     * Converts two bytes to an integer.
448     *
449     * @param bytes     byte array whose length is 2.
450     * @return          an integer value between 0 and 65535.
451     * @throws SAML2Exception if the input is null or the length is not 2.
452     */
453    public static int twoBytesToInt(byte[] bytes)
454    throws SAML2Exception {
455        if (bytes == null || bytes.length != 2) {
456            debug.error("SAML2Utils.twoBytesToInt: input is null or length is "
457            + "not 2.");
458            throw new SAML2Exception(bundle.getString("wrontInput"));
459        }
460        
461        String str0 = Integer.toHexString(bytes[0]);
462        int len0 = str0.length();
463        String norm0 = null;
464        if (len0 > 2) {
465            norm0 = str0.substring(len0-2, len0);
466        } else {
467            norm0 = str0;
468        }
469        String str1 = Integer.toHexString(bytes[1]);
470        int len1 = str1.length();
471        String norm1 = null;
472        if (len1 > 2) {
473            norm1 = str1.substring(len1-2, len1);
474        } else if (len1 == 1) {
475            norm1 = "0"+str1;
476        } else {
477            norm1 = str1;
478        }
479        
480        String wholeHexStr = norm0+norm1;
481        
482        int i = Integer.parseInt(wholeHexStr, 16);
483        
484        return i;
485    }
486    
487    /**
488     * Generates message handle used in an <code>Artifact</code>.
489     *
490     * @return          String format of 20-byte sequence identifying
491     *                  a message.
492     */
493    public static String generateMessageHandle() {
494        if (random == null) {
495            return null;
496        }
497        byte bytes[] = new byte[SAML2Constants.ID_LENGTH];
498        random.nextBytes(bytes);
499        return byteArrayToString(bytes);
500    }
501    
502    /**
503     * Converts String to Byte Array.
504     *
505     * @param input     String to be converted.
506     * @return          result of the conversion.
507     */
508    public static byte[] stringToByteArray(String input) {
509        char chars[] = input.toCharArray();
510        byte bytes[] = new byte[chars.length];
511        for (int i = 0; i < chars.length; i++) {
512            bytes[i] = (byte) chars[i];
513        }
514        return bytes;
515    }
516    
517    /**
518     * Converts byte array to <code>Hex</code> String.
519     *
520     * @param byteArray Byte Array to be converted.
521     * @return result of the conversion.
522     */
523    public static String byteArrayToHexString(byte[] byteArray) {
524        int readBytes = byteArray.length;
525        StringBuffer hexData = new StringBuffer();
526        int onebyte;
527        for (int i=0; i < readBytes; i++) {
528            onebyte = ((0x000000ff & byteArray[i]) | 0xffffff00);
529            hexData.append(Integer.toHexString(onebyte).substring(6));
530        }
531        return hexData.toString();
532    }
533    
534    /**
535     * Converts <code>Hex</code> String to Byte Array.
536     *
537     * @param hexString <code>Hex</code> String to be converted.
538     * @return result of the conversion.
539     */
540    public static byte[] hexStringToByteArray(String hexString) {
541        int read = hexString.length();
542        byte[] byteArray = new byte[read/2];
543        for (int i=0, j=0; i < read; i++, j++) {
544            String part = hexString.substring(i,i+2);
545            byteArray[j] =
546            new Short(Integer.toString(Integer.parseInt(part,16))).
547            byteValue();
548            i++;
549        }
550        return byteArray;
551    }
552    
553    /**
554     * Generates ID.
555     * @return ID value.
556     */
557    public static String generateID() {
558        if (random == null) {
559            return null;
560        }
561        byte bytes[] = new byte[SAML2Constants.ID_LENGTH];
562        random.nextBytes(bytes);
563        return (SAML2ID_PREFIX + byteArrayToHexString(bytes));
564    }    
565
566    /**
567     * Gets the Discovery bootstrap resource offering in an attribute
568     * statement. After a single sign-on with an Identity Provider, a service
569     * provider may get Discovery service esource Offerings through a SAML2
570     * assertion. This APIs helps in retrieving the resource offerings
571     * if the user has been authenticated through the SAML2 SSO. It will
572     * need to have a valid single sign on token (generated through the
573     * SAML2 SSO).
574     *
575     * @param request <code>HttpServletRequest</code> associated with a user
576     *        session.
577     * @return <code>ResourceOffering</code> Discovery Resource Offering,
578     *         null if there is any failure  or if there is not one
579     */
580    public static ResourceOffering getDiscoveryBootStrapResourceOffering(
581        HttpServletRequest request) {
582
583        if (request == null) {
584            if (debug.messageEnabled()) {
585                debug.message("SAML2Utils.getDiscoveryBootStrapResource" +
586                    "Offerings: null Input params");
587            }
588            return null;
589        }
590        try {
591            SessionProvider sessionProvider = SessionManager.getProvider();
592            Object session = sessionProvider.getSession(request);
593
594            String[] roStr = sessionProvider.getProperty(session,
595                SAML2Constants.DISCOVERY_BOOTSTRAP_ATTRIBUTE_NAME);
596            if ((roStr == null) || (roStr.length == 0)) {
597                return null;
598            }
599
600            return new ResourceOffering(
601                XMLUtils.toDOMDocument(roStr[0], debug).getDocumentElement());
602
603        } catch(Exception ex) {
604            debug.error("SAML2Utils.getDiscoveryBootStrapResourceOfferings: " +
605                " Exception while retrieving discovery boot strap info.", ex);
606            return null;
607        }
608       
609    }
610    
611    /**
612     * Gets the Discovery bootstrap credentials.
613     * After a single sign-on with an Identity Provider, a service
614     * provider may get Discovery bootstrap resource offerings and credentials
615     * through a SAML assertion. This APIs helps in retrieving the credentials
616     * if the user has been authenticated through the SAML2 SSO. It will
617     * need to have a valid single sign on token (generated through the
618     * SAML2 SSO).
619     *
620     * @param request <code>HttpServletRequest</code> associated with a user
621     *     session.
622     * @return <code>List</code> of <code>SecurityAssertions</code>,
623     *     null if there is any failure  or if there is not one
624     */
625    public static List getDiscoveryBootStrapCredentials(
626        HttpServletRequest request) {
627  
628        if (request == null) {
629            if (debug.messageEnabled()) {
630                debug.message("SAML2Utils.getDiscoveryBootStrapCredentials: " +
631                    " null Input params");
632            }
633            return null;
634        }
635        try {
636            SessionProvider sessionProvider = SessionManager.getProvider();
637            Object session = sessionProvider.getSession(request);
638            String[] credentials = sessionProvider.getProperty(session,
639                SAML2Constants.DISCOVERY_BOOTSTRAP_CREDENTIALS);
640            if ((credentials == null) || (credentials.length == 0)) {
641                return null;
642            }
643
644            List securityAssertions = new ArrayList(); 
645            for(int i=0; i< credentials.length; i++) {
646                SecurityAssertion securityAssertion = new SecurityAssertion(
647                    XMLUtils.toDOMDocument(credentials[i], debug)
648                    .getDocumentElement());
649                securityAssertions.add(securityAssertion);
650            }
651            return securityAssertions;
652        } catch(Exception ex) {
653            debug.error("SAML2Utils.getDiscoveryBootStrapCredentials: ", ex);
654            return null;
655        }
656    }
657
658    /**
659     * Creates <code>SOAPMessage</code> with the input XML String
660     * as message body.
661     * @param xmlString XML string to be put into <code>SOAPMessage</code> body.
662     * @return newly created <code>SOAPMessage</code>.
663     * @exception SOAPException if it cannot create the
664     *            <code>SOAPMessage</code>.
665     */
666    public static String createSOAPMessageString(String xmlString)
667    throws SOAPException, SAML2Exception {
668            StringBuffer sb = new StringBuffer(500);
669            if (debug.messageEnabled()) {
670                debug.message("SAML2Utils.createSOAPMessage: xmlstr = " +
671                        xmlString);
672            }
673            sb.append("<").append(SAMLConstants.SOAP_ENV_PREFIX).
674                    append(":Envelope").append(SAMLConstants.SPACE).
675                    append("xmlns:").append(SAMLConstants.SOAP_ENV_PREFIX).
676                    append("=\"").append(SAMLConstants.SOAP_URI).append("\">").
677                    append("<").
678                    append(SAMLConstants.SOAP_ENV_PREFIX).append(":Body>").
679                    append(xmlString).
680                    append(SAMLConstants.START_END_ELEMENT).
681                    append(SAMLConstants.SOAP_ENV_PREFIX).
682                    append(":Body>").
683                    append(SAMLConstants.START_END_ELEMENT).
684                    append(SAMLConstants.SOAP_ENV_PREFIX).
685                    append(":Envelope>").append(SAMLConstants.NL);
686
687            if (debug.messageEnabled()) {
688                debug.message("SAML2Utils.createSOAPMessage: soap message = " +
689                        sb.toString());
690            }
691        return sb.toString();
692     }
693    
694    
695    /**
696     * Fills in basic auth user and password inside the location URL
697     * if configuration is done properly
698     * @param config Either an SPSSOConfigElement object , an
699     *               IDPSSOConfigElement object or PEPConfigElement.
700     * @param locationURL The original location URL which is to be
701     *                    inserted with user:password@ before the
702     *                    hostname part and after //
703     * @return The modified location URL with the basic auth user
704     *         and password if configured properly
705     */
706    public static String fillInBasicAuthInfo(
707            BaseConfigType config,
708            String locationURL) {
709
710        if (config == null) {
711            return locationURL;
712        }
713        Map map = SAML2MetaUtils.getAttributes(config);
714        List baoList = (List)map.get(
715                SAML2Constants.BASIC_AUTH_ON);
716        if (baoList == null || baoList.isEmpty()) {
717            return locationURL;
718        }
719        String on = (String)baoList.get(0);
720        if (on == null) {
721            return locationURL;
722        }
723        on = on.trim();
724        if (on.length() == 0 || !on.equalsIgnoreCase("true")) {
725            return locationURL;
726        }
727        List ul =  (List)map.get(
728                SAML2Constants.BASIC_AUTH_USER);
729  
730        if (ul == null || ul.isEmpty()) {
731            return locationURL;
732        }
733        String u = (String) ul.get(0);
734        if (u == null) {
735            return locationURL;
736        }
737        u = u.trim();
738        if (u.length() == 0) {
739            return locationURL;
740        }
741        List pl = (List)map.get(
742                SAML2Constants.BASIC_AUTH_PASSWD);
743        String p = null;
744        if (pl != null && !pl.isEmpty()) {
745            p = (String) pl.get(0);
746        }
747        if (p == null) {
748            p = "";
749        }
750
751        String dp = SAMLUtilsCommon.decodePassword(p);
752
753        int index = locationURL.indexOf("//");
754        return locationURL.substring(0, index+2) +
755                u + ":" + dp + "@" +
756                locationURL.substring(index+2);
757    }
758
759    /**
760     * Converts a value of XML boolean type to Boolean object.
761     *
762     * @param str a value of XML boolean type
763     * @return a Boolean object.
764     * @throws SAML2Exception if there is a syntax error
765     */
766    public static Boolean StringToBoolean(String str) throws SAML2Exception {
767        if (str == null) {
768            return null;
769        }
770        
771        if (str.equals("true") || str.equals("1")) {
772            return Boolean.TRUE;
773        }
774        
775        if (str.equals("false") || str.equals("0")) {
776            return Boolean.FALSE;
777        }
778        
779        throw new SAML2Exception(SAML2SDKUtils.bundle.getString(
780            "invalidXMLBooleanValue"));
781    }
782
783    /**
784     * Removes deployment URI from the pass down string. i.e.
785     * from "/opensso/ArtifactResolver/metaAlias/idp" to
786     * "/ArtifactResolver/metaAlias/idp".
787     * @param uri the URI string which the deployment uri is to be removed
788     * return string without deployment uri
789     */
790    public static String removeDeployUri(String uri) {
791        if ((uri == null) || (uri.length() == 0)) {
792            return uri;
793        }
794        int loc = uri.indexOf("/", 1);
795        if (loc == -1) {
796            return null;
797        } else {
798            return uri.substring(loc);
799        }
800    }
801
802    /**
803     * Returns the boolean value as a <code>Boolean</code> object.
804     *
805     * @param value boolean value true or false.
806     *
807     */
808    public static Boolean booleanValueOf(String value) {
809        return new Boolean("true".equalsIgnoreCase(value) || "1".equals(value));
810    }
811
812    /**
813     * If enabled, decodes the provided XML element and prints it out to the decryption debug log.
814     * @param callerName String representing the name of the calling method.
815     * @param xmlElement String representing an XML document with decrypted
816     *                  data.
817     */
818    public static void decodeXMLToDebugLog(String callerName, Element xmlElement) {
819        if (decryptDebug.messageEnabled() && isSAMLDecryptionDebugEnabled()) {
820            String xmlOutput = XMLUtils.print(xmlElement);
821            decryptDebug.message(callerName + "Decrypted xml element node:\n" +
822                ((xmlOutput != null) ? xmlOutput : "NULL"));
823        }
824    }
825
826    /**
827     * Tells whether SAML SP decryption debug mode is enabled.
828     *
829     * @return <code>true</code> if SAML decryption debug mode is enabled, or <code>false</code> otherwise or if the
830     *         property is not found.
831     */
832    public static boolean isSAMLDecryptionDebugEnabled() {
833        return SystemPropertiesManager.getAsBoolean(SAML2Constants.SAML_DECRYPTION_DEBUG_MODE);
834    }
835}