be.fedict.eid.applet.service.signer.odf.ODFSignatureVerifier.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.applet.service.signer.odf.ODFSignatureVerifier.java

Source

/*
 * eID Applet Project.
 * Copyright (C) 2008-2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */
package be.fedict.eid.applet.service.signer.odf;

import java.io.IOException;
import java.io.InputStream;

import java.net.URL;

import java.security.cert.X509Certificate;

import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xml.security.exceptions.XMLSecurityException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;

import be.fedict.eid.applet.service.signer.KeyInfoKeySelector;

/**
 * ODF Signature Verifier.
 * 
 * @author fcorneli
 */
public class ODFSignatureVerifier {

    private static final Log LOG = LogFactory.getLog(ODFSignatureVerifier.class);

    private ODFSignatureVerifier() {
        super();
    }

    /**
     * Checks whether the ODF document available via the given URL has been
     * signed.
     *
     * @param odfUrl
     * @return
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws org.apache.xml.security.signature.XMLSignatureException
     * @throws XMLSecurityException
     * @throws MarshalException
     * @throws XMLSignatureException
     */
    public static boolean hasOdfSignature(URL odfUrl) throws IOException, ParserConfigurationException,
            SAXException, org.apache.xml.security.signature.XMLSignatureException, XMLSecurityException,
            MarshalException, XMLSignatureException {
        List<X509Certificate> signers = getSigners(odfUrl);
        return false == signers.isEmpty();
    }

    /**
     * return list of signers for the document available via the given
     * URL.
     *
     * @param odfUrl
     * @return list of X509 certificates
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws MarshalException
     * @throws XMLSignatureException
     */
    public static List<X509Certificate> getSigners(URL odfUrl) throws IOException, ParserConfigurationException,
            SAXException, MarshalException, XMLSignatureException {
        List<X509Certificate> signers = new LinkedList<X509Certificate>();
        if (null == odfUrl) {
            throw new IllegalArgumentException("odfUrl is null");
        }
        ZipInputStream odfZipInputStream = new ZipInputStream(odfUrl.openStream());
        ZipEntry zipEntry;

        while (null != (zipEntry = odfZipInputStream.getNextEntry())) {
            if (ODFUtil.isSignatureFile(zipEntry)) {
                Document documentSignatures = ODFUtil.loadDocument(odfZipInputStream);
                NodeList signatureNodeList = documentSignatures.getElementsByTagNameNS(XMLSignature.XMLNS,
                        "Signature");

                for (int idx = 0; idx < signatureNodeList.getLength(); idx++) {
                    Node signatureNode = signatureNodeList.item(idx);
                    X509Certificate signer = getVerifiedSignatureSigner(odfUrl, signatureNode);
                    if (null == signer) {
                        LOG.debug("JSR105 says invalid signature");
                    } else {
                        signers.add(signer);
                    }
                }
                return signers;
            }
        }
        LOG.debug("no signature file present");
        return signers;
    }

    private static X509Certificate getVerifiedSignatureSigner(URL odfUrl, Node signatureNode)
            throws MarshalException, XMLSignatureException {
        if (null == odfUrl) {
            throw new IllegalArgumentException("odfUrl is null");
        }
        KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
        DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, signatureNode);
        ODFURIDereferencer dereferencer = new ODFURIDereferencer(odfUrl);
        domValidateContext.setURIDereferencer(dereferencer);

        XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance();
        LOG.debug("java version: " + System.getProperty("java.version"));
        /*
         * Requires Java 6u10 because of a bug. See also:
         * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6696582
         */
        XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
        boolean validity = xmlSignature.validate(domValidateContext);
        if (false == validity) {
            LOG.debug("invalid signature");
            return null;
        }
        // TODO: check what has been signed.

        X509Certificate signer = keySelector.getCertificate();
        if (null == signer) {
            throw new IllegalStateException("signer X509 certificate is null");
        }
        LOG.debug("signer: " + signer.getSubjectX500Principal());
        return signer;
    }

    /**
     * Checks whether the document available on the given URL is an ODF document
     * or not.
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static boolean isODF(URL url) throws IOException {
        InputStream resStream = ODFUtil.findDataInputStream(url.openStream(), ODFUtil.MIMETYPE_FILE);
        if (null == resStream) {
            /*
             * Some ODF implementations do not include a mimetype file
             * TODO: try harder to check if a file is ODF or not
             */
            LOG.debug("mimetype stream not found in ODF package");
            return false;
        }
        String mimetypeContent = IOUtils.toString(resStream);
        return mimetypeContent.startsWith(ODFUtil.MIMETYPE_START);
    }
}