public void processLogoutResponse(SAMLMessageContext context)
      throws SAMLException, org.opensaml.xml.security.SecurityException, ValidationException {

    SAMLObject message = context.getInboundSAMLMessage();

    // Verify type
    if (!(message instanceof LogoutResponse)) {
      log.debug("Received response is not of a Response object type");
      throw new SAMLException("Error validating SAML response");
    }
    LogoutResponse response = (LogoutResponse) message;

    // Make sure request was authenticated if required, authentication is done as part of the
    // binding processing
    if (!context.isInboundSAMLMessageAuthenticated()
        && context.getLocalExtendedMetadata().isRequireLogoutResponseSigned()) {
      log.debug(
          "Logout Response object is required to be signed by the entity policy: "
              + context.getInboundSAMLMessageId());
      throw new SAMLException("Logout Response object is required to be signed");
    }

    // Verify issue time
    DateTime time = response.getIssueInstant();
    if (!isDateTimeSkewValid(getResponseSkew(), time)) {
      log.debug("Response issue time is either too old or with date in the future");
      throw new SAMLException("Error validating SAML response");
    }

    // Verify response to field if present, set request if correct
    // The inResponseTo field is optional, SAML 2.0 Core, 1542
    SAMLMessageStorage messageStorage = context.getMessageStorage();
    if (messageStorage != null && response.getInResponseTo() != null) {
      XMLObject xmlObject = messageStorage.retrieveMessage(response.getInResponseTo());
      if (xmlObject == null) {
        log.debug(
            "InResponseToField doesn't correspond to sent message", response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      } else if (xmlObject instanceof LogoutRequest) {
        // Expected
      } else {
        log.debug(
            "Sent request was of different type then received response",
            response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify destination
    if (response.getDestination() != null) {
      SPSSODescriptor localDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata();

      // Check if destination is correct on this SP
      List<SingleLogoutService> services = localDescriptor.getSingleLogoutServices();
      boolean found = false;
      for (SingleLogoutService service : services) {
        if (response.getDestination().equals(service.getLocation())
            && context.getInboundSAMLBinding().equals(service.getBinding())) {
          found = true;
          break;
        }
      }
      if (!found) {
        log.debug(
            "Destination of the response was not the expected value", response.getDestination());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify issuer
    if (response.getIssuer() != null) {
      Issuer issuer = response.getIssuer();
      verifyIssuer(issuer, context);
    }

    // Verify status
    String statusCode = response.getStatus().getStatusCode().getValue();
    if (StatusCode.SUCCESS_URI.equals(statusCode)) {
      log.trace("Single Logout was successful");
    } else if (StatusCode.PARTIAL_LOGOUT_URI.equals(statusCode)) {
      log.trace("Single Logout was partially successful");
    } else {
      String[] logMessage = new String[2];
      logMessage[0] = response.getStatus().getStatusCode().getValue();
      StatusMessage message1 = response.getStatus().getStatusMessage();
      if (message1 != null) {
        logMessage[1] = message1.getMessage();
      }
      log.warn("Received LogoutResponse has invalid status code", logMessage);
    }
  }
  /**
   * @param SAMLToken - SAML Assertion XML
   * @param contextAttributeName - SAML Attribute name containing Context data
   * @param verfiySignature
   * @return a base64 encoded string containing the context
   * @throws SAMLValidationException - if validation fails
   */
  public static List<AttributeStatement> validate(byte[] SAMLToken, SAMLConfiguration samlConfig)
      throws SAMLValidationException {
    if (logger.isDebugEnabled()) {
      logger.debug("validate(byte[], SAMLConfiguration) - start"); // $NON-NLS-1$
    }

    Decrypter samlDecrypter = samlConfig.getSamlDecrypter();
    SignatureTrustEngine sigTrustEngine = samlConfig.getTrustEngine();
    String myURI = samlConfig.getMyURI();
    MessageReplayRule replayRule = samlConfig.getReplayRule();

    BasicParserPool ppMgr = new BasicParserPool();
    ppMgr.setNamespaceAware(true);

    VerifySignatureType verfiySignature = samlConfig.getVerifySignature();

    List<AttributeStatement> attrStatements = null;
    if (SAMLToken != null) {
      try {

        InputStream in = new ByteArrayInputStream(SAMLToken);
        Document SAMLDoc;
        try {
          SAMLDoc = ppMgr.parse(in);
        } catch (XMLParserException e) {
          logger.error(
              "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
              e); //$NON-NLS-1$

          throw createSAMLValidationException("Error parsing SAML XML", true, e);
        }
        Element responseElement = SAMLDoc.getDocumentElement();

        SAMLType samlType = SAMLType.Response;
        if (responseElement == null) {
          throw createSAMLValidationException(
              "Missing SAML Encrypted Assertion or Assertion or Assertion Response", true);
        }
        if (!"Response".equals(responseElement.getLocalName())
            || !SAMLConstants.SAML20P_NS.equals(responseElement.getNamespaceURI())) {
          if (!"Assertion".equals(responseElement.getLocalName())
              || !SAMLConstants.SAML20_NS.equals(responseElement.getNamespaceURI())) {
            if (!"EncryptedAssertion".equals(responseElement.getLocalName())
                || !SAMLConstants.SAML20_NS.equals(responseElement.getNamespaceURI())) {
              throw createSAMLValidationException(
                  "Missing or invalid SAML Encrypted Assertion or Assertion or Assertion Response",
                  true);
            } else {
              samlType = SAMLType.EncryptedAssertion;
            }
          } else {
            samlType = SAMLType.Assertion;
          }
        } else {
          samlType = SAMLType.Response;
        }

        if (samlType == SAMLType.Response) {
          // Unmarshall SAML Assertion Response into an OpenSAML Java object.
          UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
          Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement);
          Response samlResponse;
          try {
            samlResponse = (Response) unmarshaller.unmarshall(responseElement);
          } catch (UnmarshallingException e) {
            logger.error(
                "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                e); //$NON-NLS-1$

            throw createSAMLValidationException(
                "Error in unmarshalling SAML XML Document", true, e);
          }

          // Check the replay attack for Response
          if (replayRule != null) {
            BasicSAMLMessageContext messageContext = new BasicSAMLMessageContext();
            // messageContext.setInboundMessage(samlResponse);
            if (samlResponse.getIssuer() != null)
              messageContext.setInboundMessageIssuer(samlResponse.getIssuer().getValue());
            messageContext.setInboundSAMLMessageId(samlResponse.getID());

            try {
              replayRule.evaluate(messageContext);
            } catch (SecurityPolicyException e) {
              logger.error(
                  "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                  e); //$NON-NLS-1$

              throw createSAMLValidationException("Possible Replay Attack for Response", false, e);
            }
          }

          // Validate the status code
          if (!StatusCode.SUCCESS_URI.equals(samlResponse.getStatus().getStatusCode().getValue())) {
            throw createSAMLValidationException("Invalid Status Code.", true);
          }

          boolean responseSignatureVerified =
              validateResponseSignature(samlResponse, sigTrustEngine, verfiySignature);

          // Get first encrypted Assertion, if not present get first unencrypted assertion
          int assertCount = samlResponse.getEncryptedAssertions().size();
          if (assertCount == 0) {
            assertCount = samlResponse.getAssertions().size();
            if (assertCount == 0) {
              throw createSAMLValidationException(
                  "No Assertion or EncryptedAssertion found in received response", true);
            } else {
              for (Assertion samlAssertion : samlResponse.getAssertions()) {
                attrStatements =
                    validateAssertion(
                        samlAssertion,
                        sigTrustEngine,
                        myURI,
                        replayRule,
                        verfiySignature,
                        responseSignatureVerified);
                break; // Use the first only
              }
            }
          } else {
            for (EncryptedAssertion samlEncryptedAssertion :
                samlResponse.getEncryptedAssertions()) {

              // Decryption

              Assertion samlAssertion = null;
              try {
                samlAssertion = samlDecrypter.decrypt(samlEncryptedAssertion);
              } catch (DecryptionException e) {
                logger.error(
                    "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                    e); //$NON-NLS-1$

                throw createSAMLValidationException(
                    "Error Decrypting Received Encrypted Assertion", true, e);
              }
              attrStatements =
                  validateAssertion(
                      samlAssertion,
                      sigTrustEngine,
                      myURI,
                      replayRule,
                      verfiySignature,
                      responseSignatureVerified);

              break; // Use the first only
            }
          }
        } else if (samlType == SAMLType.EncryptedAssertion) {
          // Unmarshall SAML Encrypted Assertion into an OpenSAML Java object.
          UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
          Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement);
          EncryptedAssertion encryptedAssertion = null;
          try {
            encryptedAssertion = (EncryptedAssertion) unmarshaller.unmarshall(responseElement);
          } catch (UnmarshallingException e) {
            logger.error(
                "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                e); //$NON-NLS-1$

            throw createSAMLValidationException(
                "Error in unmarshalling SAML XML Document", true, e);
          }

          boolean responseSignatureVerified = false;

          // Decryption

          Assertion samlAssertion = null;
          try {
            samlAssertion = samlDecrypter.decrypt(encryptedAssertion);
          } catch (DecryptionException e) {
            logger.error(
                "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                e); //$NON-NLS-1$

            throw createSAMLValidationException(
                "Error Decrypting Received Encrypted Assertion", true, e);
          }

          attrStatements =
              validateAssertion(
                  samlAssertion,
                  sigTrustEngine,
                  myURI,
                  replayRule,
                  verfiySignature,
                  responseSignatureVerified);
        } else if (samlType == SAMLType.Assertion) {
          // Unmarshall SAML Assertion  into an OpenSAML Java object.
          UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
          Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseElement);
          Assertion samlAssertion = null;
          try {
            samlAssertion = (Assertion) unmarshaller.unmarshall(responseElement);
          } catch (UnmarshallingException e) {
            logger.error(
                "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType)",
                e); //$NON-NLS-1$

            throw createSAMLValidationException(
                "Error in unmarshalling SAML XML Document", true, e);
          }

          boolean responseSignatureVerified = false;

          attrStatements =
              validateAssertion(
                  samlAssertion,
                  sigTrustEngine,
                  myURI,
                  replayRule,
                  verfiySignature,
                  responseSignatureVerified);
        }
      } catch (SAMLValidationException e) {
        throw e;
      }
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          "validate(byte[], String, Decrypter, SignatureTrustEngine, String, MessageReplayRule, ParserPool, VerifySignatureType) - end"); //$NON-NLS-1$
    }
    return attrStatements;
  }