001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: FSResponse.java,v 1.2 2008/06/25 05:46:45 qcheng Exp $ 026 * Portions Copyrighted 2014 ForgeRock AS 027 */ 028 029package com.sun.identity.federation.message; 030 031import java.text.ParseException; 032import java.util.List; 033import java.util.Collections; 034import java.util.ArrayList; 035import java.util.Iterator; 036 037import java.io.ByteArrayInputStream; 038import java.io.IOException; 039 040import org.w3c.dom.Element; 041import org.w3c.dom.Node; 042import org.w3c.dom.NodeList; 043import org.w3c.dom.Document; 044 045import com.sun.identity.shared.encode.Base64; 046import com.sun.identity.shared.xml.XMLUtils; 047import com.sun.identity.shared.DateUtils; 048import com.sun.identity.saml.common.SAMLConstants; 049import com.sun.identity.saml.common.SAMLException; 050import com.sun.identity.saml.common.SAMLResponderException; 051import com.sun.identity.saml.common.SAMLVersionMismatchException; 052import com.sun.identity.saml.xmlsig.XMLSignatureManager; 053import com.sun.identity.saml.protocol.Response; 054import com.sun.identity.saml.protocol.Status; 055 056import com.sun.identity.federation.message.common.FSMsgException; 057import com.sun.identity.federation.common.FSUtils; 058import com.sun.identity.federation.common.IFSConstants; 059 060/** 061 * This class contains methods for creating a Liberty <code>Response</code>. 062 * 063 * @supported.all.api 064 * @deprecated since 12.0.0 065 */ 066@Deprecated 067public class FSResponse extends Response { 068 protected String id = null; 069 070 /** 071 * Returns the value of <code>id</code> attribute. 072 * 073 * @return the value of <code>id</code> attribute. 074 * @see #setID(String) 075 */ 076 public String getID() { 077 return id; 078 } 079 080 /** 081 * Sets the value of <code>id</code> attribute. 082 * 083 * @param id the value of <code>id</code> attribute. 084 * @see #getID() 085 */ 086 public void setID(String id) { 087 this.id = id; 088 } 089 090 /** 091 * Returns the signed <code>XML</code> string. 092 * 093 * @return the signed <code>XML</code> string. 094 */ 095 public String getSignatureString(){ 096 return signatureString; 097 } 098 099 /** 100 * Returns the <code>MinorVersion</code>. 101 * 102 * @return the <code>MinorVersion</code>. 103 * @see #setMinorVersion(int) 104 */ 105 106 public int getMinorVersion() { 107 return minorVersion; 108 } 109 110 /** 111 * Sets the <code>MinorVersion</code>. 112 * 113 * @param version the <code>MinorVersion</code>. 114 * @see #getMinorVersion() 115 */ 116 public void setMinorVersion(int version) { 117 minorVersion = version; 118 } 119 120 /** 121 * Constructor creates <code>FSResponse</code> object. 122 * 123 * @param responseID value of <code>ResponseId</code> attribute. 124 * @param inResponseTo value of <code>inResponseTo</code> attribute. 125 * @param status the <code>Status</code> object. 126 * @param contents list containing response elements. 127 * @throws SAMLException it there is an error creating this object. 128 * @throws FSMsgException it there is an error creating this object. 129 */ 130 public FSResponse(String responseID, 131 String inResponseTo, 132 Status status, 133 List contents) throws SAMLException, FSMsgException { 134 super( responseID, inResponseTo, status, contents); 135 } 136 137 public static FSResponse parseResponseXML( 138 String xml 139 ) throws SAMLException, FSMsgException { 140 // parse the xml string 141 FSUtils.debug.message("FSResponse.parseResponseXML: Called"); 142 Element root; 143 Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug); 144 if (doc == null) { 145 FSUtils.debug.error("FSResponse.parseXML:Error " 146 + "while parsing input xml string"); 147 throw new FSMsgException("parseError",null); 148 } 149 root = doc.getDocumentElement(); 150 return new FSResponse(root); 151 } 152 153 /** 154 * Constructor creates <code>FSResponse</code> object form 155 * a Document Element. 156 * 157 * @param root the Document Element object. 158 * @throws SAMLException if there is an error creating this object. 159 * @throws FSMsgException if there is an error creating this object. 160 */ 161 public FSResponse(Element root) throws SAMLException, FSMsgException { 162 FSUtils.debug.message("FSResponse(Element): Called"); 163 if (root == null) { 164 FSUtils.debug.message("FSResponse(Element): " 165 + "Input paramenter (root) is null"); 166 throw new FSMsgException("nullInput",null); 167 } 168 String tag = null; 169 if (((tag = root.getLocalName()) == null) || 170 (!tag.equals("Response"))) { 171 FSUtils.debug.message("FSResponse(Element): " 172 + "Root element name is not Response"); 173 throw new FSMsgException("wrongInput",null); 174 } 175 id = root.getAttribute("id"); 176 responseID = root.getAttribute("ResponseID"); 177 if ((responseID == null) || (responseID.length() == 0)) { 178 if (FSUtils.debug.messageEnabled()) { 179 FSUtils.debug.message("FSResponse(Element): " 180 + "Response doesn't have ResponseID attribute"); 181 } 182 String[] args = { IFSConstants.RESPONSE_ID }; 183 throw new FSMsgException("missingAttribute",args); 184 } 185 186 inResponseTo = root.getAttribute("InResponseTo"); 187 if (inResponseTo == null) { 188 if (FSUtils.debug.messageEnabled()) { 189 FSUtils.debug.message("FSResponse(Element): " 190 + "Response doesn't have InResponseTo attribute"); 191 } 192 String[] args = { IFSConstants.IN_RESPONSE_TO }; 193 throw new FSMsgException("missingAttribute",args); 194 } 195 196 // Attribute IssueInstant 197 String instantString = root.getAttribute("IssueInstant"); 198 if ((instantString == null) || (instantString.length() == 0)) { 199 FSUtils.debug.message("FSResponse(Element): missing IssueInstant"); 200 String[] args = { IFSConstants.ISSUE_INSTANT }; 201 throw new FSMsgException("missingAttribute",args); 202 } else { 203 try { 204 issueInstant = DateUtils.stringToDate(instantString); 205 } catch (ParseException e) { 206 if (FSUtils.debug.messageEnabled()) { 207 FSUtils.debug.message("FSResponse(Element): could not " 208 + "parse IssueInstant:", e); 209 } 210 throw new FSMsgException("wrongInput", null); 211 } 212 } 213 parseMajorVersion(root.getAttribute("MajorVersion")); 214 parseMinorVersion(root.getAttribute("MinorVersion")); 215 setRecipient(root.getAttribute("Recipient")); 216 NodeList nl = root.getChildNodes(); 217 Node child; 218 String childName; 219 int length = nl.getLength(); 220 for (int i = 0; i < length; i++) { 221 child = nl.item(i); 222 if ((childName = child.getLocalName()) != null) { 223 if (childName.equals("Status")) { 224 if (status != null) { 225 if (FSUtils.debug.messageEnabled()) { 226 FSUtils.debug.message( 227 "FSResponse(Element): included more" 228 + " than one <Status>"); 229 } 230 throw new FSMsgException("moreElement",null); 231 } 232 status = new Status((Element) child); 233 } else if (childName.equals("Assertion")) { 234 if (assertions == Collections.EMPTY_LIST) { 235 assertions = new ArrayList(); 236 } 237 assertions.add(new FSAssertion((Element) child)); 238 }else { 239 if (FSUtils.debug.messageEnabled()) { 240 FSUtils.debug.message( 241 "FSResponse(Element): included wrong " 242 + "element: " + childName); 243 } 244 throw new FSMsgException("wrongInput",null); 245 } 246 } // end if childName != null 247 } // end for loop 248 249 if (status == null) { 250 FSUtils.debug.message( 251 "FSResponse(Element): missing element <Status>."); 252 throw new FSMsgException("missingElement",null); 253 } 254 255 //check for signature 256 List signs = XMLUtils.getElementsByTagNameNS1(root, 257 SAMLConstants.XMLSIG_NAMESPACE_URI, 258 SAMLConstants.XMLSIG_ELEMENT_NAME); 259 int signsSize = signs.size(); 260 if (signsSize == 1) { 261 Element elem = (Element)signs.get(0); 262 setSignature(elem); 263 xmlString = XMLUtils.print(root); 264 signed = true; 265 } else if (signsSize != 0) { 266 if (FSUtils.debug.messageEnabled()) { 267 FSUtils.debug.message("FSResponse(Element): included more than" 268 + " one Signature element."); 269 } 270 throw new FSMsgException("moreElement",null); 271 } 272 //end check for signature 273 } 274 275 /** 276 * Sets the <code>MajorVersion</code> by parsing the version string. 277 * 278 * @param majorVer a String representing the <code>MajorVersion</code> to 279 * be set. 280 * @throws SAMLException on error. 281 * @throws FSMsgException if there is an error parsing the version string. 282 */ 283 private void parseMajorVersion(String majorVer) 284 throws SAMLException, FSMsgException { 285 try { 286 majorVersion = Integer.parseInt(majorVer); 287 } catch (NumberFormatException e) { 288 if (FSUtils.debug.messageEnabled()) { 289 FSUtils.debug.message("FSResponse(Element): invalid " 290 + "MajorVersion", e); 291 } 292 throw new FSMsgException("wrongInput",null); 293 } 294 295 if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) { 296 if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) { 297 if (FSUtils.debug.messageEnabled()) { 298 FSUtils.debug.message("FSResponse(Element):MajorVersion of" 299 + " the Response is too high."); 300 } 301 throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME, 302 "responseVersionTooHigh",null); 303 } else { 304 if (FSUtils.debug.messageEnabled()) { 305 FSUtils.debug.message("FSResponse(Element):MajorVersion of" 306 + " the Response is too low."); 307 } 308 throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME, 309 "responseVersionTooLow",null); 310 } 311 } 312 } 313 314 /** 315 * Sets the <code>MinorVersion</code> by parsing the version string. 316 * 317 * @param minorVer a String representing the <code>MinorVersion</code> to 318 * be set. 319 * @throws SAMLException when the version mismatchs. 320 * @throws FSMsgException if there is an error 321 * parsing the version string. 322 */ 323 private void parseMinorVersion(String minorVer) 324 throws SAMLException, FSMsgException { 325 try { 326 minorVersion = Integer.parseInt(minorVer); 327 } catch (NumberFormatException e) { 328 if (FSUtils.debug.messageEnabled()) { 329 FSUtils.debug.message("FSResponse(Element): invalid " 330 + "MinorVersion", e); 331 } 332 throw new FSMsgException("wrongInput",null); 333 } 334 335 if (minorVersion > IFSConstants.FF_12_SAML_PROTOCOL_MINOR_VERSION) { 336 FSUtils.debug.error("FSResponse(Element):MinorVersion of" 337 + " the Response is too high."); 338 throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME, 339 "responseVersionTooHigh",null); 340 } else if (minorVersion < 341 IFSConstants.FF_11_SAML_PROTOCOL_MINOR_VERSION) { 342 FSUtils.debug.error("FSResponse(Element):MinorVersion of" 343 + " the Response is too low."); 344 throw new SAMLVersionMismatchException(FSUtils.BUNDLE_NAME, 345 "responseVersionTooLow",null); 346 } 347 } 348 349 /** 350 * Returns a String representation of the Logout Response. 351 * 352 * @return a string containing the valid XML for this element 353 * @throws FSMsgException if there is an error converting 354 * this object to a string. 355 */ 356 public String toXMLString() throws FSMsgException { 357 return this.toXMLString(true, true); 358 } 359 360 /** 361 * Returns a String representation of the Logout Response. 362 * 363 * @param includeNS : Determines whether or not the namespace qualifier 364 * is prepended to the Element when converted 365 * @param declareNS : Determines whether or not the namespace is declared 366 * within the Element. 367 * @return a string containing the valid XML for this element 368 * @throws FSMsgException if there is an error converting 369 * this object ot a string. 370 */ 371 372 public String toXMLString(boolean includeNS, boolean declareNS) 373 throws FSMsgException { 374 return toXMLString(includeNS, declareNS, false); 375 } 376 377 public String toXMLString(boolean includeNS,boolean declareNS, 378 boolean includeHeader) throws FSMsgException { 379 FSUtils.debug.message("FSResponse.toXMLString(3): Called"); 380 StringBuffer xml = new StringBuffer(500); 381 if (includeHeader) { 382 xml.append("<?xml version=\"1.0\" encoding=\""). 383 append(SAMLConstants.DEFAULT_ENCODING).append("\" ?>"); 384 } 385 String prefixSAML=null; 386 String prefixLIB=null; 387 String prefixSAML_PROTOCOL = ""; 388 String uriSAML_PROTOCOL = ""; 389 String uriSAML = ""; 390 String uriLIB = ""; 391 String uriDS=""; 392 String uriXSI=""; 393 394 if (includeNS) { 395 prefixLIB = IFSConstants.LIB_PREFIX; 396 prefixSAML = IFSConstants.ASSERTION_PREFIX; 397 prefixSAML_PROTOCOL = IFSConstants.PROTOCOL_PREFIX; 398 } 399 if (declareNS) { 400 if(minorVersion == IFSConstants.FF_12_SAML_PROTOCOL_MINOR_VERSION){ 401 uriLIB = IFSConstants.LIB_12_NAMESPACE_STRING; 402 } else { 403 uriLIB = IFSConstants.LIB_NAMESPACE_STRING; 404 } 405 uriSAML = IFSConstants.assertionDeclareStr; 406 uriSAML_PROTOCOL = IFSConstants.PROTOCOL_NAMESPACE_STRING; 407 uriDS = IFSConstants.DSSAMLNameSpace; 408 uriXSI = IFSConstants.XSI_NAMESPACE_STRING; 409 } 410 411 String instantString = DateUtils.toUTCDateFormat(issueInstant); 412 413 if((responseID != null) && (inResponseTo != null)){ 414 xml.append("<").append(prefixSAML_PROTOCOL).append("Response"). 415 append(uriLIB). 416 append(uriSAML).append(uriSAML_PROTOCOL).append(" "). 417 append(uriDS). 418 append(" ").append(uriXSI).append(" ResponseID=\""). 419 append(responseID).append("\" "); 420 if ((inResponseTo != null) && (inResponseTo.length() != 0)) { 421 xml.append(" InResponseTo=\"").append(inResponseTo). 422 append("\" "); 423 } 424 if (minorVersion == IFSConstants.FF_11_PROTOCOL_MINOR_VERSION && 425 id != null && !(id.length() == 0)){ 426 xml.append(" id=\"").append(id).append("\""); 427 } 428 xml.append(" MajorVersion=\""). 429 append(majorVersion).append("\" "). 430 append(" MinorVersion=\"").append(minorVersion). 431 append("\" "). 432 append(" IssueInstant=\"").append(instantString). 433 append("\""); 434 if ((recipient != null) && (recipient.length() != 0)) { 435 xml.append(" Recipient=\"").append(recipient).append("\" "); 436 } 437 xml.append(">"); 438 } 439 440 if (signed) { 441 if (signatureString != null) { 442 xml.append(signatureString); 443 } else if (signature != null) { 444 signatureString = XMLUtils.print(signature); 445 xml.append(signatureString); 446 } 447 } 448 449 if(status != null) 450 xml.append(status.toString(includeNS, false)); 451 452 if ((assertions != null) && (assertions != Collections.EMPTY_LIST)) { 453 Iterator j = assertions.iterator(); 454 while (j.hasNext()) { 455 xml.append(((FSAssertion) j.next()). 456 toXMLString(true,declareNS)); 457 } 458 } 459 460 xml.append("</").append(prefixSAML_PROTOCOL).append("Response>"); 461 return xml.toString(); 462 } 463 464 /** 465 * Returns <code>FSResponse</code> object. The object 466 * is created by parsing an Base64 encoded response string. 467 * 468 * @param encodedRes the encoded response string 469 * @throws FSMsgException if there is an error creating 470 * <code>FSResponse</code> object. 471 * @throws FSMsgException if there is an error creating 472 * <code>FSResponse</code> object. 473 */ 474 public static FSResponse parseBASE64EncodedString( 475 String encodedRes) throws FSMsgException, SAMLException { 476 FSUtils.debug.message("FSResponse.parseBASE64EncodedString:Called new"); 477 if (encodedRes != null) { 478 String decodedAuthnRes = new String(Base64.decode(encodedRes)); 479 if (FSUtils.debug.messageEnabled()) { 480 FSUtils.debug.message("FSResponse.parseBASE64EncodedString:" 481 + "Decoded AuthnResponse message: " 482 + decodedAuthnRes); 483 } 484 return parseResponseXML(decodedAuthnRes); 485 } else{ 486 if (FSUtils.debug.messageEnabled()) { 487 FSUtils.debug.message("FSResponse.parseBASE64EncodedString:" 488 + "null String passed in as argument."); 489 } 490 throw new FSMsgException("nullInput",null); 491 } 492 } 493 494 /** 495 * Returns a Base64 Encoded String. 496 * 497 * @return a Base64 Encoded String. 498 * @throws FSMsgException if there is an error encoding the string. 499 */ 500 public String toBASE64EncodedString() throws FSMsgException { 501 FSUtils.debug.message("FSResponse.toBASE64EncodedString: Called"); 502 if ((responseID == null) || (responseID.length() == 0)){ 503 responseID = FSUtils.generateID(); 504 if (responseID == null) { 505 FSUtils.debug.error("FSResponse.toBASE64EncodedString: " 506 + "couldn't generate ResponseID."); 507 throw new FSMsgException("errorGenerateID",null); 508 } 509 } 510 return Base64.encode(this.toXMLString().getBytes()); 511 } 512 513 /** 514 * Signs the Response. 515 * 516 * @param certAlias the Certificate Alias. 517 * @throws XMLSignatureException if <code>FSAuthnRequest</code> 518 * cannot be signed. 519 */ 520 public void signXML(String certAlias) throws SAMLException { 521 FSUtils.debug.message("FSResponse.signXML: Called"); 522 if (signed) { 523 if (FSUtils.debug.messageEnabled()) { 524 FSUtils.debug.message("FSResponse.signXML: the assertion is " 525 + "already signed."); 526 } 527 throw new SAMLResponderException(FSUtils.BUNDLE_NAME, 528 "alreadySigned",null); 529 } 530 if (certAlias == null || certAlias.length() == 0) { 531 throw new SAMLResponderException(FSUtils.BUNDLE_NAME, 532 "cannotFindCertAlias",null); 533 } 534 try { 535 XMLSignatureManager manager = XMLSignatureManager.getInstance(); 536 if (minorVersion == IFSConstants.FF_11_PROTOCOL_MINOR_VERSION) { 537 signatureString = manager.signXML( 538 this.toXMLString(true, true), 539 certAlias, IFSConstants.DEF_SIG_ALGO, 540 IFSConstants.ID, 541 this.id, false); 542 } else if (minorVersion == 543 IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) { 544 signatureString = manager.signXML( 545 this.toXMLString(true, true), 546 certAlias, IFSConstants.DEF_SIG_ALGO, 547 IFSConstants.RESPONSE_ID, 548 this.getResponseID(), false); 549 } else { 550 if (FSUtils.debug.messageEnabled()) { 551 FSUtils.debug.message("invalid minor version."); 552 } 553 } 554 555 signature = 556 XMLUtils.toDOMDocument(signatureString, FSUtils.debug) 557 .getDocumentElement(); 558 559 signed = true; 560 xmlString = this.toXMLString(true, true); 561 } catch(Exception e){ 562 throw new SAMLResponderException(FSUtils.BUNDLE_NAME, 563 "signFailed",null); 564 } 565 } 566 567 /** 568 * Unsupported operation. 569 */ 570 public void signXML() throws SAMLException { 571 throw new SAMLException(FSUtils.BUNDLE_NAME, 572 "unsupportedOperation",null); 573 } 574}