/**
   * Turn a KeyInfo into an EncryptedKey collection.
   *
   * @param keyInfo KeyInfo to process
   * @param limit depth of references to follow
   * @return encrypted keys
   */
  @Nonnull
  protected Iterable<EncryptedKey> resolveKeyInfo(@Nullable final KeyInfo keyInfo, int limit) {
    List<EncryptedKey> resolvedEncKeys = new ArrayList<>();

    if (keyInfo == null) {
      return resolvedEncKeys;
    }

    // The first time in, we don't directly resolve any keys, only references.
    // After that, we always start by looking inline.
    if (limit < depthLimit) {
      for (final EncryptedKey encKey : keyInfo.getEncryptedKeys()) {
        if (matchRecipient(encKey.getRecipient())) {
          resolvedEncKeys.add(encKey);
        }
      }
    }

    if (limit == 0) {
      log.info("Reached depth limit for KeyInfoReferences");
    } else {
      for (final KeyInfoReference ref : keyInfo.getKeyInfoReferences()) {
        for (final EncryptedKey encKey : resolveKeyInfo(dereferenceURI(ref), limit - 1)) {
          resolvedEncKeys.add(encKey);
        }
      }
    }

    return resolvedEncKeys;
  }
  /**
   * Checks whether the supplied public key matches one of the keys in the given KeyInfo.
   *
   * <p>Evaluates both {@link KeyValue} and {@link DEREncodedKeyValue} children of the KeyInfo.
   *
   * <p>Matches are performed using Java <code>equals()</code> against {@link PublicKey}s decoded
   * from the KeyInfo data.
   *
   * @param key public key presenter of the assertion
   * @param keyInfo key info from subject confirmation of the assertion
   * @return true if the public key in the certificate matches one of the key values in the key
   *     info, false otherwise
   * @throws AssertionValidationException thrown if there is a problem matching the key value
   */
  protected boolean matchesKeyValue(@Nullable final PublicKey key, @Nonnull final KeyInfo keyInfo)
      throws AssertionValidationException {

    if (key == null) {
      log.debug("Presenter PublicKey was null, skipping KeyValue match");
      return false;
    }

    if (matchesKeyValue(key, keyInfo.getKeyValues())) {
      return true;
    }

    if (matchesDEREncodedKeyValue(key, keyInfo.getDEREncodedKeyValues())) {
      return true;
    }

    log.debug(
        "Failed to match either a KeyInfo KeyValue or DEREncodedKeyValue against supplied PublicKey param");
    return false;
  }
  /**
   * Checks whether the presenter's certificate matches a certificate described by the X509Data
   * within the KeyInfo.
   *
   * <p>Matches are performed using Java <code>equals()</code> against {@link X509Certificate}s
   * decoded from the KeyInfo data.
   *
   * @param cert certificate of the presenter of the assertion
   * @param keyInfo key info from subject confirmation of the assertion
   * @return true if the presenter's certificate matches the key described by an X509Data within the
   *     KeyInfo, false otherwise.
   * @throws AssertionValidationException thrown if there is a problem matching the certificate
   */
  protected boolean matchesX509Certificate(
      @Nullable final X509Certificate cert, @Nonnull final KeyInfo keyInfo)
      throws AssertionValidationException {
    if (cert == null) {
      log.debug("Presenter X509Certificate was null, skipping certificate match");
      return false;
    }

    List<X509Data> x509Datas = keyInfo.getX509Datas();
    if (x509Datas == null || x509Datas.isEmpty()) {
      log.debug("KeyInfo contained no X509Data children, skipping certificate match");
      return false;
    }

    log.debug("Attempting to match KeyInfo X509Data to supplied X509Certificate param");

    List<org.opensaml.xmlsec.signature.X509Certificate> xmlCertificates;
    for (X509Data data : x509Datas) {
      xmlCertificates = data.getX509Certificates();
      if (xmlCertificates == null || xmlCertificates.isEmpty()) {
        log.debug("X509Data contained no X509Certificate children, skipping certificate match");
        continue;
      }

      for (org.opensaml.xmlsec.signature.X509Certificate xmlCertificate : xmlCertificates) {
        try {
          X509Certificate kiCert = KeyInfoSupport.getCertificate(xmlCertificate);
          if (Objects.equals(cert, kiCert)) {
            log.debug("Matched X509Certificate");
            return true;
          }
        } catch (CertificateException e) {
          log.warn("KeyInfo contained Certificate value that can not be parsed", e);
        }
      }
    }

    log.debug("Failed to match a KeyInfo X509Data against supplied X509Certificate param");
    return false;
  }