/** {@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;
  }