001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2008 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: AssertionIDRequestUtil.java,v 1.8 2009/06/12 22:21:40 mallas Exp $
026 *
027 * Portions Copyrighted 2013-2016 ForgeRock AS.
028 */
029package com.sun.identity.saml2.profile;
030
031import static org.forgerock.openam.utils.Time.*;
032
033import java.io.BufferedInputStream;
034import java.io.BufferedOutputStream;
035import java.io.IOException;
036import java.io.UnsupportedEncodingException;
037import java.net.HttpURLConnection;
038import java.net.MalformedURLException;
039import java.net.URL;
040import java.security.PrivateKey;
041import java.security.cert.X509Certificate;
042import java.util.ArrayList;
043import java.util.Date;
044import java.util.Hashtable;
045import java.util.Iterator;
046import java.util.List;
047import java.util.Set;
048import javax.servlet.http.HttpServletRequest;
049import javax.servlet.http.HttpServletResponse;
050import javax.xml.soap.SOAPException;
051import javax.xml.soap.SOAPMessage;
052
053import com.sun.identity.saml2.common.SAML2FailoverUtils;
054import com.sun.identity.saml2.common.SOAPCommunicator;
055import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException;
056import com.sun.identity.common.HttpURLConnectionManager;
057import org.w3c.dom.Element;
058
059import com.sun.identity.saml.xmlsig.KeyProvider;
060import com.sun.identity.saml.common.SAMLUtils;
061import com.sun.identity.saml2.assertion.Assertion;
062import com.sun.identity.saml2.assertion.AssertionFactory;
063import com.sun.identity.saml2.assertion.AssertionIDRef;
064import com.sun.identity.saml2.assertion.Issuer;
065import com.sun.identity.saml2.common.SAML2Constants;
066import com.sun.identity.saml2.common.SAML2Exception;
067import com.sun.identity.saml2.common.SAML2Utils;
068import com.sun.identity.saml2.jaxb.entityconfig.BaseConfigType;
069import com.sun.identity.saml2.jaxb.metadata.AttributeAuthorityDescriptorElement;
070import com.sun.identity.saml2.jaxb.metadata.AssertionIDRequestServiceElement;
071import com.sun.identity.saml2.jaxb.metadata.AuthnAuthorityDescriptorElement;
072import com.sun.identity.saml2.jaxb.metadata.IDPSSODescriptorElement;
073import com.sun.identity.saml2.jaxb.metadata.RoleDescriptorType;
074import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement;
075import com.sun.identity.saml2.key.KeyUtil;
076import com.sun.identity.saml2.meta.SAML2MetaException;
077import com.sun.identity.saml2.meta.SAML2MetaManager;
078import com.sun.identity.saml2.plugins.AssertionIDRequestMapper;
079import com.sun.identity.saml2.protocol.AssertionIDRequest;
080import com.sun.identity.saml2.protocol.ProtocolFactory;
081import com.sun.identity.saml2.protocol.Response;
082import com.sun.identity.saml2.protocol.Status;
083import com.sun.identity.saml2.protocol.StatusCode;
084
085/**
086 * This class provides methods to send or process
087 * <code>AssertionIDRequest</code>.
088 *
089 * @supported.api
090 */
091public class AssertionIDRequestUtil {
092
093    static KeyProvider keyProvider = KeyUtil.getKeyProviderInstance(); 
094    static SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager();
095    static Hashtable assertionIDRequestMapperCache = new Hashtable(); 
096    static final String MIME_TYPE_ASSERTION = "application/samlassertion+xml";
097
098    private AssertionIDRequestUtil() {
099    }
100
101    /**
102     * Sends the <code>AssertionIDRequest</code> to specifiied Assertion ID
103     * Request Service and returns <code>Response</code> coming from the
104     * Assertion ID Request Service.
105     *
106     * @param assertionIDRequest the <code>AssertionIDRequest</code> object
107     * @param samlAuthorityEntityID entity ID of SAML authority
108     * @param role SAML authority role, for example,
109     * <code>SAML2Constants.ATTR_AUTH_ROLE</code>, 
110     * <code>SAML2Constants.AUTHN_AUTH_ROLE</code> or
111     * <code>SAML2Constants.IDP_ROLE</code>
112     * @param realm the realm of hosted entity
113     * @param binding the binding
114     *
115     * @return the <code>Response</code> object
116     * @exception SAML2Exception if the operation is not successful
117     *
118     * @supported.api
119     */
120    public static Response sendAssertionIDRequest(
121        AssertionIDRequest assertionIDRequest, String samlAuthorityEntityID,
122        String role, String realm, String binding) throws SAML2Exception {
123
124        StringBuffer location = new StringBuffer();
125        RoleDescriptorType roled = getRoleDescriptorAndLocation(
126            samlAuthorityEntityID, role, realm, binding, location);
127
128        if (binding.equalsIgnoreCase(SAML2Constants.SOAP)) {
129            signAssertionIDRequest(assertionIDRequest, realm, false);
130            return sendAssertionIDRequestBySOAP(assertionIDRequest,
131                location.toString(), realm, samlAuthorityEntityID, role, roled);
132        } else {
133            throw new SAML2Exception(
134                SAML2Utils.bundle.getString("unsupportedBinding"));
135        }
136    }
137
138    /**
139     * Sends the Assertion ID to specifiied Assertion ID Request Service and
140     * returns <code>Assertion</code> coming from the Assertion ID Request
141     * Service.
142     *
143     * @param assertionID the asssertionID</code> object
144     * @param samlAuthorityEntityID entity ID of SAML authority
145     * @param role SAML authority role, for example,
146     * <code>SAML2Constants.ATTR_AUTH_ROLE</code>, 
147     * <code>SAML2Constants.AUTHN_AUTH_ROLE</code> or
148     * <code>SAML2Constants.IDP_ROLE</code>
149     * @param realm the realm of hosted entity
150     *
151     * @return the <code>Assertion</code> object
152     * @exception SAML2Exception if the operation is not successful
153     *
154     * @supported.api
155     */
156    public static Assertion sendAssertionIDRequestURI(
157        String assertionID, String samlAuthorityEntityID,
158        String role, String realm) throws SAML2Exception {
159
160        StringBuffer locationSB = new StringBuffer();
161        getRoleDescriptorAndLocation(samlAuthorityEntityID, role, realm,
162            SAML2Constants.URI, locationSB);
163        if (locationSB.indexOf("?") == -1) {
164            locationSB.append("?");
165        } else {
166            locationSB.append("&");
167        }
168        locationSB.append("ID=").append(assertionID);
169        String location = fillInBasicAuthInfo(locationSB.toString(), realm,
170            samlAuthorityEntityID, role); 
171
172        URL url = null;
173        try {
174            url = new URL(location);
175        } catch (MalformedURLException me) {
176            throw new SAML2Exception(me.getMessage());
177        }
178
179        try {
180            HttpURLConnection conn = HttpURLConnectionManager.getConnection(url);
181            conn.setInstanceFollowRedirects(false);
182            conn.setUseCaches(false);
183            conn.setDoOutput(false);
184            conn.connect();
185
186            int respCode = conn.getResponseCode();
187            if (SAML2Utils.debug.messageEnabled()) {
188                SAML2Utils.debug.message(
189                    "AssertionIDRequestUtil.sendAssertionIDRequestURI: " +
190                    "Response code = " + respCode + ", Response message = " +
191                    conn.getResponseMessage());
192            }
193            if (respCode != HttpURLConnection.HTTP_OK) {
194                return null;
195            }
196
197            String contentType = conn.getContentType();
198            if (SAML2Utils.debug.messageEnabled()) {
199                SAML2Utils.debug.message(
200                    "AssertionIDRequestUtil.sendAssertionIDRequestURI: " +
201                    "Content type = " + contentType);
202            }
203            if ((contentType == null) ||
204                (contentType.indexOf(MIME_TYPE_ASSERTION) == -1)) {
205
206                return null;
207            }
208
209            int contentLength = conn.getContentLength();
210            if (SAML2Utils.debug.messageEnabled()) {
211                SAML2Utils.debug.message(
212                    "AssertionIDRequestUtil.sendAssertionIDRequestURI: " +
213                    "Content length = " + contentLength);
214            }
215
216            BufferedInputStream bin =
217                new BufferedInputStream(conn.getInputStream());
218            StringBuffer contentSB = new StringBuffer();
219            byte content[] = new byte[2048];
220
221            if (contentLength != -1) {
222                int read = 0, totalRead = 0;
223                int left;
224                while (totalRead < contentLength) {
225                    left = contentLength - totalRead;
226                    read = bin.read(content, 0,
227                        left < content.length ? left : content.length);
228                    if (read == -1) {
229                        // We need to close connection !!
230                        break;
231                    } else {
232                        if (read > 0) {
233                            totalRead += read;
234                            contentSB.append(new String(content, 0, read));
235                        }
236                    }
237                }
238            } else {
239                int numbytes;
240                int totalRead = 0;
241
242                while (true) {
243                    numbytes = bin.read(content);
244                    if (numbytes == -1) {
245                        break;
246                    }
247
248                    totalRead += numbytes;
249                    contentSB.append(new String(content, 0, numbytes));
250                }
251            }
252
253            return AssertionFactory.getInstance().createAssertion(
254                contentSB.toString());
255        } catch (IOException ioex) {
256            SAML2Utils.debug.error(
257                "AssertionIDRequest.sendAssertionIDRequestURI:", ioex);
258            throw new SAML2Exception(ioex.getMessage());
259        }
260    }
261
262    /**
263     * Gets assertion ID from URI and returns assertion if found.
264     *
265     * @param request the <code>HttpServletRequest</code> object
266     * @param response the <code>HttpServletResponse</code> object
267     * @param samlAuthorityEntityID entity ID of SAML authority
268     * @param role SAML authority role
269     * @param realm the realm of hosted entity
270     *
271     * @exception IOException if response can't be sent
272     */
273    public static void processAssertionIDRequestURI(HttpServletRequest request,
274        HttpServletResponse response, String samlAuthorityEntityID,
275        String role, String realm) throws IOException {
276
277        String assertionID = request.getParameter("ID");
278        if (assertionID == null) {
279            SAMLUtils.sendError(request, response, 
280                HttpServletResponse.SC_BAD_REQUEST, "nullAssertionID",
281                SAML2Utils.bundle.getString("nullAssertionID"));
282            return;
283        }
284
285        AssertionIDRequestMapper aidReqMapper = null;
286        try {
287            aidReqMapper = getAssertionIDRequestMapper(realm,
288                samlAuthorityEntityID, role);
289        } catch (SAML2Exception ex) {
290            SAMLUtils.sendError(request, response, 
291                HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
292                "failedToGetAssertionIDRequestMapper", ex.getMessage());
293            return;
294        }
295
296        try {
297            aidReqMapper.authenticateRequesterURI(request, response,
298                samlAuthorityEntityID, role, realm);
299        } catch (SAML2Exception ex) {
300            SAMLUtils.sendError(request, response, 
301                HttpServletResponse.SC_FORBIDDEN,
302                "failedToAuthenticateRequesterURI", ex.getMessage());
303            return;
304        }
305
306        Assertion assertion = (Assertion)IDPCache.assertionByIDCache.get(
307            assertionID);
308
309        if ((assertion == null) || (!assertion.isTimeValid())) {
310            SAMLUtils.sendError(request, response, 
311                HttpServletResponse.SC_NOT_FOUND,
312                "invalidAssertionID",
313                SAML2Utils.bundle.getString("invalidAssertionID"));
314            return;
315        }
316
317        response.setContentType(MIME_TYPE_ASSERTION);
318        response.addHeader("Cache-Control", "no-cache, no-store");
319        response.addHeader("Pragma", "no-cache");
320
321        String content = null;
322        try {
323            content = assertion.toXMLString(true, true);
324        } catch (SAML2Exception ex) {
325            if (SAML2Utils.debug.messageEnabled()) {
326                SAML2Utils.debug.message("AssertionIDRequestUtil." +
327                "processAssertionIDRequestURI:", ex);
328            }
329            SAMLUtils.sendError(request, response, 
330                HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
331                "invalidAssertion", ex.getMessage());
332            return;
333        }
334
335        byte[] bytes = null;
336        try {
337            bytes = content.getBytes("UTF-8");
338        } catch(UnsupportedEncodingException ueex) {
339            if (SAML2Utils.debug.messageEnabled()) {
340                SAML2Utils.debug.message("AssertionIDRequestUtil." +
341                "processAssertionIDRequestURI:", ueex);
342            }
343            SAMLUtils.sendError(request, response, 
344                HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
345                "unsupportedEncoding", ueex.getMessage());
346            return;
347        }
348        response.setContentLength(bytes.length);
349
350        BufferedOutputStream bos = null;
351        try {
352            bos = new BufferedOutputStream(response.getOutputStream());
353            bos.write(bytes, 0, bytes.length);
354        } catch (IOException ioex) {
355            SAML2Utils.debug.error("AssertionIDRequestUtil." +
356                "processAssertionIDRequestURI:", ioex);
357        } finally {
358            if (bos != null) {
359                try {
360                    bos.close();
361                } catch (IOException ioex) {
362                    SAML2Utils.debug.error("AssertionIDRequestUtil." +
363                        "processAssertionIDRequestURI:", ioex);
364                }
365            }
366        }
367    }
368
369    /**
370     * This method processes the <code>AssertionIDRequest</code> coming
371     * from a requester.
372     *
373     * @param assertionIDRequest the <code>AssertionIDRequest</code> object
374     * @param request the <code>HttpServletRequest</code> object
375     * @param response the <code>HttpServletResponse</code> object
376     * @param samlAuthorityEntityID entity ID of SAML authority
377     * @param role the role of SAML authority
378     * @param realm the realm of SAML authority
379     * @return the <code>Response</code> object
380     * @exception SAML2Exception if the operation is not successful
381     */
382    public static Response processAssertionIDRequest(
383        AssertionIDRequest assertionIDRequest, HttpServletRequest request, 
384        HttpServletResponse response, String samlAuthorityEntityID,
385        String role, String realm) throws SAML2Exception {
386
387        try {
388            verifyAssertionIDRequest(assertionIDRequest, samlAuthorityEntityID,
389                role, realm);
390        } catch(SAML2Exception se) {
391            SAML2Utils.debug.error("AssertionIDRequestUtil." +
392                "processAssertionIDRequest:", se);
393            return SAML2Utils.getErrorResponse(assertionIDRequest,
394                SAML2Constants.REQUESTER, null, se.getMessage(),
395                samlAuthorityEntityID);
396        }
397
398        Issuer issuer = assertionIDRequest.getIssuer();
399        String spEntityID = issuer.getValue();        
400
401        RoleDescriptorType roled = null;
402        try {
403            if (SAML2Constants.IDP_ROLE.equals(role)) {
404                roled = metaManager.getIDPSSODescriptor(realm,
405                    samlAuthorityEntityID);
406            } else if (SAML2Constants.AUTHN_AUTH_ROLE.equals(role)) {
407                roled = metaManager.getAuthnAuthorityDescriptor(realm,
408                    samlAuthorityEntityID);
409            } else if (SAML2Constants.ATTR_AUTH_ROLE.equals(role)) {
410                roled = metaManager.getAttributeAuthorityDescriptor(realm,
411                    samlAuthorityEntityID);
412            }
413        } catch (SAML2MetaException sme) {
414            SAML2Utils.debug.error("AssertionIDRequestUtil." +
415                "processAssertionIDRequest:", sme);
416            return SAML2Utils.getErrorResponse(assertionIDRequest,
417                SAML2Constants.RESPONDER, null, sme.getMessage(),
418                samlAuthorityEntityID);
419        }
420
421        if (roled == null) {
422            return SAML2Utils.getErrorResponse(assertionIDRequest,
423                SAML2Constants.REQUESTER, null, SAML2Utils.bundle.getString(
424                "samlAuthorityNotFound"), samlAuthorityEntityID);
425        }
426
427        List returnAssertions = null;
428        List assertionIDRefs = assertionIDRequest.getAssertionIDRefs();
429        for(Iterator iter = assertionIDRefs.iterator(); iter.hasNext();) {
430            AssertionIDRef assertionIDRef = (AssertionIDRef)iter.next();
431            String assertionID = assertionIDRef.getValue();
432
433            Assertion assertion = (Assertion)IDPCache.assertionByIDCache.get(
434                assertionID);
435            if ((assertion == null) && (SAML2FailoverUtils.isSAML2FailoverEnabled())) {
436                if (SAML2Utils.debug.messageEnabled()) {
437                    SAML2Utils.debug.message("AssertionIDRequestUtil.processAssertionIDRequest: " +
438                        "reading assertion from the SAML2 Token Repository using assertionID:" + assertionID);
439                }
440                String assertionStr = null;
441                try {
442                    assertionStr = (String) SAML2FailoverUtils.retrieveSAML2Token(assertionID);
443                } catch (SAML2TokenRepositoryException se) {
444                    SAML2Utils.debug.error("AssertionIDRequestUtil.processAssertionIDRequest: " +
445                         "There was a problem reading assertion from the SAML2 Token Repository using assertionID:"
446                            + assertionID, se);
447                }
448                if (assertionStr != null) {
449                    assertion = AssertionFactory.getInstance().createAssertion(
450                        assertionStr);
451                }
452            }
453
454            if ((assertion != null) && (assertion.isTimeValid())) {
455                if (returnAssertions == null) {
456                    returnAssertions = new ArrayList();
457                }
458                returnAssertions.add(assertion);
459            }
460        }        
461
462        ProtocolFactory protocolFactory = ProtocolFactory.getInstance();
463        Response samlResp = protocolFactory.createResponse();
464        samlResp.setAssertion(returnAssertions);
465
466        samlResp.setID(SAML2Utils.generateID());
467        samlResp.setInResponseTo(assertionIDRequest.getID());
468
469        samlResp.setVersion(SAML2Constants.VERSION_2_0);
470        samlResp.setIssueInstant(newDate());
471    
472        Status status = protocolFactory.createStatus();
473        StatusCode statusCode = protocolFactory.createStatusCode();
474        statusCode.setValue(SAML2Constants.SUCCESS);
475        status.setStatusCode(statusCode);
476        samlResp.setStatus(status);
477
478        Issuer respIssuer = AssertionFactory.getInstance().createIssuer();
479        respIssuer.setValue(samlAuthorityEntityID);
480        samlResp.setIssuer(respIssuer);
481
482        signResponse(samlResp, samlAuthorityEntityID, role, realm, false);
483
484        return samlResp;
485    }
486
487    private static RoleDescriptorType getRoleDescriptorAndLocation(
488        String samlAuthorityEntityID, String role, String realm,
489        String binding, StringBuffer location) throws SAML2Exception {
490
491        List aIDReqServices = null;
492        RoleDescriptorType roled = null;
493        try {
494            if (role == null) {
495                throw new SAML2Exception(SAML2Utils.bundle.getString(
496                    "unsupportedRole"));
497            } else if (role.equals(SAML2Constants.IDP_ROLE)) {
498                IDPSSODescriptorElement idpd =
499                    metaManager.getIDPSSODescriptor(realm,
500                    samlAuthorityEntityID);
501                if (idpd == null) {
502                    throw new SAML2Exception(SAML2Utils.bundle.getString(
503                        "idpNotFound"));
504                }
505                aIDReqServices = idpd.getAssertionIDRequestService();
506                roled = idpd;
507            } else if (role.equals(SAML2Constants.AUTHN_AUTH_ROLE)) {
508                AuthnAuthorityDescriptorElement attrd =
509                    metaManager.getAuthnAuthorityDescriptor(realm,
510                    samlAuthorityEntityID);
511                if (attrd == null) {
512                    throw new SAML2Exception(SAML2Utils.bundle.getString(
513                        "authnAuthorityNotFound"));
514                }
515                aIDReqServices = attrd.getAssertionIDRequestService();
516                roled = attrd;
517            } else if (role.equals(SAML2Constants.ATTR_AUTH_ROLE)) {
518                AttributeAuthorityDescriptorElement aad =
519                    metaManager.getAttributeAuthorityDescriptor(realm,
520                    samlAuthorityEntityID);
521                if (aad == null) {
522                    throw new SAML2Exception(SAML2Utils.bundle.getString(
523                        "attrAuthorityNotFound"));
524                }
525                aIDReqServices = aad.getAssertionIDRequestService();
526                roled = aad;
527            } else {
528                throw new SAML2Exception(SAML2Utils.bundle.getString(
529                    "unsupportedRole"));
530            }
531        } catch (SAML2MetaException sme) {
532            SAML2Utils.debug.error(
533                "AssertionIDRequest.getRoleDescriptorAndLocation:", sme);
534            throw new SAML2Exception(SAML2Utils.bundle.getString(
535                "metaDataError"));
536        }
537
538        if (binding == null) {
539            throw new SAML2Exception(
540                SAML2Utils.bundle.getString("unsupportedBinding"));
541        }
542
543        if ((aIDReqServices == null) || (aIDReqServices.isEmpty())) {
544            throw new SAML2Exception(
545                SAML2Utils.bundle.getString("aIDReqServiceNotFound"));
546        }
547
548        for(Iterator iter = aIDReqServices.iterator(); iter.hasNext(); ) {
549            AssertionIDRequestServiceElement aIDReqService =
550                (AssertionIDRequestServiceElement)iter.next();
551            if (binding.equalsIgnoreCase(aIDReqService.getBinding())) {
552                location.append(aIDReqService.getLocation());
553                break;
554            }
555        }
556        if (location.length() == 0) {
557            throw new SAML2Exception(
558                SAML2Utils.bundle.getString("unsupportedBinding"));
559        }
560
561        return roled;
562    }
563
564    private static void signAssertionIDRequest(
565        AssertionIDRequest assertionIDRequest,
566        String realm, boolean includeCert) throws SAML2Exception {
567
568        String spEntityID = assertionIDRequest.getIssuer().getValue();
569        
570        String alias = SAML2Utils.getSigningCertAlias(realm, spEntityID,
571            SAML2Constants.SP_ROLE);
572
573        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
574        X509Certificate signingCert = null;
575        if (includeCert) {
576            signingCert = keyProvider.getX509Certificate(alias);
577        }
578        
579        if (signingKey != null) {
580            assertionIDRequest.sign(signingKey, signingCert);
581        }
582    }
583
584    private static void verifyAssertionIDRequest(
585        AssertionIDRequest assertionIDRequest, String samlAuthorityEntityID,
586        String role, String realm) throws SAML2Exception {
587
588        Issuer issuer = assertionIDRequest.getIssuer();
589        String requestedEntityID = issuer.getValue();
590
591        if (!SAML2Utils.isSourceSiteValid(issuer, realm,
592            samlAuthorityEntityID)) {
593
594            throw new SAML2Exception(SAML2Utils.bundle.getString(
595                "assertionIDRequestIssuerInvalid"));
596        }
597
598        SPSSODescriptorElement spSSODesc = metaManager.getSPSSODescriptor(
599            realm, requestedEntityID);
600        if (spSSODesc == null) {
601            throw new SAML2Exception(SAML2Utils.bundle.getString(
602                "assertionIDRequestIssuerNotFound"));
603        }
604
605        Set<X509Certificate> verificationCerts = KeyUtil.getVerificationCerts(spSSODesc, requestedEntityID,
606                SAML2Constants.SP_ROLE);
607
608        if (!verificationCerts.isEmpty()) {
609            boolean valid = assertionIDRequest.isSignatureValid(verificationCerts);
610            if (SAML2Utils.debug.messageEnabled()) {
611                SAML2Utils.debug.message(
612                    "AssertionIDRequestUtil.verifyAssertionIDRequest: " +
613                    "Signature validity is : " + valid);
614            }
615            if (!valid) {
616                throw new SAML2Exception(SAML2Utils.bundle.getString(
617                    "invalidSignatureAssertionIDRequest"));
618            }
619        } else {
620            throw new SAML2Exception(
621                    SAML2Utils.bundle.getString("missingSigningCertAlias"));
622        }
623    }
624
625    private static void signResponse(Response response,
626        String samlAuthorityEntityID, String role, String realm,
627        boolean includeCert) throws SAML2Exception {
628        
629        String alias = SAML2Utils.getSigningCertAlias(realm, samlAuthorityEntityID, role);
630
631        String encryptedKeyPass = SAML2Utils.getSigningCertEncryptedKeyPass(realm, samlAuthorityEntityID, role);
632        PrivateKey signingKey;
633        if (encryptedKeyPass == null  || encryptedKeyPass.isEmpty()) {
634            signingKey = keyProvider.getPrivateKey(alias);
635        } else {
636            signingKey = keyProvider.getPrivateKey(alias, encryptedKeyPass);
637        }
638        X509Certificate signingCert = null;
639        if (includeCert) {
640            signingCert = keyProvider.getX509Certificate(alias);
641        }
642        
643        if (signingKey != null) {
644            response.sign(signingKey, signingCert);
645        }
646    }
647
648    private static String fillInBasicAuthInfo(String location, String realm,
649        String samlAuthorityEntityID, String role) {
650
651        BaseConfigType config = null;
652        try {
653            if (role.equals(SAML2Constants.IDP_ROLE)) {
654                config = metaManager.getIDPSSOConfig(realm,
655                    samlAuthorityEntityID);
656            } else if (role.equals(SAML2Constants.AUTHN_AUTH_ROLE)) {
657                config = metaManager.getAuthnAuthorityConfig(realm,
658                    samlAuthorityEntityID);
659            } else if (role.equals(SAML2Constants.ATTR_AUTH_ROLE)) {
660                config = metaManager.getAttributeAuthorityConfig(realm,
661                    samlAuthorityEntityID);
662            }
663        } catch (SAML2MetaException sme) {
664            if (SAML2Utils.debug.messageEnabled()) {
665                SAML2Utils.debug.message(
666                    "AssertionIDRequestUtil.getSSOConfig:", sme);
667            }
668        }
669
670        return SAML2Utils.fillInBasicAuthInfo(config, location); 
671    }
672
673    private static Response sendAssertionIDRequestBySOAP(
674        AssertionIDRequest assertionIDRequest, String location, String realm,
675        String samlAuthorityEntityID, String role, RoleDescriptorType roled)
676        throws SAML2Exception {
677
678        String aIDReqStr = assertionIDRequest.toXMLString(true, true);
679        if (SAML2Utils.debug.messageEnabled()) {
680            SAML2Utils.debug.message(
681                "AssertionIDRequestUtil.sendAssertionIDRequestBySOAP: " +
682                "assertionIDRequest = " + aIDReqStr);
683            SAML2Utils.debug.message(
684                "AssertionIDRequestUtil.sendAssertionIDRequestBySOAP: " +
685                "location = " + location);
686        }
687
688        location = fillInBasicAuthInfo(location, realm, samlAuthorityEntityID,
689            role); 
690
691        SOAPMessage resMsg = null;
692        try {
693            resMsg = SOAPCommunicator.getInstance().sendSOAPMessage(aIDReqStr, location, true);
694        } catch (SOAPException se) {
695            SAML2Utils.debug.error(
696                "AssertionIDRequestUtil.sendAssertionIDRequestBySOAP:", se);
697            throw new SAML2Exception(
698                SAML2Utils.bundle.getString("errorSendingAssertionIDRequest"));
699        }
700        
701        Element respElem = SOAPCommunicator.getInstance().getSamlpElement(resMsg, "Response");
702        Response response =
703            ProtocolFactory.getInstance().createResponse(respElem);
704        
705        if (SAML2Utils.debug.messageEnabled()) {
706            SAML2Utils.debug.message(
707                "AssertionIDRequestUtil.sendAssertionIDRequestBySOAP: " +
708                "response = " + response.toXMLString(true, true));
709        }
710
711        verifyResponse(response, assertionIDRequest, samlAuthorityEntityID,
712            role, roled);
713
714        return response;
715    }
716
717    private static void verifyResponse(Response response,
718        AssertionIDRequest assertionIDRequest, String samlAuthorityEntityID,
719        String role, RoleDescriptorType roled) throws SAML2Exception {
720
721        String aIDReqID = assertionIDRequest.getID();
722        if ((aIDReqID != null) &&
723            (!aIDReqID.equals(response.getInResponseTo()))) {
724
725            throw new SAML2Exception(SAML2Utils.bundle.getString(
726                "invalidInResponseToAssertionIDRequest"));
727        }
728
729        Issuer respIssuer = response.getIssuer();
730        if (respIssuer == null) {
731            return;
732        }
733
734        if (!samlAuthorityEntityID.equals(respIssuer.getValue())) {
735            throw new SAML2Exception(SAML2Utils.bundle.getString(
736                "responseIssuerMismatch"));
737        }
738
739
740        Set<X509Certificate> signingCerts = KeyUtil.getVerificationCerts(roled, samlAuthorityEntityID, role);
741
742        if (!signingCerts.isEmpty()) {
743            boolean valid = response.isSignatureValid(signingCerts);
744            if (SAML2Utils.debug.messageEnabled()) {
745                SAML2Utils.debug.message(
746                    "AssertionIDRequestUtil .verifyResponse: " +
747                    "Signature validity is : " + valid);
748            }
749            if (!valid) {
750                throw new SAML2Exception(SAML2Utils.bundle.getString(
751                    "invalidSignatureOnResponse"));
752            }
753        } else {
754            throw new SAML2Exception(SAML2Utils.bundle.getString("missingSigningCertAlias"));
755        }
756
757    }
758
759    private static AssertionIDRequestMapper getAssertionIDRequestMapper(
760        String realm, String samlAuthorityEntityID, String role)
761        throws SAML2Exception {
762
763        String aidReqMapperName = null;
764        AssertionIDRequestMapper aidReqMapper = null;
765        try {
766            aidReqMapperName = SAML2Utils.getAttributeValueFromSSOConfig(realm,
767                samlAuthorityEntityID, role,
768                SAML2Constants.ASSERTION_ID_REQUEST_MAPPER);
769
770            if (aidReqMapperName == null) {
771                aidReqMapperName = 
772                    SAML2Constants.DEFAULT_ASSERTION_ID_REQUEST_MAPPER_CLASS;
773                if (SAML2Utils.debug.messageEnabled()) {
774                    SAML2Utils.debug.message(
775                        "AssertionIDRequestUtil.getAssertionIDRequestMapper:" +
776                        " use "+ aidReqMapperName);
777                }
778            }
779            aidReqMapper = (AssertionIDRequestMapper)
780                assertionIDRequestMapperCache.get(aidReqMapperName);
781            if (aidReqMapper == null) {
782                aidReqMapper = (AssertionIDRequestMapper)
783                    Class.forName(aidReqMapperName).newInstance();
784                assertionIDRequestMapperCache.put(aidReqMapperName,
785                    aidReqMapper);
786            } else {
787                if (SAML2Utils.debug.messageEnabled()) {
788                    SAML2Utils.debug.message(
789                        "AssertionIDRequestUtil.getAssertionIDRequestMapper:" +
790                        " got the AssertionIDRequestMapper from cache");
791                }
792            }
793        } catch (Exception ex) {
794            SAML2Utils.debug.error(
795                "AssertionIDRequestUtil.getAssertionIDRequestMapper:", ex);
796            throw new SAML2Exception(ex);
797        }
798
799        return aidReqMapper;
800    }
801
802}