/*
 *  Name:    $Id: SignatureValidator.java,v 1.8 2010/01/07 14:57:52 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;

import ch.admin.bit.edec.dsig.builder.X509CRLBuilder;
import ch.admin.bit.edec.dsig.builder.X509CertificateBuilder;
import ch.admin.bit.edec.dsig.context.ProviderSelectorImpl;
import ch.admin.bit.edec.dsig.context.SystemContext;
import ch.admin.bit.edec.dsig.context.SystemContextImpl;
import ch.admin.bit.edec.dsig.out.OutputterStrategy;
import ch.admin.bit.edec.dsig.out.StringBufferOutputter;
import ch.admin.bit.edec.dsig.util.XMLUtil;
import ch.admin.bit.edec.dsig.validator.X509CertificateValidator;
import ch.admin.bit.edec.dsig.validator.XMLSignatureValidator;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Date;

/**
 * API which can be used in a Java application
 *
 * @author t724w (mru)
 */
public class SignatureValidator {

  private X509CRL x509crl;
  private X509Certificate ca;

  private final SystemContext systemContext;

  /**
   * @param caPath
   * @param crlPath
   * @throws IllegalArgumentException
   */
  public SignatureValidator(String caPath, String crlPath) {
    // create system context
    systemContext = createSystemContext();

    // crl is optional
    if (crlPath != null) {
      X509CRLBuilder crlBuilder = new X509CRLBuilder(systemContext);
      try {
        x509crl = crlBuilder.createCRLFromPath(crlPath);
      }
      catch (Exception ex) {
        // crl file not found or wrong provider
        throw new IllegalArgumentException(ex);
      }
    }

    X509CertificateBuilder certificateBuilder = new X509CertificateBuilder(systemContext);

    try {
      ca = certificateBuilder.createCertificateFromPath(caPath);
    }
    catch (Exception ex) {
      // certifcate not found or wrong provider
      throw new IllegalArgumentException(ex);
    }

  }

  /**
   * @param certificate
   * @param x509crl
   */
  public SignatureValidator(X509Certificate certificate, X509CRL x509crl) {
    systemContext = createSystemContext();
    this.ca = certificate;
    this.x509crl = x509crl;
  }

  public ValidationResult validate(Document xmlDoc) {

    ValidationResultImpl result = new ValidationResultImpl();
    OutputterStrategy out = systemContext.getOutputterStrategy();

    try {
      out.write("signature validator version: " + Environment.getVersion());
      out.write("system jre version: " + System.getProperty("java.version"));
      out.write("system jre vendor: " + System.getProperty("java.vendor"));
      out.write("system jre home: " + System.getProperty("java.home"));
      out.write("system os: " + System.getProperty("os.name"));
      out.write("system os version: " + System.getProperty("os.version"));
      out.write("system java endorsed dirs: " + System.getProperty("java.endorsed.dirs"));

      X509CertificateBuilder certificateBuilder = new X509CertificateBuilder(systemContext);

      // extract certificate with public key from xml document
      X509Certificate cert = certificateBuilder.createCertificate(XMLUtil.extractX509Token(xmlDoc).getTextContent());

      // systemContext.getOutputterStrategy().write("Cert:", cert);

      Node dsSignature = XMLUtil.extractDSignature(xmlDoc);

      X509CertificateValidator certificateValidator = new X509CertificateValidator(systemContext, ca);
      certificateValidator.setX509crl(x509crl);
      certificateValidator.setDate(new Date());

      // systemContext.getOutputterStrategy().write("Crl:", cert);

      if (certificateValidator.isValid(cert)) {
        result.setValid(true);

      }
      else {
        result.setValid(false);
        String msg = "Certificate Error\n" + out.toString();
        result.setMessage(msg);
        return result;
      }

      XMLSignatureValidator signatureValidator = new XMLSignatureValidator(systemContext, cert.getPublicKey());

      if (signatureValidator.isValid(dsSignature)) {
        result.setValid(true);
      }
      else {
        result.setValid(false);
        String msg = "Signature Error\n" + out.toString();
        result.setMessage(msg);
        return result;
      }

    }
    catch (Exception ex) {
      result.setValid(false);
      result.setException(ex);
      String msg = "Exception:\n" + ex.getMessage() + "\n" + out.toString();
      result.setMessage(msg);

    }

    return result;
  }

  private SystemContext createSystemContext() {
    SystemContextImpl contextImpl = new SystemContextImpl();
    contextImpl.setOutputterStrategy(new StringBufferOutputter());
    contextImpl.setProviderSelector(new ProviderSelectorImpl());
    return contextImpl;
  }

}
