/*
 *  Name:    $Id: X509CertificateValidator.java,v 1.2 2010/01/07 14:57:51 t724w Exp $
 *
 *  Copyright 2009 Bundesamt für Informatik und Telekommunikation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package ch.admin.bit.edec.dsig.validator;

import ch.admin.bit.edec.dsig.context.SystemContext;

import java.security.cert.*;
import java.util.*;

/**
 * Simple X.509 Certificate Validator. Default to not check against CRL and the
 * current date.
 *
 * @author t724w (mru)
 */
public class X509CertificateValidator {

  private Date date = new Date();

  private X509CRL x509crl = null;

  private final X509Certificate caCert;

  private final SystemContext context;

  public X509CertificateValidator(SystemContext context, X509Certificate caCert) {
    this.caCert = caCert;
    this.context = context;
  }

  /**
   * Set the CRL which should be used
   *
   * @param x509crl
   */
  public void setX509crl(X509CRL x509crl) {
    this.x509crl = x509crl;
  }

  /**
   * Set the date for which should be validate if a given certificate is
   * valid.
   *
   * @param date
   */
  public void setDate(Date date) {
    this.date = date;
  }

  /**
   * Check if a certifcate is valid
   *
   * @param cert
   * @return return true if the certifcate is valid or false when the
   * certifcate is not valid
   */
  public boolean isValid(X509Certificate cert) {
    try {
      CertificateFactory cf = context.getProviderSelector().getCertificateFactory();

      List<Object> list = new ArrayList<>();
      list.add(caCert);
      list.add(cert);

      CertStoreParameters certStoreParams = new CollectionCertStoreParameters(list);

      CertStore certStore = context.getProviderSelector().getCertStore(certStoreParams);

      // trusted ca cert
      Set<TrustAnchor> trust = Collections.singleton(new TrustAnchor(caCert, null));
      PKIXParameters params = new PKIXParameters(trust);

      // set CRL checking
      params.setRevocationEnabled(false);
      params.addCertStore(certStore);

      // sets the time for which the validity of the certification path should be determined
      params.setDate(date);

      List<X509Certificate> certChain = new ArrayList<>();
      // add certificate
      certChain.add(cert);

      CertPath certPath = cf.generateCertPath(certChain);
      CertPathValidator certPathValidator = context.getProviderSelector().getCertPathValidator();
      certPathValidator.validate(certPath, params);

      // do manual CRL check when CRL was set
      if (x509crl != null) {
        X509CRLValidator validator = new X509CRLValidator(x509crl);
        return validator.isValid(cert);
      }
      else {
        // no CRL check but certificate path and date is valid
        return true;
      }

    }
    catch (Exception e) {
      context.getOutputterStrategy().write("certificate error: " + e.getMessage());
      return false;
    }
  }

}
