eu.europa.ec.markt.dss.signature.xades.XAdESProfileT.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.ec.markt.dss.signature.xades.XAdESProfileT.java

Source

/*
 * DSS - Digital Signature Services
 *
 * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
 *
 * Developed by: 2011 ARHS Developments S.A. (rue Nicolas Bov 2B, L-1253 Luxembourg) http://www.arhs-developments.com
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
 * the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * DSS 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
 * "DSS - Digital Signature Services".  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.europa.ec.markt.dss.signature.xades;

import eu.europa.ec.markt.dss.ConfigurationException;
import eu.europa.ec.markt.dss.ConfigurationException.MSG;
import eu.europa.ec.markt.dss.DigestAlgorithm;
import eu.europa.ec.markt.dss.signature.Document;
import eu.europa.ec.markt.dss.signature.InMemoryDocument;
import eu.europa.ec.markt.dss.signature.SignatureExtension;
import eu.europa.ec.markt.dss.signature.SignatureFormat;
import eu.europa.ec.markt.dss.signature.SignatureParameters;
import eu.europa.ec.markt.dss.validation.tsp.TSPSource;
import eu.europa.ec.markt.dss.validation.xades.XAdESSignature;
import eu.europa.ec.markt.tsl.jaxb.xades.EncapsulatedPKIDataType;
import eu.europa.ec.markt.tsl.jaxb.xades.ObjectFactory;
import eu.europa.ec.markt.tsl.jaxb.xades.UnsignedPropertiesType;
import eu.europa.ec.markt.tsl.jaxb.xades.UnsignedSignaturePropertiesType;
import eu.europa.ec.markt.tsl.jaxb.xades.XAdESTimeStampType;
import eu.europa.ec.markt.tsl.jaxb.xmldsig.CanonicalizationMethodType;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.codec.binary.Hex;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.bouncycastle.tsp.TimeStampResponse;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.SAXException;

/**
 * -T profile of XAdES signature
 * 
 * 
 * @version $Revision: 1867 $ - $Date: 2013-04-08 13:44:56 +0200 (Mon, 08 Apr 2013) $
 */

public class XAdESProfileT implements SignatureExtension {

    private static final Logger LOG = Logger.getLogger(XAdESProfileT.class.getName());

    private TSPSource tspSource;

    protected Marshaller marshaller;

    protected Unmarshaller unmarshaller;

    protected ObjectFactory xadesObjectFactory = new ObjectFactory();

    private eu.europa.ec.markt.tsl.jaxb.xmldsig.ObjectFactory _xmldsigObjectFactory;

    /**
     * @return the xmldsigObjectFactory
     */
    public eu.europa.ec.markt.tsl.jaxb.xmldsig.ObjectFactory getXmldsigObjectFactory() {
        if (_xmldsigObjectFactory == null) {
            _xmldsigObjectFactory = new eu.europa.ec.markt.tsl.jaxb.xmldsig.ObjectFactory();
        }
        return _xmldsigObjectFactory;
    }

    /**
     * The default constructor for XAdESProfileT.
     * 
     */
    public XAdESProfileT() {
        super();
        Init.init();

        try {
            JAXBContext context = JAXBContext.newInstance(eu.europa.ec.markt.jaxb.xades141.ObjectFactory.class);
            marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            unmarshaller = context.createUnmarshaller();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * @param tspSource the tspSource to set
     */
    public void setTspSource(TSPSource tspSource) {
        this.tspSource = tspSource;
    }

    private XAdESTimeStampType createUnsignedXAdESProperties(byte[] data) throws IOException {

        UnsignedPropertiesType unsigned = xadesObjectFactory.createUnsignedPropertiesType();

        try {
            /* Create a timestamp over the signature value */
            LOG.info("C14n " + Hex.encodeHexString(data));

            MessageDigest digest = MessageDigest.getInstance(DigestAlgorithm.SHA1.getName());
            digest.update(data);
            byte[] digestValue = digest.digest();
            LOG.info("Digest " + Hex.encodeHexString(digestValue));

            XAdESTimeStampType timestamp = createXAdESTimeStamp(DigestAlgorithm.SHA1, digestValue);

            UnsignedSignaturePropertiesType properties = unsigned.getUnsignedSignatureProperties();
            if (properties == null) {
                properties = xadesObjectFactory.createUnsignedSignaturePropertiesType();
                unsigned.setUnsignedSignatureProperties(properties);
            }

            properties.getCounterSignatureOrSignatureTimeStampOrCompleteCertificateRefs()
                    .add(xadesObjectFactory.createSignatureTimeStamp(timestamp));

            return timestamp;

        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException("Cannot find algorithm");
        }

    }

    protected byte[] getC14nValue(Node node) {
        try {
            Canonicalizer c14n = Canonicalizer.getInstance(CanonicalizationMethod.EXCLUSIVE);
            return c14n.canonicalizeSubtree(node);
        } catch (InvalidCanonicalizerException e) {
            throw new RuntimeException("c14n algo error: " + e.getMessage(), e);
        } catch (CanonicalizationException e) {
            throw new RuntimeException("c14n error: " + e.getMessage(), e);
        }
    }

    protected byte[] getC14nValue(List<Node> nodeList) {
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            for (Node node : nodeList) {
                Canonicalizer c14n = Canonicalizer.getInstance(CanonicalizationMethod.EXCLUSIVE);
                buffer.write(c14n.canonicalizeSubtree(node));
            }
            return buffer.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InvalidCanonicalizerException e) {
            throw new RuntimeException("c14n algo error: " + e.getMessage(), e);
        } catch (CanonicalizationException e) {
            throw new RuntimeException("c14n error: " + e.getMessage(), e);
        }
    }

    protected XAdESTimeStampType createXAdESTimeStamp(DigestAlgorithm algorithm, byte[] digest) throws IOException {

        LOG.info("Create timestamp for digest " + new String(Hex.encodeHex(digest)));
        TimeStampResponse resp = tspSource.getTimeStampResponse(algorithm, digest);
        byte[] timeStampToken = resp.getTimeStampToken().getEncoded();

        XAdESTimeStampType xadesTimeStamp = xadesObjectFactory.createXAdESTimeStampType();
        CanonicalizationMethodType c14nMethod = getXmldsigObjectFactory().createCanonicalizationMethodType();
        c14nMethod.setAlgorithm(CanonicalizationMethod.EXCLUSIVE);
        xadesTimeStamp.setCanonicalizationMethod(c14nMethod);
        xadesTimeStamp.setId("time-stamp-" + UUID.randomUUID().toString());

        EncapsulatedPKIDataType encapsulatedTimeStamp = xadesObjectFactory.createEncapsulatedPKIDataType();
        encapsulatedTimeStamp.setValue(timeStampToken);
        encapsulatedTimeStamp.setId("time-stamp-token-" + UUID.randomUUID().toString());
        List<Serializable> timeStampContent = xadesTimeStamp.getEncapsulatedTimeStampOrXMLTimeStamp();
        timeStampContent.add(encapsulatedTimeStamp);

        return xadesTimeStamp;
    }

    private void extendSignatureTag(Element signatureEl, UnsignedPropertiesType unsigned,
            SignatureFormat signatureFormat) throws IOException {

        XAdESSignature signature = new XAdESSignature(signatureEl);
        XAdESTimeStampType signatureTimestamp = createUnsignedXAdESProperties(
                signature.getSignatureTimestampData());

        UnsignedSignaturePropertiesType sp = unsigned.getUnsignedSignatureProperties();
        if (sp == null) {
            sp = xadesObjectFactory.createUnsignedSignaturePropertiesType();
            unsigned.setUnsignedSignatureProperties(sp);
        }

        /* First we count the already existing timestamp */
        List<Object> existingTimestamp = new ArrayList<Object>();
        for (Object o : sp.getCounterSignatureOrSignatureTimeStampOrCompleteCertificateRefs()) {
            if (o instanceof JAXBElement) {
                JAXBElement e = (JAXBElement) o;
                if (e.getName().getLocalPart().equals("SignatureTimeStamp")) {
                    existingTimestamp.add(o);
                }
            }
        }

        /*
         * We add the timestamp only if there is no timestamp or there is one but we goes for a extension of level -T
         * again
         */
        if (existingTimestamp.size() == 0
                || (existingTimestamp.size() > 0 && signatureFormat == SignatureFormat.XAdES_T)) {
            sp.getCounterSignatureOrSignatureTimeStampOrCompleteCertificateRefs()
                    .add(xadesObjectFactory.createSignatureTimeStamp(signatureTimestamp));

            /*
             * for (Object o : existingTimestamp) {
             * sp.getCounterSignatureOrSignatureTimeStampOrCompleteCertificateRefs().remove(o); }
             */

        }

    }

    @SuppressWarnings("unchecked")
    protected void extendSignatureTag(Element signatureEl, Document originalData, SignatureFormat signatureFormat) {

        try {

            Element qualifyingProperties = XMLUtils.getElement(signatureEl,
                    "./ds:Object/xades:QualifyingProperties");
            Element unsignedPropertiesNode = XMLUtils.getElement(qualifyingProperties,
                    "./xades:UnsignedProperties");

            UnsignedPropertiesType unsignedPropertiesType = null;
            if (unsignedPropertiesNode != null) {
                unsignedPropertiesType = ((JAXBElement<UnsignedPropertiesType>) unmarshaller
                        .unmarshal(unsignedPropertiesNode)).getValue();
            } else {
                unsignedPropertiesType = xadesObjectFactory.createUnsignedPropertiesType();
            }

            extendSignatureTag(signatureEl, unsignedPropertiesType, signatureFormat);

            if (unsignedPropertiesNode != null) {
                qualifyingProperties.removeChild(unsignedPropertiesNode);
            }
            marshaller.marshal(xadesObjectFactory.createUnsignedProperties(unsignedPropertiesType),
                    qualifyingProperties);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);

        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Document extendSignatures(Document document, Document originalData, SignatureParameters parameters)
            throws IOException {
        InputStream input = document.openStream();

        if (this.tspSource == null) {
            throw new ConfigurationException(MSG.CONFIGURE_TSP_SERVER);
        }

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            org.w3c.dom.Document doc = db.parse(input);

            NodeList signatureNodeList = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
            if (signatureNodeList.getLength() == 0) {
                throw new RuntimeException(
                        "Impossible to perform the extension of the signature, the document is not signed.");
            }
            for (int i = 0; i < signatureNodeList.getLength(); i++) {
                Element signatureEl = (Element) signatureNodeList.item(i);
                extendSignatureTag(signatureEl, originalData, parameters.getSignatureFormat());
            }

            DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
            DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
            LSSerializer writer = impl.createLSSerializer();

            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            LSOutput output = impl.createLSOutput();
            output.setByteStream(buffer);
            writer.write(doc, output);

            return new InMemoryDocument(buffer.toByteArray());

        } catch (ParserConfigurationException ex) {
            throw new RuntimeException(ex);
        } catch (SAXException e) {
            throw new IOException("Cannot parse document", e);
        } catch (ClassCastException e) {
            throw new IOException("Cannot save document", e);
        } catch (ClassNotFoundException e) {
            throw new IOException("Cannot save document", e);
        } catch (InstantiationException e) {
            throw new IOException("Cannot save document", e);
        } catch (IllegalAccessException e) {
            throw new IOException("Cannot save document", e);
        } finally {
            if (input != null) {
                input.close();
            }
        }

    }

    @Override
    public Document extendSignature(Object signatureId, Document document, Document originalData,
            SignatureParameters parameters) throws IOException {
        InputStream input = document.openStream();

        if (this.tspSource == null) {
            throw new ConfigurationException(MSG.CONFIGURE_TSP_SERVER);
        }

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            org.w3c.dom.Document doc = db.parse(input);

            NodeList signatureNodeList = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
            if (signatureNodeList.getLength() == 0) {
                throw new RuntimeException(
                        "Impossible to perform the extension of the signature, the document is not signed.");
            }
            for (int i = 0; i < signatureNodeList.getLength(); i++) {
                Element signatureEl = (Element) signatureNodeList.item(i);
                String sid = signatureEl.getAttribute("Id");
                if (signatureId.equals(sid)) {
                    extendSignatureTag(signatureEl, originalData, parameters.getSignatureFormat());
                }
            }

            DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
            DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
            LSSerializer writer = impl.createLSSerializer();

            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            LSOutput output = impl.createLSOutput();
            output.setByteStream(buffer);
            writer.write(doc, output);

            return new InMemoryDocument(buffer.toByteArray());

        } catch (ParserConfigurationException ex) {
            throw new RuntimeException(ex);
        } catch (SAXException e) {
            throw new IOException("Cannot parse document", e);
        } catch (ClassCastException e) {
            throw new IOException("Cannot save document", e);
        } catch (ClassNotFoundException e) {
            throw new IOException("Cannot save document", e);
        } catch (InstantiationException e) {
            throw new IOException("Cannot save document", e);
        } catch (IllegalAccessException e) {
            throw new IOException("Cannot save document", e);
        } finally {
            if (input != null) {
                input.close();
            }
        }

    }

}