/* * 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.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.xml.crypto.URIDereferencer; 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.utils.Constants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; import be.fedict.eid.applet.service.signer.AbstractXmlSignatureService; import be.fedict.eid.applet.service.signer.DigestAlgo; import be.fedict.eid.applet.service.signer.facets.KeyInfoSignatureFacet; import be.fedict.eid.applet.service.signer.facets.XAdESSignatureFacet; /** * Signature Service implementation for OpenDocument format signatures. * * The signatures created with this class are accepted as valid signature within * OpenOffice.org 3.x. They probably don't get accepted by older OOo versions. * * @see http://wiki.services.openoffice.org/wiki/Security/Digital_Signatures * * @author fcorneli * */ abstract public class AbstractODFSignatureService extends AbstractXmlSignatureService { private static final Log LOG = LogFactory.getLog(AbstractODFSignatureService.class); private final XAdESSignatureFacet xadesSignatureFacet; public AbstractODFSignatureService(DigestAlgo digestAlgo) { super(digestAlgo); addSignatureFacet(new ODFSignatureFacet(this, getSignatureDigestAlgorithm())); addSignatureFacet(new OpenOfficeSignatureFacet(getSignatureDigestAlgorithm())); this.xadesSignatureFacet = new XAdESSignatureFacet(getSignatureDigestAlgorithm()); addSignatureFacet(this.xadesSignatureFacet); addSignatureFacet(new KeyInfoSignatureFacet(false, true, false)); } /** * Gives back the used XAdES signature facet. Allows for extra configuration * of the XAdES elements. * * @return */ protected XAdESSignatureFacet getXAdESSignatureFacet() { return this.xadesSignatureFacet; } /** * Returns the URL of the ODF to be signed. * * @return */ abstract protected URL getOpenDocumentURL(); @Override protected final URIDereferencer getURIDereferencer() { URL odfUrl = getOpenDocumentURL(); return new ODFURIDereferencer(odfUrl); } @Override protected String getSignatureDescription() { return "ODF Document"; } @Override protected final OutputStream getSignedDocumentOutputStream() { LOG.debug("get signed document output stream"); /* * Create each time a new object; we want an empty output stream to * start with. */ OutputStream signedDocumentOutputStream = new ODFSignedDocumentOutputStream(); return signedDocumentOutputStream; } private class ODFSignedDocumentOutputStream extends ByteArrayOutputStream { @Override public void close() throws IOException { LOG.debug("close ODF signed document output stream"); super.close(); outputSignedOpenDocument(this.toByteArray()); } } private void outputSignedOpenDocument(byte[] signatureData) throws IOException { LOG.debug("output signed open document"); OutputStream signedOdfOutputStream = getSignedOpenDocumentOutputStream(); if (null == signedOdfOutputStream) { throw new NullPointerException("signedOpenDocumentOutputStream is null"); } /* * Copy the original ODF content to the signed ODF package. */ ZipOutputStream zipOutputStream = new ZipOutputStream(signedOdfOutputStream); ZipInputStream zipInputStream = new ZipInputStream(this.getOpenDocumentURL().openStream()); ZipEntry zipEntry; while (null != (zipEntry = zipInputStream.getNextEntry())) { if (!zipEntry.getName().equals(ODFUtil.SIGNATURE_FILE)) { zipOutputStream.putNextEntry(zipEntry); IOUtils.copy(zipInputStream, zipOutputStream); } } zipInputStream.close(); /* * Add the ODF XML signature file to the signed ODF package. */ zipEntry = new ZipEntry(ODFUtil.SIGNATURE_FILE); zipOutputStream.putNextEntry(zipEntry); IOUtils.write(signatureData, zipOutputStream); zipOutputStream.close(); } /** * The output stream to which to write the signed ODF file. * * @return */ abstract protected OutputStream getSignedOpenDocumentOutputStream(); public final String getFilesDigestAlgorithm() { /* * No local files to digest. */ return null; } @Override protected final Document getEnvelopingDocument() throws ParserConfigurationException, IOException, SAXException { Document document = getODFSignatureDocument(); if (null != document) { return document; } document = ODFUtil.getNewDocument(); Element rootElement = document.createElementNS(ODFUtil.SIGNATURE_NS, ODFUtil.SIGNATURE_ELEMENT); rootElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", ODFUtil.SIGNATURE_NS); document.appendChild(rootElement); return document; } /** * Get the XML signature file from the ODF package * * @return * @throws IOException * @throws ParserConfigurationException * @throws SAXException */ private Document getODFSignatureDocument() throws IOException, ParserConfigurationException, SAXException { URL odfUrl = this.getOpenDocumentURL(); InputStream inputStream = ODFUtil.findDataInputStream(odfUrl.openStream(), ODFUtil.SIGNATURE_FILE); if (null != inputStream) { return ODFUtil.loadDocument(inputStream); } return null; } }