/**
   * Signs the message.
   *
   * @param soapMessage SOAPMessage that needs to be signed.
   * @param profile Security profile that needs to be used for signing.
   * @param assertion Security Assertion
   * @return SOAPMessage signed SOAPMessage.
   */
  private SOAPMessage signMessage(
      SOAPMessage soapMessage, String profile, SecurityAssertion assertion)
      throws SOAPBindingException {
    try {
      SOAPHeader soapHeader = soapMessage.getSOAPPart().getEnvelope().getHeader();
      if (soapHeader == null) {
        soapMessage.getSOAPPart().getEnvelope().addHeader();
      }
      SOAPBody soapBody = soapMessage.getSOAPPart().getEnvelope().getBody();
      if (soapBody == null) {
        throw new SOAPBindingException(WSSUtils.bundle.getString("nullSOAPBody"));
      }

      String bodyId = SAMLUtils.generateID();
      soapBody.setAttributeNS(WSSEConstants.NS_WSU_WSF11, WSSEConstants.WSU_ID, bodyId);
      List ids = new ArrayList();
      ids.add(bodyId);
      if (correlationId != null) {
        ids.add(correlationId);
      }

      Certificate cert = null;
      Element sigElem = null;
      ByteArrayInputStream bin = null;
      ByteArrayOutputStream bop = new ByteArrayOutputStream();
      Document doc = null;
      if (profile == null
          || profile.equals(Message.NULL_X509)
          || profile.equals(Message.TLS_X509)
          || profile.equals(Message.CLIENT_TLS_X509)
          || profile.equals(Message.NULL_X509_WSF11)
          || profile.equals(Message.TLS_X509_WSF11)
          || profile.equals(Message.CLIENT_TLS_X509_WSF11)) {

        BinarySecurityToken binaryToken = addBinaryToken(soapMessage);
        cert = SecurityUtils.getCertificate(binaryToken);
        soapMessage.writeTo(bop);
        bin = new ByteArrayInputStream(bop.toByteArray());
        doc = XMLUtils.toDOMDocument(bin, WSSUtils.debug);
        sigElem =
            SecurityUtils.getSignatureManager()
                .signWithWSSX509TokenProfile(
                    doc, cert, "", ids, SOAPBindingConstants.WSF_11_VERSION);

      } else if (profile.equals(Message.NULL_SAML)
          || profile.equals(Message.TLS_SAML)
          || profile.equals(Message.CLIENT_TLS_SAML)
          || profile.equals(Message.NULL_SAML_WSF11)
          || profile.equals(Message.TLS_SAML_WSF11)
          || profile.equals(Message.CLIENT_TLS_SAML_WSF11)) {

        cert = SecurityUtils.getCertificate(assertion);
        soapMessage.writeTo(bop);
        new ByteArrayInputStream(bop.toByteArray());
        bin = new ByteArrayInputStream(bop.toByteArray());
        doc = XMLUtils.toDOMDocument(bin, WSSUtils.debug);
        sigElem =
            SecurityUtils.getSignatureManager()
                .signWithWSSSAMLTokenProfile(
                    doc,
                    cert,
                    assertion.getAssertionID(),
                    "",
                    ids,
                    SOAPBindingConstants.WSF_11_VERSION);
      }

      if (sigElem == null) {
        WSSUtils.debug.error("MessageProcessor.signMessage: " + "SigElement is null");
        throw new SOAPBindingException(WSSUtils.bundle.getString("cannotSignMessage"));
      }

      Element securityHeader = getSecurityHeader(soapMessage);
      securityHeader.appendChild(securityHeader.getOwnerDocument().importNode(sigElem, true));

      return Utils.DocumentToSOAPMessage(sigElem.getOwnerDocument());

    } catch (Exception ex) {
      WSSUtils.debug.error("MessageProcessor.signMessage: " + "Signing failed.", ex);
      throw new SOAPBindingException(WSSUtils.bundle.getString("cannotSignMessage"));
    }
  }
  /**
   * Initiates <code>SAML</code> web browser POST profile. This method takes in a TARGET in the
   * request, creates a SAMLResponse, then redirects user to the destination site.
   *
   * @param request <code>HttpServletRequest</code> instance
   * @param response <code>HttpServletResponse</code> instance
   * @throws ServletException if there is an error.
   * @throws IOException if there is an error.
   */
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    if ((request == null) || (response == null)) {
      String[] data = {SAMLUtils.bundle.getString("nullInputParameter")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.NULL_PARAMETER, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "nullInputParameter",
          SAMLUtils.bundle.getString("nullInputParameter"));
      return;
    }

    SAMLUtils.checkHTTPContentLength(request);

    // get Session
    Object token = getSession(request);
    if (token == null) {
      response.sendRedirect(SAMLUtils.getLoginRedirectURL(request));
      return;
    }

    // obtain TARGET
    String target = request.getParameter(SAMLConstants.POST_TARGET_PARAM);
    if (target == null || target.length() == 0) {
      String[] data = {SAMLUtils.bundle.getString("missingTargetSite")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.MISSING_TARGET, data, token);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "missingTargetSite",
          SAMLUtils.bundle.getString("missingTargetSite"));
      return;
    }

    // Get the Destination site Entry
    // find the destSite POST URL, which is the Receipient
    SAMLServiceManager.SiteEntry destSite = getDestSite(target);
    String destSiteUrl = null;
    if ((destSite == null) || ((destSiteUrl = destSite.getPOSTUrl()) == null)) {
      String[] data = {SAMLUtils.bundle.getString("targetForbidden"), target};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.TARGET_FORBIDDEN, data, token);
      SAMLUtils.sendError(
          request,
          response,
          response.SC_BAD_REQUEST,
          "targetForbidden",
          SAMLUtils.bundle.getString("targetForbidden") + " " + target);
      return;
    }

    Response samlResponse = null;
    try {
      String version = destSite.getVersion();
      int majorVersion = SAMLConstants.PROTOCOL_MAJOR_VERSION;
      int minorVersion = SAMLConstants.PROTOCOL_MINOR_VERSION;
      if (version != null) {
        StringTokenizer st = new StringTokenizer(version, ".");
        if (st.countTokens() == 2) {
          majorVersion = Integer.parseInt(st.nextToken().trim());
          minorVersion = Integer.parseInt(st.nextToken().trim());
        }
      }
      // create assertion
      AssertionManager am = AssertionManager.getInstance();
      SessionProvider sessionProvider = SessionManager.getProvider();
      Assertion assertion =
          am.createSSOAssertion(
              sessionProvider.getSessionID(token),
              null,
              request,
              response,
              destSite.getSourceID(),
              target,
              majorVersion + "." + minorVersion);

      // create SAMLResponse
      StatusCode statusCode = new StatusCode(SAMLConstants.STATUS_CODE_SUCCESS);
      Status status = new Status(statusCode);
      List contents = new ArrayList();
      contents.add(assertion);
      samlResponse = new Response(null, status, destSiteUrl, contents);
      samlResponse.setMajorVersion(majorVersion);
      samlResponse.setMinorVersion(minorVersion);
    } catch (SessionException sse) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doGet: Exception " + "Couldn't get SessionProvider:", sse);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "couldNotCreateResponse",
          sse.getMessage());
      return;
    } catch (NumberFormatException ne) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doGet: Exception " + "when creating Response: ", ne);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "couldNotCreateResponse",
          ne.getMessage());
      return;
    } catch (SAMLException se) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doGet: Exception " + "when creating Response: ", se);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "couldNotCreateResponse",
          se.getMessage());
      return;
    }

    // sign the samlResponse
    byte signedBytes[] = null;
    try {
      samlResponse.signXML();
      if (SAMLUtils.debug.messageEnabled()) {
        SAMLUtils.debug.message(
            "SAMLPOSTProfileServlet.doGet: "
                + "signed samlResponse is"
                + samlResponse.toString(true, true, true));
      }
      signedBytes = SAMLUtils.getResponseBytes(samlResponse);
    } catch (Exception e) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doGet: Exception " + "when signing the response:", e);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "errorSigningResponse",
          SAMLUtils.bundle.getString("errorSigningResponse"));
      return;
    }

    // base64 encode the signed samlResponse
    String encodedResponse = null;
    try {
      encodedResponse = Base64.encode(signedBytes, true).trim();
    } catch (Exception e) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doGet: Exception " + "when encoding the response:", e);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "errorEncodeResponse",
          SAMLUtils.bundle.getString("errorEncodeResponse"));
      return;
    }

    if (LogUtils.isAccessLoggable(java.util.logging.Level.FINE)) {
      String[] data = {
        SAMLUtils.bundle.getString("redirectTo"),
        target,
        destSiteUrl,
        new String(signedBytes, "UTF-8")
      };
      LogUtils.access(java.util.logging.Level.FINE, LogUtils.REDIRECT_TO_URL, data, token);
    } else {
      String[] data = {SAMLUtils.bundle.getString("redirectTo"), target, destSiteUrl};
      LogUtils.access(java.util.logging.Level.INFO, LogUtils.REDIRECT_TO_URL, data, token);
    }
    response.setContentType("text/html; charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println("<HTML>");
    out.println("<BODY Onload=\"document.forms[0].submit()\">");
    out.println("<FORM METHOD=\"POST\" ACTION=\"" + destSiteUrl + "\">");
    out.println("<INPUT TYPE=\"HIDDEN\" NAME=\"" + SAMLConstants.POST_SAML_RESPONSE_PARAM + "\" ");
    out.println("VALUE=\"" + encodedResponse + "\">");
    out.println(
        "<INPUT TYPE=\"HIDDEN\" NAME=\""
            + SAMLConstants.POST_TARGET_PARAM
            + "\" VALUE=\""
            + target
            + "\"> </FORM>");
    out.println("</BODY></HTML>");
    out.close();
  }
  /**
   * Signs the entity descriptor root element by the following rules:
   *
   * <ul>
   *   <li>Hosted Entity
   *       <ul>
   *         <li>If there is a signature already on the EntityDescriptor, removes it, then signs the
   *             EntityDescriptor.
   *         <li>Simply signs the EntityDescriptor otherwise.
   *       </ul>
   *   <li>Remote Entity
   *       <ul>
   *         <li>If there is a signature already on the EntityDescriptor, then does not change it,
   *             but returns the Document with the original signature.
   *         <li>Simply signs the EntityDescriptor otherwise
   *       </ul>
   * </ul>
   *
   * If there is no extended metadata for the entity, the entity is considered as remote.
   *
   * @param realm The realm where the EntityDescriptor belongs to.
   * @param descriptor The entity descriptor.
   * @return Signed <code>Document</code> for the entity descriptor or null if no metadata signing
   *     key is found in the configuration.
   * @throws SAML2MetaException if unable to sign the entity descriptor.
   * @throws JAXBException if the entity descriptor is invalid.
   */
  public static Document sign(String realm, EntityDescriptorElement descriptor)
      throws JAXBException, SAML2MetaException {
    if (descriptor == null) {
      throw new SAML2MetaException("Unable to sign null descriptor");
    }

    SAML2MetaManager metaManager = new SAML2MetaManager();
    EntityConfigElement cfgElem = metaManager.getEntityConfig(realm, descriptor.getEntityID());
    boolean isHosted;
    if (cfgElem == null) {
      // if there is no EntityConfig, this is considered as a remote entity
      isHosted = false;
    } else {
      isHosted = cfgElem.isHosted();
    }

    String signingCert = getRealmSetting(METADATA_SIGNING_KEY, realm);
    if (signingCert == null) {
      return null;
    }

    initializeKeyStore();

    String xmlstr = SAML2MetaUtils.convertJAXBToString(descriptor);
    xmlstr = formatBase64BinaryElement(xmlstr);

    Document doc = XMLUtils.toDOMDocument(xmlstr, debug);
    NodeList childNodes = doc.getDocumentElement().getChildNodes();
    for (int i = 0; i < childNodes.getLength(); i++) {
      Node node = childNodes.item(i);
      if (node.getLocalName() != null
          && node.getLocalName().equals("Signature")
          && node.getNamespaceURI().equals(NS_XMLSIG)) {
        if (isHosted) {
          node.getParentNode().removeChild(node);
          break;
        } else {
          // one signature found for this remote entity on the root element,
          // in this case returning the entry with the original signature
          // as that may be judged more accurately
          return doc;
        }
      }
    }

    // we need to sign or re-sign the document, let's generate a new ID
    String descriptorId = SAMLUtils.generateID();
    doc.getDocumentElement().setAttribute(ATTR_ID, descriptorId);

    XMLSignatureManager sigManager = XMLSignatureManager.getInstance();
    try {
      String xpath =
          "//*[local-name()=\""
              + TAG_ENTITY_DESCRIPTOR
              + "\" and namespace-uri()=\""
              + NS_META
              + "\"]/*[1]";
      sigManager.signXMLUsingKeyPass(
          doc,
          signingCert,
          getRealmSetting(METADATA_SIGNING_KEY_PASS, realm),
          null,
          SAML2Constants.ID,
          descriptorId,
          true,
          xpath);
    } catch (XMLSignatureException xmlse) {
      if (debug.messageEnabled()) {
        debug.message("SAML2MetaSecurityUtils.sign:", xmlse);
      }
    }

    return doc;
  }
  /**
   * This method processes TARGET and SAMLResponse info from the request, validates the
   * response/assertion(s), then redirects user to the TARGET resource if all are valid.
   *
   * @param request <code>HttpServletRequest</code> instance
   * @param response <code>HttpServletResponse</code> instance
   * @throws ServletException if there is an error.
   * @throws IOException if there is an error.
   */
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    response.setContentType("text/html; charset=UTF-8");

    if ((request == null) || (response == null)) {
      String[] data = {SAMLUtils.bundle.getString("nullInputParameter")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.NULL_PARAMETER, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "nullInputParameter",
          SAMLUtils.bundle.getString("nullInputParameter"));
      return;
    }

    SAMLUtils.checkHTTPContentLength(request);

    // obtain TARGET
    String target = request.getParameter(SAMLConstants.POST_TARGET_PARAM);
    if (target == null || target.length() == 0) {
      String[] data = {SAMLUtils.bundle.getString("missingTargetSite")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.MISSING_TARGET, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "missingTargetSite",
          SAMLUtils.bundle.getString("missingTargetSite"));
      return;
    }

    // obtain SAMLResponse
    String samlResponse = request.getParameter(SAMLConstants.POST_SAML_RESPONSE_PARAM);
    if (samlResponse == null) {
      String[] data = {SAMLUtils.bundle.getString("missingSAMLResponse")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.MISSING_RESPONSE, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "missingSAMLResponse",
          SAMLUtils.bundle.getString("missingSAMLResponse"));
      return;
    }

    // decode the Response
    byte raw[] = null;
    try {
      raw = Base64.decode(samlResponse);
    } catch (Exception e) {
      SAMLUtils.debug.error(
          "SAMLPOSTProfileServlet.doPost: Exception " + "when decoding SAMLResponse:", e);
      SAMLUtils.sendError(
          request,
          response,
          response.SC_INTERNAL_SERVER_ERROR,
          "errorDecodeResponse",
          SAMLUtils.bundle.getString("errorDecodeResponse"));
      return;
    }

    // Get Response back
    Response sResponse = SAMLUtils.getResponse(raw);
    if (sResponse == null) {
      String[] data = {SAMLUtils.bundle.getString("errorObtainResponse")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.RESPONSE_MESSAGE_ERROR, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "errorObtainResponse",
          SAMLUtils.bundle.getString("errorObtainResponse"));
      return;
    }

    if (SAMLUtils.debug.messageEnabled()) {
      SAMLUtils.debug.message("SAMLPOSTProfileServlet.doPost: Received " + sResponse.toString());
    }

    // verify that Response is correct
    StringBuffer requestUrl = request.getRequestURL();
    if (SAMLUtils.debug.messageEnabled()) {
      SAMLUtils.debug.message("SAMLPOSTProfileServlet.doPost: " + "requestUrl=" + requestUrl);
    }
    boolean valid = SAMLUtils.verifyResponse(sResponse, requestUrl.toString(), request);
    if (!valid) {
      String[] data = {SAMLUtils.bundle.getString("invalidResponse")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.INVALID_RESPONSE, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_BAD_REQUEST,
          "invalidResponse",
          SAMLUtils.bundle.getString("invalidResponse"));
      return;
    }

    Map attrMap = null;
    List assertions = null;
    javax.security.auth.Subject authSubject = null;
    try {
      Map sessionAttr = SAMLUtils.processResponse(sResponse, target);
      Object token = SAMLUtils.generateSession(request, response, sessionAttr);
    } catch (Exception ex) {
      SAMLUtils.debug.error("generateSession: ", ex);
      String[] data = {SAMLUtils.bundle.getString("failedCreateSSOToken")};
      LogUtils.error(java.util.logging.Level.INFO, LogUtils.FAILED_TO_CREATE_SSO_TOKEN, data);
      SAMLUtils.sendError(
          request,
          response,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "failedCreateSSOToken",
          ex.getMessage());
      ;
      return;
    }

    if (LogUtils.isAccessLoggable(java.util.logging.Level.FINE)) {
      String[] data = {SAMLUtils.bundle.getString("accessGranted"), new String(raw, "UTF-8")};
      LogUtils.access(java.util.logging.Level.FINE, LogUtils.ACCESS_GRANTED, data);
    } else {
      String[] data = {SAMLUtils.bundle.getString("accessGranted")};
      LogUtils.access(java.util.logging.Level.INFO, LogUtils.ACCESS_GRANTED, data);
    }
    if (SAMLUtils.postYN(target)) {
      if (SAMLUtils.debug.messageEnabled()) {
        SAMLUtils.debug.message("POST to target:" + target);
      }
      SAMLUtils.postToTarget(response, assertions, target, attrMap);
    } else {
      response.setHeader("Location", target);
      response.sendRedirect(target);
    }
  }