/**
   * Checks to see whether the schema type of the subject confirmation data, if present, is the
   * required {@link KeyInfoConfirmationDataType#TYPE_NAME}.
   *
   * @param confirmation subject confirmation bearing the confirmation data to be checked
   * @return true if the confirmation data's schema type is correct, false otherwise
   * @throws AssertionValidationException thrown if there is a problem validating the confirmation
   *     data type
   */
  protected boolean isValidConfirmationDataType(@Nonnull final SubjectConfirmation confirmation)
      throws AssertionValidationException {
    QName confirmationDataSchemaType = confirmation.getSubjectConfirmationData().getSchemaType();
    if (confirmationDataSchemaType != null
        && !confirmationDataSchemaType.equals(KeyInfoConfirmationDataType.TYPE_NAME)) {
      log.debug(
          "SubjectConfirmationData xsi:type was non-null and did not match {}",
          KeyInfoConfirmationDataType.TYPE_NAME);
      return false;
    }

    log.debug(
        "SubjectConfirmationData xsi:type was either null or matched {}",
        KeyInfoConfirmationDataType.TYPE_NAME);

    return true;
  }
  /**
   * Extracts the {@link KeyInfo}s from the given subject confirmation data.
   *
   * @param confirmation subject confirmation data
   * @param assertion assertion bearing the subject to be confirmed
   * @param context current message processing context
   * @return list of key informations available in the subject confirmation data, never null
   * @throws AssertionValidationException if there is a problem processing the SubjectConfirmation
   */
  @Nonnull
  protected List<KeyInfo> getSubjectConfirmationKeyInformation(
      @Nonnull final SubjectConfirmation confirmation,
      @Nonnull final Assertion assertion,
      @Nonnull final ValidationContext context)
      throws AssertionValidationException {

    SubjectConfirmationData confirmationData = confirmation.getSubjectConfirmationData();

    List<KeyInfo> keyInfos = new LazyList<>();
    for (XMLObject object : confirmationData.getUnknownXMLObjects(KeyInfo.DEFAULT_ELEMENT_NAME)) {
      if (object != null) {
        keyInfos.add((KeyInfo) object);
      }
    }

    log.debug("Found '{}' KeyInfo children of SubjectConfirmationData", keyInfos.size());
    return keyInfos;
  }
  /** {@inheritDoc} */
  @Nonnull
  protected ValidationResult doValidate(
      @Nonnull final SubjectConfirmation confirmation,
      @Nonnull final Assertion assertion,
      @Nonnull final ValidationContext context)
      throws AssertionValidationException {

    if (!Objects.equals(confirmation.getMethod(), SubjectConfirmation.METHOD_HOLDER_OF_KEY)) {
      return ValidationResult.INDETERMINATE;
    }

    log.debug("Attempting holder-of-key subject confirmation");
    if (!isValidConfirmationDataType(confirmation)) {
      String msg =
          String.format(
              "Subject confirmation data is not of type '%s'",
              KeyInfoConfirmationDataType.TYPE_NAME);
      log.debug(msg);
      context.setValidationFailureMessage(msg);
      return ValidationResult.INVALID;
    }

    List<KeyInfo> possibleKeys =
        getSubjectConfirmationKeyInformation(confirmation, assertion, context);
    if (possibleKeys.isEmpty()) {
      String msg =
          String.format(
              "No key information for holder of key subject confirmation in assertion '%s'",
              assertion.getID());
      log.debug(msg);
      context.setValidationFailureMessage(msg);
      return ValidationResult.INVALID;
    }

    Pair<PublicKey, X509Certificate> keyCertPair = null;
    try {
      keyCertPair = getKeyAndCertificate(context);
    } catch (IllegalArgumentException e) {
      log.warn("Problem with the validation context presenter key/cert params: {}", e.getMessage());
      context.setValidationFailureMessage(
          "Unable to obtain presenter key/cert params from validation context");
      return ValidationResult.INDETERMINATE;
    }

    if (keyCertPair.getFirst() == null && keyCertPair.getSecond() == null) {
      log.debug("Neither the presenter's certificate nor its public key were provided");
      context.setValidationFailureMessage(
          "Neither the presenter's certificate nor its public key were provided");
      return ValidationResult.INDETERMINATE;
    }

    for (KeyInfo keyInfo : possibleKeys) {
      if (matchesKeyValue(keyCertPair.getFirst(), keyInfo)) {
        log.debug(
            "Successfully matched public key in subject confirmation data to supplied key param");
        context
            .getDynamicParameters()
            .put(SAML2AssertionValidationParameters.SC_HOK_CONFIRMED_KEYINFO, keyInfo);
        return ValidationResult.VALID;
      } else if (matchesX509Certificate(keyCertPair.getSecond(), keyInfo)) {
        log.debug(
            "Successfully matched certificate in subject confirmation data to supplied cert param");
        context
            .getDynamicParameters()
            .put(SAML2AssertionValidationParameters.SC_HOK_CONFIRMED_KEYINFO, keyInfo);
        return ValidationResult.VALID;
      }
    }

    return ValidationResult.INVALID;
  }