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: AuthnQueryUtil.java,v 1.8 2008/12/03 00:32:31 hengming Exp $
026 *
027 *
028 *  Portions Copyrighted 2010-2014 ForgeRock AS.
029 */
030
031package com.sun.identity.saml2.profile;
032
033import java.util.ArrayList;
034import java.util.Date;
035import java.util.Iterator;
036import java.util.List;
037import java.security.PrivateKey;
038import java.security.cert.X509Certificate;
039import javax.servlet.http.HttpServletRequest;
040import javax.servlet.http.HttpServletResponse;
041import javax.xml.soap.SOAPException;
042import javax.xml.soap.SOAPMessage;
043
044import com.sun.identity.saml2.common.SAML2FailoverUtils;
045import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException;
046import org.w3c.dom.Element;
047
048import com.sun.identity.saml.xmlsig.KeyProvider;
049import com.sun.identity.saml2.assertion.Assertion;
050import com.sun.identity.saml2.assertion.AssertionFactory;
051import com.sun.identity.saml2.assertion.AuthnContext;
052import com.sun.identity.saml2.assertion.AuthnStatement;
053import com.sun.identity.saml2.assertion.EncryptedAssertion;
054import com.sun.identity.saml2.assertion.EncryptedID;
055import com.sun.identity.saml2.assertion.Issuer;
056import com.sun.identity.saml2.assertion.NameID;
057import com.sun.identity.saml2.assertion.Subject;
058import com.sun.identity.saml2.common.SAML2Constants;
059import com.sun.identity.saml2.common.SAML2Exception;
060import com.sun.identity.saml2.common.SAML2Utils;
061import com.sun.identity.saml2.jaxb.entityconfig.AuthnAuthorityConfigElement;
062import com.sun.identity.saml2.jaxb.metadata.AuthnAuthorityDescriptorElement;
063import com.sun.identity.saml2.jaxb.metadata.AuthnQueryServiceElement;
064import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement;
065import com.sun.identity.saml2.key.KeyUtil;
066import com.sun.identity.saml2.meta.SAML2MetaException;
067import com.sun.identity.saml2.meta.SAML2MetaManager;
068import com.sun.identity.saml2.plugins.IDPAccountMapper;
069import com.sun.identity.saml2.plugins.IDPAuthnContextMapper;
070import com.sun.identity.saml2.protocol.AuthnQuery;
071import com.sun.identity.saml2.protocol.ProtocolFactory;
072import com.sun.identity.saml2.protocol.RequestedAuthnContext;
073import com.sun.identity.saml2.protocol.Response;
074import com.sun.identity.saml2.protocol.Status;
075import com.sun.identity.saml2.protocol.StatusCode;
076
077
078
079/**
080 * This class provides methods to send or process <code>AuthnQuery</code>.
081 *
082 * @supported.api
083 */
084
085public class AuthnQueryUtil {
086
087    static KeyProvider keyProvider = KeyUtil.getKeyProviderInstance(); 
088    static SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager();
089
090    private AuthnQueryUtil() {
091    }
092
093    /**
094     * This method sends the <code>AuthnQuery</code> to specifiied
095     * authentication authority and returns <code>Response</code> coming
096     * from the authentication authority.
097     *
098     * @param authnQuery the <code>AuthnQuery</code> object
099     * @param authnAuthorityEntityID entity ID of authentication authority
100     * @param realm the realm of hosted entity
101     * @param binding the binding
102     *
103     * @return the <code>Response</code> object
104     * @exception SAML2Exception if the operation is not successful
105     *
106     * @supported.api
107     */
108    public static Response sendAuthnQuery(AuthnQuery authnQuery,
109        String authnAuthorityEntityID, String realm, String binding)
110        throws SAML2Exception {
111
112        SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager();
113        AuthnAuthorityDescriptorElement aad = null;
114        try {
115            aad = metaManager.getAuthnAuthorityDescriptor(realm,
116                authnAuthorityEntityID);
117        } catch (SAML2MetaException sme) {
118            SAML2Utils.debug.error("AttributeService.sendAuthnQuery:",
119                sme);
120            throw new SAML2Exception(
121                SAML2Utils.bundle.getString("metaDataError"));
122        }
123
124        if (aad == null) {
125            throw new SAML2Exception(
126                SAML2Utils.bundle.getString("authnAuthorityNotFound"));
127        }
128
129        if (binding == null) {
130            throw new SAML2Exception(
131                SAML2Utils.bundle.getString("unsupportedBinding"));
132        }
133
134        String location = null;
135        List authnService = aad.getAuthnQueryService();
136        for(Iterator iter = authnService.iterator(); iter.hasNext(); ) {
137            AuthnQueryServiceElement authnService1 =
138                (AuthnQueryServiceElement)iter.next();
139            if (binding.equalsIgnoreCase(authnService1.getBinding())) {
140                location = authnService1.getLocation();
141                break;
142            }
143        }
144        if (location == null) {
145            throw new SAML2Exception(
146                SAML2Utils.bundle.getString("unsupportedBinding"));
147        }                
148
149        if (binding.equalsIgnoreCase(SAML2Constants.SOAP)) {
150            signAuthnQuery(authnQuery, realm, false);
151            return sendAuthnQuerySOAP(authnQuery, location,
152               authnAuthorityEntityID, realm, aad);
153        } else {
154            throw new SAML2Exception(
155                SAML2Utils.bundle.getString("unsupportedBinding"));
156        }
157    }
158
159    /**
160     * This method processes the <code>AuthnQuery</code> coming
161     * from a requester.
162     *
163     * @param authnQuery the <code>AuthnQuery</code> object
164     * @param request the <code>HttpServletRequest</code> object
165     * @param response the <code>HttpServletResponse</code> object
166     * @param authnAuthorityEntityID entity ID of authentication authority
167     * @param realm the realm of hosted entity
168     *
169     * @return the <code>Response</code> object
170     * @exception SAML2Exception if the operation is not successful
171     */
172    public static Response processAuthnQuery(AuthnQuery authnQuery,
173        HttpServletRequest request, HttpServletResponse response,
174        String authnAuthorityEntityID, String realm) throws SAML2Exception {
175
176        try {
177            verifyAuthnQuery(authnQuery, authnAuthorityEntityID, realm);
178        } catch(SAML2Exception se) {
179            SAML2Utils.debug.error("AuthnQueryUtil.processAuthnQuery:", se);
180            return SAML2Utils.getErrorResponse(authnQuery,
181                SAML2Constants.REQUESTER, null, se.getMessage(), null);
182        }
183
184        Issuer issuer = authnQuery.getIssuer();
185        String spEntityID = issuer.getValue();        
186        AuthnAuthorityDescriptorElement aad = null;
187        SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager();
188        try {
189            aad = metaManager.getAuthnAuthorityDescriptor(realm,
190                authnAuthorityEntityID);
191        } catch (SAML2MetaException sme) {
192            SAML2Utils.debug.error("AuthnQueryUtil.processAuthnQuery:", sme);
193            return SAML2Utils.getErrorResponse(authnQuery,
194                SAML2Constants.RESPONDER, null,
195                SAML2Utils.bundle.getString("metaDataError"), null);
196        } 
197
198        if (aad == null) {
199            return SAML2Utils.getErrorResponse(authnQuery,
200                SAML2Constants.REQUESTER, null,
201                SAML2Utils.bundle.getString("authnAuthorityNotFound"), null);
202        }
203
204        NameID nameID = getNameID(authnQuery.getSubject(), realm,
205            authnAuthorityEntityID);
206
207        if (nameID == null) {
208            return SAML2Utils.getErrorResponse(authnQuery,
209                SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL,
210                null, null);
211        }
212
213        IDPAccountMapper idpAcctMapper = SAML2Utils.getIDPAccountMapper(
214            realm, authnAuthorityEntityID);
215
216        String userID = idpAcctMapper.getIdentity(nameID,
217            authnAuthorityEntityID, spEntityID, realm);
218
219        if (userID == null) {
220            return SAML2Utils.getErrorResponse(authnQuery,
221                SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL,
222                null, null);
223        }
224
225        IDPAuthnContextMapper idpAuthnContextMapper =
226            IDPSSOUtil.getIDPAuthnContextMapper(realm, authnAuthorityEntityID);
227
228        // get assertion for matching authncontext using session
229        List returnAssertions = new ArrayList();
230        String qSessionIndex = authnQuery.getSessionIndex();
231        RequestedAuthnContext requestedAC =
232            authnQuery.getRequestedAuthnContext();
233
234        List assertions = null;
235        String cacheKey = userID.toLowerCase();
236        AssertionFactory assertionFactory = AssertionFactory.getInstance();
237        if (SAML2FailoverUtils.isSAML2FailoverEnabled()) {
238            if (SAML2Utils.debug.messageEnabled()) {
239                SAML2Utils.debug.message("AuthnQueryUtil.processAuthnQuery: " +
240                    "getting user assertions from DB. user = " + cacheKey);
241            }
242            List list = null;
243            try {
244                list = SAML2FailoverUtils.retrieveSAML2TokensWithSecondaryKey(cacheKey);
245            } catch(SAML2TokenRepositoryException se) {
246                SAML2Utils.debug.error("AuthnQueryUtil.processAuthnQuery: " +
247                        "Unable to obtain user assertions from CTS Repository. user = " + cacheKey, se);
248            }
249            if (list != null && !list.isEmpty()) {
250                assertions = new ArrayList();
251                for (Iterator iter = list.iterator(); iter.hasNext(); ) {
252                    String assertionStr = (String)iter.next();
253                    assertions.add(assertionFactory.createAssertion(
254                        assertionStr));
255                }
256            }
257        } else {
258            assertions = (List)IDPCache.assertionCache.get(cacheKey);
259        }
260
261        if ((assertions != null) && (!assertions.isEmpty())) {
262
263            synchronized (assertions) {
264                for(Iterator aIter = assertions.iterator(); aIter.hasNext();) {
265                    Assertion assertion = (Assertion)aIter.next();
266
267                    if (!assertion.isTimeValid()) {
268                        if (SAML2Utils.debug.messageEnabled()) {
269                            SAML2Utils.debug.message(
270                                "AuthnQueryUtil.processAuthnQuery: "  +
271                                " assertion " + assertion.getID() +
272                                " expired.");
273                        }
274                        continue;
275                    }
276
277                    List authnStmts = assertion.getAuthnStatements();
278
279                    for(Iterator asIter = authnStmts.iterator();
280                        asIter.hasNext();){
281
282                        AuthnStatement authnStmt =
283                            (AuthnStatement)asIter.next();
284                        AuthnContext authnStmtAC = authnStmt.getAuthnContext();
285                        String sessionIndex = authnStmt.getSessionIndex();
286
287                        String authnStmtACClassRef =
288                            authnStmtAC.getAuthnContextClassRef();
289                        if (SAML2Utils.debug.messageEnabled()) {
290                            SAML2Utils.debug.message(
291                                "AuthnQueryUtil.processAuthnQuery: " +
292                                "authnStmtACClassRef is " +
293                                authnStmtACClassRef + ", sessionIndex = " +
294                                sessionIndex);
295                        }
296
297                        if ((qSessionIndex != null) &&
298                            (qSessionIndex.length() != 0) &&
299                            (!qSessionIndex.equals(sessionIndex))) {
300                            continue;
301                        }
302
303                        if (requestedAC != null) {
304                            List requestedACClassRefs =
305                                requestedAC.getAuthnContextClassRef();
306                            String comparison =  requestedAC.getComparison();
307 
308                            if (idpAuthnContextMapper.isAuthnContextMatching(
309                                requestedACClassRefs, authnStmtACClassRef,
310                                comparison, realm, authnAuthorityEntityID)) {
311
312                                returnAssertions.add(assertion);
313                                break;
314                            }
315                        } else {
316                            returnAssertions.add(assertion);
317                            break;
318                        }
319                    }
320                }
321            } // end assertion iterator while.
322        }
323
324        ProtocolFactory protocolFactory = ProtocolFactory.getInstance();
325        Response samlResp = protocolFactory.createResponse();
326        if (!returnAssertions.isEmpty()) {
327            samlResp.setAssertion(returnAssertions);
328        }
329        samlResp.setID(SAML2Utils.generateID());
330        samlResp.setInResponseTo(authnQuery.getID());
331
332        samlResp.setVersion(SAML2Constants.VERSION_2_0);
333        samlResp.setIssueInstant(new Date());
334    
335        Status status = protocolFactory.createStatus();
336        StatusCode statusCode = protocolFactory.createStatusCode();
337        statusCode.setValue(SAML2Constants.SUCCESS);
338        status.setStatusCode(statusCode);
339        samlResp.setStatus(status);
340
341        Issuer respIssuer = assertionFactory.createIssuer();
342        respIssuer.setValue(authnAuthorityEntityID);
343        samlResp.setIssuer(respIssuer);
344
345        signResponse(samlResp, authnAuthorityEntityID, realm, false);
346
347        return samlResp;
348    }
349
350    private static void signAuthnQuery(AuthnQuery authnQuery, String realm,
351        boolean includeCert) throws SAML2Exception {
352
353        String spEntityID = authnQuery.getIssuer().getValue();
354        
355        String alias = SAML2Utils.getSigningCertAlias(realm, spEntityID,
356            SAML2Constants.SP_ROLE);
357
358        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
359        if (signingKey == null) {
360            throw new SAML2Exception(
361                SAML2Utils.bundle.getString("missingSigningCertAlias"));
362        }
363
364        X509Certificate signingCert = null;
365        if (includeCert) {
366            signingCert = keyProvider.getX509Certificate(alias);
367        }
368        
369        if (signingKey != null) {
370            authnQuery.sign(signingKey, signingCert);
371        }
372    }
373
374    private static void verifyAuthnQuery(AuthnQuery authnQuery,
375        String authnAuthorityEntityID, String realm) throws SAML2Exception {
376
377        if (!authnQuery.isSigned()) {
378            throw new SAML2Exception(SAML2Utils.bundle.getString(
379                "authnQueryNotSigned"));
380        }
381
382        Issuer issuer = authnQuery.getIssuer();
383        String spEntityID = issuer.getValue();
384
385        if (!SAML2Utils.isSourceSiteValid(issuer, realm,
386            authnAuthorityEntityID)) {
387
388            throw new SAML2Exception(SAML2Utils.bundle.getString(
389                "authnQueryIssuerInvalid"));
390        }
391        SPSSODescriptorElement spSSODesc = SAML2Utils.getSAML2MetaManager()
392            .getSPSSODescriptor(realm, spEntityID);
393        if (spSSODesc == null) {
394            throw new SAML2Exception(SAML2Utils.bundle.getString(
395                "authnQueryIssuerNotFound"));
396        }
397        X509Certificate signingCert = KeyUtil.getVerificationCert(spSSODesc,
398            spEntityID, SAML2Constants.SP_ROLE);
399
400        if (signingCert != null) {
401            boolean valid = authnQuery.isSignatureValid(signingCert);
402            if (SAML2Utils.debug.messageEnabled()) {
403                SAML2Utils.debug.message(
404                    "AuthnQueryUtil.verifyAuthnQuery: " +
405                    "Signature validity is : " + valid);
406            }
407            if (!valid) {
408                throw new SAML2Exception(SAML2Utils.bundle.getString(
409                    "invalidSignatureAuthnQuery"));
410            }
411        } else {
412            throw new SAML2Exception(
413                    SAML2Utils.bundle.getString("missingSigningCertAlias"));
414        }
415    }
416
417    private static void signResponse(Response response,
418        String authnAuthorityEntityID, String realm, boolean includeCert)
419        throws SAML2Exception {
420        
421        String alias = SAML2Utils.getSigningCertAlias(realm,
422            authnAuthorityEntityID, SAML2Constants.AUTHN_AUTH_ROLE);
423
424        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
425        if (signingKey == null) {
426            throw new SAML2Exception(
427                SAML2Utils.bundle.getString("missingSigningCertAlias"));
428        }
429
430        X509Certificate signingCert = null;
431        if (includeCert) {
432            signingCert = keyProvider.getX509Certificate(alias);
433        }
434        
435        if (signingKey != null) {
436            response.sign(signingKey, signingCert);
437        }
438    }
439
440    private static Response sendAuthnQuerySOAP(AuthnQuery authnQuery,
441        String authnServiceURL, String authnAuthorityEntityID, String realm,
442        AuthnAuthorityDescriptorElement aad) throws SAML2Exception {
443
444        String authnQueryXMLString = authnQuery.toXMLString(true, true);
445        if (SAML2Utils.debug.messageEnabled()) {
446            SAML2Utils.debug.message("AuthnQueryUtil.sendAuthnQuerySOAP: " +
447                "authnQueryXMLString = " + authnQueryXMLString);
448            SAML2Utils.debug.message("AuthnQueryUtil.sendAuthnQuerySOAP: " +
449                "authnServiceURL= " + authnServiceURL);
450        }
451
452        AuthnAuthorityConfigElement config =
453            metaManager.getAuthnAuthorityConfig(realm, authnAuthorityEntityID);
454        authnServiceURL = SAML2Utils.fillInBasicAuthInfo(config,
455            authnServiceURL);
456        
457        SOAPMessage resMsg = null;
458        try {
459            resMsg = SAML2Utils.sendSOAPMessage(authnQueryXMLString,
460                authnServiceURL, true);
461        } catch (SOAPException se) {
462            SAML2Utils.debug.error(
463                "AuthnQueryUtil.sendAuthnQuerySOAP: ", se);
464            throw new SAML2Exception(
465                SAML2Utils.bundle.getString("errorSendingAuthnQuery"));
466        }
467        
468        Element respElem = SAML2Utils.getSamlpElement(resMsg, "Response");
469        Response response =
470            ProtocolFactory.getInstance().createResponse(respElem);
471        
472        if (SAML2Utils.debug.messageEnabled()) {
473            SAML2Utils.debug.message("AuthnQueryUtil.sendAuthnQuerySOAP: " +
474                "response = " + response.toXMLString(true, true));
475        }
476
477        verifyResponse(response, authnQuery, authnAuthorityEntityID, realm,
478            aad);
479
480        return response;
481    }
482
483    private static void verifyResponse(Response response,
484        AuthnQuery authnQuery, String authnAuthorityEntityID, String realm,
485        AuthnAuthorityDescriptorElement aad) throws SAML2Exception {
486
487        String authnQueryID = authnQuery.getID();
488        if ((authnQueryID != null) &&
489            (!authnQueryID.equals(response.getInResponseTo()))) {
490
491            throw new SAML2Exception(
492                SAML2Utils.bundle.getString("invalidInResponseToAuthnQuery"));
493        }
494
495        Issuer respIssuer = response.getIssuer();
496        if (respIssuer == null) {
497            return;
498        }
499
500        if (!authnAuthorityEntityID.equals(respIssuer.getValue())) {
501            throw new SAML2Exception(SAML2Utils.bundle.getString(
502                "responseIssuerMismatch"));
503        }
504
505        if (!response.isSigned()) {
506            throw new SAML2Exception(SAML2Utils.bundle.getString(
507                "responseNotSigned"));
508        }
509
510        X509Certificate signingCert = KeyUtil.getVerificationCert(aad,
511            authnAuthorityEntityID, SAML2Constants.AUTHN_AUTH_ROLE);
512
513        if (signingCert == null) {
514            throw new SAML2Exception(
515                SAML2Utils.bundle.getString("missingSigningCertAlias"));
516        }
517        boolean valid = response.isSignatureValid(signingCert);
518        if (SAML2Utils.debug.messageEnabled()) {
519            SAML2Utils.debug.message("AuthnQueryUtil.verifyResponse: " +
520                "Signature validity is : " + valid);
521        }
522        if (!valid) {
523            throw new SAML2Exception(SAML2Utils.bundle.getString(
524                "invalidSignatureOnResponse"));
525        }
526
527        String spEntityID = authnQuery.getIssuer().getValue();
528
529        List assertions = response.getAssertion();
530        if (assertions == null) {
531            List encAssertions = response.getEncryptedAssertion();
532            if ((encAssertions != null) && (!encAssertions.isEmpty())) {
533                String alias = SAML2Utils.getEncryptionCertAlias(realm,
534                    spEntityID, SAML2Constants.SP_ROLE); 
535                PrivateKey privateKey = keyProvider.getPrivateKey(alias);
536                for(Iterator iter = encAssertions.iterator();iter.hasNext();) {
537                    EncryptedAssertion eAssertion =
538                        (EncryptedAssertion)iter.next();
539                    Assertion assertion = eAssertion.decrypt(privateKey);
540                    if (assertions == null) {
541                        assertions = new ArrayList();
542                    }
543                    assertions.add(assertion);
544                }
545            }
546        }
547
548        if ((assertions == null) || (assertions.isEmpty())) {
549            return;
550        }
551
552        signingCert = KeyUtil.getVerificationCert(aad,
553            authnAuthorityEntityID, SAML2Constants.IDP_ROLE);
554
555        for(Iterator iter = assertions.iterator(); iter.hasNext(); ) {
556            Assertion assertion = (Assertion)iter.next();
557            if (assertion.isSigned()) {
558
559                if (signingCert == null) {
560                    throw new SAML2Exception(
561                        SAML2Utils.bundle.getString("missingSigningCertAlias"));
562                }
563
564                valid = assertion.isSignatureValid(signingCert);
565                if (SAML2Utils.debug.messageEnabled()) {
566                    SAML2Utils.debug.message(
567                        "AuthnQueryUtil.verifyResponse: " +
568                        "Signature validity is : " + valid);
569                }
570                if (!valid) {
571                    throw new SAML2Exception(SAML2Utils.bundle.getString(
572                        "invalidSignatureOnAssertion"));
573                }
574            }
575        }
576    }
577
578    private static NameID getNameID(Subject subject, String realm,
579        String authnAuthorityEntityID){
580
581        NameID nameID = subject.getNameID();
582        if (nameID == null) {
583            String alias = SAML2Utils.getEncryptionCertAlias(realm,
584                authnAuthorityEntityID, SAML2Constants.AUTHN_AUTH_ROLE);
585
586            PrivateKey privateKey = keyProvider.getPrivateKey(alias);
587
588            EncryptedID encryptedID = subject.getEncryptedID();
589            try {
590                nameID = encryptedID.decrypt(privateKey);
591            } catch (SAML2Exception ex) {
592                if (SAML2Utils.debug.messageEnabled()) {
593                    SAML2Utils.debug.message("AuthnQueryUtil.getNameID:", ex);
594                }
595                return null; 
596            }
597        }
598
599        if (!SAML2Utils.isPersistentNameID(nameID)) {
600            return null;
601        }
602
603        return nameID;
604    }
605}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.