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