/*
 *  Name:    $Id: XMLSignatureValidator.java,v 1.3 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 org.w3c.dom.Node;

import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import java.security.Provider;
import java.security.PublicKey;
import java.util.Iterator;

/**
 * Validates a digital signature.
 *
 * @author t724w (mru)
 */
public class XMLSignatureValidator {

  private PublicKey publicKey;
  private SystemContext systemContext;

  /**
   * @param context
   * @param publicKey
   */
  public XMLSignatureValidator(SystemContext context, PublicKey publicKey) {
    this.publicKey = publicKey;
    this.systemContext = context;
  }

  /**
   * Check if a Signature element is valid
   *
   * @param dsSignature
   * @return true if the xml signature is valid or false when the signature is
   * invalid
   */
  public boolean isValid(Node dsSignature) {

    try {
      DOMValidateContext context = new DOMValidateContext(publicKey, dsSignature);

      // use default provider or a given by the property jsr105Provider
      String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

      XMLSignatureFactory factory = XMLSignatureFactory
          .getInstance("DOM", (Provider) Class.forName(providerName).newInstance());

      //      XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");

      XMLSignature signature = factory.unmarshalXMLSignature(context);

      // Check core validation status
      boolean coreValidity = signature.validate(context);

      if (coreValidity == false) {

        boolean sv = signature.getSignatureValue().validate(context);

        String status = sv ? "OK" : "NOT OK";
        systemContext.getOutputterStrategy().write("check signature value element: " + status);

        // Check the validation status of each Reference
        Iterator<Reference> i = signature.getSignedInfo().getReferences().iterator();

        for (int j = 0; i.hasNext(); j++) {
          // signature was not valid so try to find out which reference was invalid
          Reference ref = i.next();
          boolean refValid = ref.validate(context);
          String id = ref.getURI();

          status = refValid ? "OK" : "NOT OK";
          systemContext.getOutputterStrategy()
              .write("check signature reference (" + j + ") with URI [" + id + "] validation status: " + status);

        }
      }

      return coreValidity;
    }

    catch (Exception e) {
      // signature error
      systemContext.getOutputterStrategy().write("signature error: " + e.getMessage());
      return false;
    }
  }

}
