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

io.rubrica.sign.ooxml.OOXMLUtil Maven / Gradle / Ivy

/*
 * Copyright 2009-2017 Rubrica
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program.  If not, see .
 */

package io.rubrica.sign.ooxml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import io.rubrica.util.Utils;
import io.rubrica.xml.FileUtils;

/** Clase con métodos de utilidad para las firmas OOXML. */
final class OOXMLUtil {

	/** Tipo de relación correspondiente a una firma OOXML. */
	private static final String OOXML_SIGNATURE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";

	/**
	 * Tipo de relación correspondiente a la relación de firmas
	 * OOXML.
	 */
	private static final String OOXML_SIGNATURE_ORIGIN_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";

	private static final Logger logger = Logger.getLogger(OOXMLUtil.class.getName());

	/**
	 * Cuenta el número de firmas del documento OOXML.
	 * 
	 * @param ooxmlFile
	 *            Documento OOXML.
	 * @return Número de firma del documento OOXML.
	 * @throws ParserConfigurationException
	 *             Cuando hay problemas con el analizador SAX.
	 * @throws IOException
	 *             Cuando hay incosistencias de formato OOXML en los XML
	 *             internos del fichero.
	 * @throws SAXException
	 *             Cuando alguno de los XML internos del fichero no está
	 *             bien formado.
	 */
	static int countOOXMLSignatures(final byte[] ooxmlFile)
			throws IOException, SAXException, ParserConfigurationException {
		final Relationship[] rels = getOOXMLSignaturesRelationships(ooxmlFile);
		return rels == null ? 0 : rels.length;
	}

	/**
	 * Cuenta el número de firmas del documento OOXML. Si se produce
	 * algún error durante el análisis del fichero, se
	 * devolverá 0.
	 * 
	 * @param ooxmlFile
	 *            Documento OOXML.
	 * @return Número de firma del documento OOXML.
	 * @throws ParserConfigurationException
	 *             Cuando hay problemas con el analizador SAX.
	 * @throws IOException
	 *             Cuando hay incosistencias de formato OOXML en los XML
	 *             internos del fichero.
	 * @throws SAXException
	 *             Cuando alguno de los XML internos del fichero no está
	 *             bien formado.
	 */
	private static Relationship[] getOOXMLSignaturesRelationships(byte[] ooxmlFile)
			throws IOException, SAXException, ParserConfigurationException {
		List relations = new ArrayList<>();

		try (ZipFile zipFile = FileUtils.createTempZipFile(ooxmlFile);) {
			// Comprobamos si existe la relacion de firmas del documento
			ZipEntry relsEntry = getSignaturesRelsEntry(zipFile);

			// Si no existe el fichero, el documento no contiene firmas
			if (relsEntry == null) {
				return new Relationship[0];
			}

			// Analizamos el fichero de relaciones
			RelationshipsParser parser = new RelationshipsParser(zipFile.getInputStream(relsEntry));

			// Contamos las relaciones de firma
			for (Relationship rel : parser.getRelationships()) {
				if (OOXML_SIGNATURE_RELATIONSHIP_TYPE.equals(rel.getType())) {
					relations.add(rel);
				}
			}
		}

		return relations.toArray(new Relationship[0]);
	}

	/**
	 * Recupera las firmas XMLdSig empotradas en el documento OOXML.
	 * 
	 * @param ooxmlFile
	 *            Documento OOXML.
	 * @return Firmas empotradas en el documento.
	 * @throws ParserConfigurationException
	 *             Cuando hay problemas con el analizador SAX.
	 * @throws IOException
	 *             Cuando hay incosistencias de formato OOXML en los XML
	 *             internos del fichero.
	 * @throws SAXException
	 *             Cuando alguno de los XML internos del fichero no está
	 *             bien formado.
	 */
	static byte[][] getOOXMLSignatures(byte[] ooxmlFile)
			throws IOException, SAXException, ParserConfigurationException {

		List relations = new ArrayList<>();

		try (ZipFile zipFile = FileUtils.createTempZipFile(ooxmlFile);) {
			// Comprobamos si existe la relacion de firmas del documento
			ZipEntry relsEntry = getSignaturesRelsEntry(zipFile);

			// Si no existe el fichero, el documento no contiene firmas
			if (relsEntry == null) {
				return new byte[0][];
			}

			// Analizamos el fichero de relaciones
			RelationshipsParser parser = new RelationshipsParser(zipFile.getInputStream(relsEntry));

			// Contamos las relaciones de firma
			for (Relationship rel : parser.getRelationships()) {
				if (OOXML_SIGNATURE_RELATIONSHIP_TYPE.equals(rel.getType())) {
					// Comprobamos que exista el firma referenciada
					String target = rel.getTarget();

					ZipEntry signEntry = zipFile.getEntry("_xmlsignatures/" + target);
					if (signEntry == null) {
						signEntry = zipFile.getEntry("_xmlsignatures\\" + target);
					}

					if (signEntry == null) {
						logger.severe("El documento OOXML no contiene las firmas declaradas");
						zipFile.close();
						return new byte[0][];
					}

					// Guardamos la firma
					try {
						relations.add(Utils.getDataFromInputStream(zipFile.getInputStream(signEntry)));
					} catch (Exception e) {
						logger.severe("No se pudo leer una de las firmas del documento OOXML: " + e);
						zipFile.close();
						return new byte[0][];
					}
				}
			}
		}

		return relations.toArray(new byte[0][]);
	}

	/**
	 * Recupera la entrada con la relación de firmas del documento.
	 * 
	 * @param ooxmlZipFile
	 *            Fichero OOXML.
	 * @return Entrada con la relación de firmas.
	 */
	private static ZipEntry getSignaturesRelsEntry(ZipFile ooxmlZipFile) {
		ZipEntry relsEntry = ooxmlZipFile.getEntry("_rels/.rels");

		if (relsEntry == null) {
			relsEntry = ooxmlZipFile.getEntry("_rels\\.rels");
		}

		// Analizamos el fichero de relaciones
		RelationshipsParser parser;

		try {
			parser = new RelationshipsParser(ooxmlZipFile.getInputStream(relsEntry));
		} catch (Exception e) {
			logger.severe("Error en la lectura del OOXML: " + e);
			return null;
		}

		ZipEntry signsEntry = null;
		for (Relationship rel : parser.getRelationships()) {
			if (OOXML_SIGNATURE_ORIGIN_RELATIONSHIP_TYPE.equals(rel.getType())) {
				String middleTarget = rel.getTarget().substring(0, "_xmlsignatures".length() + 1);
				String target = rel.getTarget().substring("_xmlsignatures".length() + 1);
				signsEntry = ooxmlZipFile.getEntry(middleTarget + "_rels/" + target + ".rels");
				if (signsEntry == null) {
					signsEntry = ooxmlZipFile.getEntry(middleTarget + "_rels\\" + target + ".rels");
				}
				break;
			}
		}

		return signsEntry;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy