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.