All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.boot.comp.data.XMLValidator Maven / Gradle / Ivy

/*
 * @(#) $Id:  $
 *
 * Copyright 2009/2010 by sIT Solutions,
 * A-1110 Wien, Geiselbergstr.21-25.
 * All rights reserved.
 *
 */
package at.spardat.xma.boot.comp.data;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.Properties;

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 javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;

import at.spardat.xma.boot.Statics;
import at.spardat.xma.boot.comp.DTDStatics;
import at.spardat.xma.boot.logger.ILogger;
import at.spardat.xma.boot.logger.LogLevel;

public class XMLValidator {
    
    private final ILogger log;
    private final boolean enforceSignature;
    private final boolean enableSignature;
    private EntityResolver entityResolver;
    private PublicKey publicKey;

    /**
     * @param log Logger
     * @param enableSignature enable signature validation
     * @param enforceSignature enforce signature validation
     */
    protected XMLValidator(PublicKey publicKey, ILogger log, boolean enableSignature, boolean enforceSignature) {
        this.publicKey = publicKey;
        this.log = log;
        this.enableSignature = enableSignature;
        this.enforceSignature = enforceSignature;
    }
    
    public static XMLValidator create(ILogger log, Properties props) {
        if( log == null ) throw new NullPointerException();
        
        boolean enforce = false;
        boolean enable = true;
        
        String certificateAlias = Statics.strSignatureCertalias;
        if (props != null) {
            String strEnforceSignature = (String)props.get( Statics.CFG_PROP_XML_SIGNATURE_ENFORCE );
            if( strEnforceSignature != null ) {
                enforce = strEnforceSignature != null && Boolean.valueOf(strEnforceSignature).booleanValue();
            }
            if (!enforce) {
                String strAllowSignature = (String)props.get( Statics.CFG_PROP_XML_SIGNATURE );
                if( strAllowSignature!=null ) {
                   enable = Boolean.valueOf(strAllowSignature).booleanValue();
                }
            }
            String strCertificateAlias = (String)props.get( Statics.CFG_PROP_XML_SIGNATURE_CERTALIAS );
            if (strCertificateAlias != null) {
                certificateAlias = strCertificateAlias;
            }
        }
        PublicKey publicKey = null;
        try {
            publicKey = getPublicKey(log, certificateAlias);
        } catch (Exception e) {
            log.log(LogLevel.WARNING, "Error loading certificate for XML signature validation.", e);
        }
        if (publicKey == null && !enforce) {
            log.log(LogLevel.CONFIG, "Unable to load certificate for XML signature validation, alias={0}", certificateAlias);
            return null;
        }
        return new XMLValidator(publicKey, log, enable, enforce);
    }

    public void setEntityResolver(EntityResolver entityResolver) {
        this.entityResolver = entityResolver;
    }

    private static PublicKey getPublicKey(ILogger log, String certificateAlias) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        String keyStore = System.getProperty("javax.net.ssl.trustStore");
        if (keyStore == null) {
            log.log(LogLevel.CONFIG, "No trust store defined. XML signature validation not available.");
            return null;
        }
        String keyStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
        if (keyStorePassword == null) {
            keyStorePassword = "changeit";
        }
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(keyStore), keyStorePassword.toCharArray());
        
        Certificate certificate = ks.getCertificate(certificateAlias);
        return certificate == null ? null : certificate.getPublicKey();
      }
      
      protected boolean validateSignature(InputStream is) throws Exception {
          if (!enableSignature) {
              return true;
          }
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          dbf.setNamespaceAware(true);
          DocumentBuilder docBuilder = dbf.newDocumentBuilder();
          if (entityResolver != null) {
              docBuilder.setEntityResolver(entityResolver);
          }
          Document doc = docBuilder.parse(is);

          // Find Signature element
          NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, DTDStatics.SIGNATURE);
          if (nl.getLength() == 0) {
              log.log(LogLevel.CONFIG, "Cannot find Signature element. Not verifying document.");
              if (enforceSignature) {
                  logDocument(doc);
                  return false;
              }
              return true;
          }

          if (publicKey == null) {
              log.log(LogLevel.SEVERE, "No key available to verify signature. Not verifying document.");
              return false;
          }
          // Create a DOM XMLSignatureFactory that will be used to unmarshal the document containing the XMLSignature
          XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); //$NON-NLS-1$

          DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));
          XMLSignature signature = fac.unmarshalXMLSignature(valContext);

          // Validate the XMLSignature (generated above)
          boolean coreValidity = signature.validate(valContext);

          // Check core validation status
          if (coreValidity == false) {
              log.log(LogLevel.SEVERE, "Signature failed core validation");
              boolean sv = signature.getSignatureValue().validate(valContext);
              log.log(LogLevel.FINE, "signature validation status: " + sv);
              // check the validation status of each Reference
              Iterator i = signature.getSignedInfo().getReferences().iterator();
              for (int j = 0; i.hasNext(); j++) {
                  boolean refValid = i.next().validate(valContext);
                  log.log(LogLevel.FINE, "ref[" + j + "] validity status: " + refValid);
              }
              logDocument(doc);
              return false;
          } else {
              log.log(LogLevel.INFO, "Signature passed core validation");
              return true;
          }
      }

    public void logDocument(Document doc) throws IOException, TransformerException {
        if (log.getLevel().intValue() <= LogLevel.FINE.intValue()) {
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              printDocument(doc, out);
              log.log(LogLevel.FINE, out.toString());
          }
    }
    
    public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        transformer.transform(new DOMSource(doc), 
             new StreamResult(new OutputStreamWriter(out, "UTF-8")));
    }        
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy