@SuppressWarnings("static-method") @Test public void firmaODF() throws Exception { final PrivateKeyEntry pke = loadKeyEntry(CERT_PATH, CERT_PASS, CERT_ALIAS); final AOODFSigner signer = new AOODFSigner(); for (final Properties extraParams : CONFIGS) { for (int i = 0; i < DATA_FILES.length; i++) { if (DATA.get(i) != null) { System.out.println("Se va a firma el documento " + DATA_FILES[i] + (extraParams.size() == 0 ? "" : " con las opciones:")); for (final String key : extraParams.keySet().toArray(new String[0])) { System.out.println(key + ": " + extraParams.getProperty(key)); } final File tempFile = File.createTempFile("odfSign", "." + DATA_FILES[i]); try ( final FileOutputStream fos = new FileOutputStream(tempFile); ) { final byte[] result = signer.sign( DATA.get(i), "SHA512withRSA", pke.getPrivateKey(), pke.getCertificateChain(), extraParams ); Assert.assertNotNull(result); fos.write(result); fos.flush(); } System.out.println("Temporal para comprobacion manual: " + tempFile.getAbsolutePath()); } } } }
@Override public byte[] sign(final byte[] data, final String algorithm, final PrivateKey key, final java.security.cert.Certificate[] certChain, final Properties xParams) throws AOException { if (!AOSignConstants.SIGN_ALGORITHM_SHA1WITHRSA.equals(algorithm)) { LOGGER.warning( "Se ha indicado '" + algorithm + "' como algoritmo de firma, pero se usara 'SHA1withRSA' por necesidades del formato ODF" ); } final Properties extraParams = xParams != null ? xParams : new Properties(); final String digestMethodAlgorithm = extraParams.getProperty(AOODFExtraParams.REFERENCES_DIGEST_METHOD, DEFAULT_DIGEST_METHOD); final boolean useOpenOffice31Mode = Boolean.parseBoolean(extraParams.getProperty(AOODFExtraParams.USE_OPEN_OFFICE_31_MODE)); if (!isValidDataFile(data)) { throw new AOFormatFileException("Los datos introducidos no se corresponden con un documento ODF"); } String fullPath = MANIFEST_PATH; boolean isCofirm = false; try { final File zipFile = File.createTempFile("sign", ".zip"); try ( final FileOutputStream fos = new FileOutputStream(zipFile); ) { fos.write(data); fos.flush(); } zipFile.deleteOnExit(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try ( final ZipFile zf = new ZipFile(zipFile); ) { final byte[] manifestData; try ( final InputStream manifest = zf.getInputStream(zf.getEntry(fullPath)); ) { manifestData = AOUtil.getDataFromInputStream(manifest); } final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final Document docManifest = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(manifestData)); final Element rootManifest = docManifest.getDocumentElement(); final NodeList listFileEntry = rootManifest.getElementsByTagName("manifest:file-entry"); final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_METHOD_ALGORITHM_NAME); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo " + DIGEST_METHOD_ALGORITHM_NAME + ": " + e, e ); } final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); final DigestMethod dm; try { dm = fac.newDigestMethod(digestMethodAlgorithm, null); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo: " + digestMethodAlgorithm, e ); } final List<Transform> transformList = new ArrayList<>(1); transformList.add( fac.newTransform( Canonicalizer.CANONICAL_XML, (TransformParameterSpec) null ) ); final List<Reference> referenceList = new ArrayList<>(); if (!useOpenOffice31Mode) { try ( final InputStream zis = zf.getInputStream( zf.getEntry("mimetype") ) ) { referenceList.add( fac.newReference( "mimetype", dm, null, null, null, md.digest( AOUtil.getDataFromInputStream( zis ) ) ) ); } referenceList.add( fac.newReference( MANIFEST_PATH, dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse( new ByteArrayInputStream(manifestData) ).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ) ); } Reference reference; for (int i = 0; i < listFileEntry.getLength(); i++) { fullPath = ((Element) listFileEntry.item(i)).getAttribute("manifest:full-path"); if (!fullPath.endsWith("/")) { if (fullPath.equals("content.xml") || fullPath.equals("meta.xml") || fullPath.equals("styles.xml") || fullPath.equals("settings.xml")) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse(zis).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ); } } else { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, null, null, null, md.digest(AOUtil.getDataFromInputStream(zis)) ); } } if (!fullPath.equals(SIGNATURES_PATH)) { referenceList.add(reference); } else { isCofirm = true; } } } if (!isCofirm && zf.getEntry(SIGNATURES_PATH) != null) { isCofirm = true; } final Document docSignatures; final Element rootSignatures; if (isCofirm) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(SIGNATURES_PATH)) ) { docSignatures = dbf.newDocumentBuilder().parse( zis ); } rootSignatures = docSignatures.getDocumentElement(); } else { docSignatures = dbf.newDocumentBuilder().newDocument(); rootSignatures = docSignatures.createElement("document-signatures"); rootSignatures.setAttribute("xmlns", OPENOFFICE); docSignatures.appendChild(rootSignatures); if (useOpenOffice31Mode) { final Element nodeDocumentSignatures = docManifest.createElement("manifest:file-entry"); nodeDocumentSignatures.setAttribute("manifest:media-type", ""); nodeDocumentSignatures.setAttribute("manifest:full-path", SIGNATURES_PATH); rootManifest.appendChild(nodeDocumentSignatures); final Element nodeMetaInf = docManifest.createElement("manifest:file-entry"); nodeMetaInf.setAttribute("manifest:media-type", ""); nodeMetaInf.setAttribute("manifest:full-path", "META-INF/"); rootManifest.appendChild(nodeMetaInf); } } final String signatureId = UUID.randomUUID().toString(); final String signaturePropertyId = UUID.randomUUID().toString(); referenceList.add(fac.newReference("#" + signaturePropertyId, dm)); final String contentString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SS") .format(new Date()); final Document dateDocument = dbf.newDocumentBuilder().parse( new ByteArrayInputStream( ("<dc:date xmlns:dc=\"http: .getBytes())); final Element content = dateDocument.getDocumentElement(); final List<XMLStructure> contentList = new ArrayList<>(); contentList.add(new DOMStructure(content)); final List<SignatureProperty> spList = new ArrayList<>(); spList.add(fac.newSignatureProperty(contentList, "#" + signatureId, signaturePropertyId)); final List<SignatureProperties> spsList = new ArrayList<>(); spsList.add(fac.newSignatureProperties(spList, null)); final List<XMLObject> objectList = new ArrayList<>(); objectList.add(fac.newXMLObject(spsList, null, null, null)); final KeyInfoFactory kif = fac.getKeyInfoFactory(); final List<Object> x509Content = new ArrayList<>(); final X509Certificate cert = (X509Certificate) certChain[0]; x509Content.add(cert.getSubjectX500Principal().getName()); x509Content.add(cert); final javax.xml.crypto.dsig.XMLSignature xmlSignature = fac.newXMLSignature( fac.newSignedInfo( fac.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), referenceList ), kif.newKeyInfo( Collections.singletonList(kif.newX509Data(x509Content)), null ), objectList, signatureId, null ); final DOMSignContext context = new DOMSignContext(key, rootSignatures); xmlSignature.sign( context ); try ( final ZipOutputStream zos = new ZipOutputStream(baos); ) { final Enumeration<? extends ZipEntry> e = zf.entries(); ZipEntry ze; ZipEntry zeOut; while (e.hasMoreElements()) { ze = e.nextElement(); zeOut = new ZipEntry(ze.getName()); if (!ze.getName().equals(SIGNATURES_PATH) && !ze.getName().equals(MANIFEST_PATH)) { zos.putNextEntry(zeOut); try ( final InputStream zis = zf.getInputStream(ze) ) { zos.write( AOUtil.getDataFromInputStream( zis ) ); } } } zos.putNextEntry(new ZipEntry(SIGNATURES_PATH)); final ByteArrayOutputStream baosXML = new ByteArrayOutputStream(); writeXML(baosXML, rootSignatures, false); zos.write(baosXML.toByteArray()); zos.closeEntry(); zos.putNextEntry(new ZipEntry(MANIFEST_PATH)); final ByteArrayOutputStream baosManifest = new ByteArrayOutputStream(); writeXML(baosManifest, rootManifest, false); zos.write(baosManifest.toByteArray()); zos.closeEntry(); } } return baos.toByteArray(); } catch (final SAXException saxex) { throw new AOFormatFileException("Estructura de archivo no valida '" + fullPath + "': " + saxex); } catch (final Exception e) { if ("es.gob.jmulticard.CancelledOperationException".equals(e.getClass().getName())) { throw new AOCancelledOperationException(); } throw new AOException("No ha sido posible generar la firma ODF: " + e, e); } }
AOODFSigner implements AOSigner { @Override public byte[] sign(final byte[] data, final String algorithm, final PrivateKey key, final java.security.cert.Certificate[] certChain, final Properties xParams) throws AOException { if (!AOSignConstants.SIGN_ALGORITHM_SHA1WITHRSA.equals(algorithm)) { LOGGER.warning( "Se ha indicado '" + algorithm + "' como algoritmo de firma, pero se usara 'SHA1withRSA' por necesidades del formato ODF" ); } final Properties extraParams = xParams != null ? xParams : new Properties(); final String digestMethodAlgorithm = extraParams.getProperty(AOODFExtraParams.REFERENCES_DIGEST_METHOD, DEFAULT_DIGEST_METHOD); final boolean useOpenOffice31Mode = Boolean.parseBoolean(extraParams.getProperty(AOODFExtraParams.USE_OPEN_OFFICE_31_MODE)); if (!isValidDataFile(data)) { throw new AOFormatFileException("Los datos introducidos no se corresponden con un documento ODF"); } String fullPath = MANIFEST_PATH; boolean isCofirm = false; try { final File zipFile = File.createTempFile("sign", ".zip"); try ( final FileOutputStream fos = new FileOutputStream(zipFile); ) { fos.write(data); fos.flush(); } zipFile.deleteOnExit(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try ( final ZipFile zf = new ZipFile(zipFile); ) { final byte[] manifestData; try ( final InputStream manifest = zf.getInputStream(zf.getEntry(fullPath)); ) { manifestData = AOUtil.getDataFromInputStream(manifest); } final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final Document docManifest = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(manifestData)); final Element rootManifest = docManifest.getDocumentElement(); final NodeList listFileEntry = rootManifest.getElementsByTagName("manifest:file-entry"); final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_METHOD_ALGORITHM_NAME); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo " + DIGEST_METHOD_ALGORITHM_NAME + ": " + e, e ); } final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); final DigestMethod dm; try { dm = fac.newDigestMethod(digestMethodAlgorithm, null); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo: " + digestMethodAlgorithm, e ); } final List<Transform> transformList = new ArrayList<>(1); transformList.add( fac.newTransform( Canonicalizer.CANONICAL_XML, (TransformParameterSpec) null ) ); final List<Reference> referenceList = new ArrayList<>(); if (!useOpenOffice31Mode) { try ( final InputStream zis = zf.getInputStream( zf.getEntry("mimetype") ) ) { referenceList.add( fac.newReference( "mimetype", dm, null, null, null, md.digest( AOUtil.getDataFromInputStream( zis ) ) ) ); } referenceList.add( fac.newReference( MANIFEST_PATH, dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse( new ByteArrayInputStream(manifestData) ).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ) ); } Reference reference; for (int i = 0; i < listFileEntry.getLength(); i++) { fullPath = ((Element) listFileEntry.item(i)).getAttribute("manifest:full-path"); if (!fullPath.endsWith("/")) { if (fullPath.equals("content.xml") || fullPath.equals("meta.xml") || fullPath.equals("styles.xml") || fullPath.equals("settings.xml")) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse(zis).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ); } } else { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, null, null, null, md.digest(AOUtil.getDataFromInputStream(zis)) ); } } if (!fullPath.equals(SIGNATURES_PATH)) { referenceList.add(reference); } else { isCofirm = true; } } } if (!isCofirm && zf.getEntry(SIGNATURES_PATH) != null) { isCofirm = true; } final Document docSignatures; final Element rootSignatures; if (isCofirm) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(SIGNATURES_PATH)) ) { docSignatures = dbf.newDocumentBuilder().parse( zis ); } rootSignatures = docSignatures.getDocumentElement(); } else { docSignatures = dbf.newDocumentBuilder().newDocument(); rootSignatures = docSignatures.createElement("document-signatures"); rootSignatures.setAttribute("xmlns", OPENOFFICE); docSignatures.appendChild(rootSignatures); if (useOpenOffice31Mode) { final Element nodeDocumentSignatures = docManifest.createElement("manifest:file-entry"); nodeDocumentSignatures.setAttribute("manifest:media-type", ""); nodeDocumentSignatures.setAttribute("manifest:full-path", SIGNATURES_PATH); rootManifest.appendChild(nodeDocumentSignatures); final Element nodeMetaInf = docManifest.createElement("manifest:file-entry"); nodeMetaInf.setAttribute("manifest:media-type", ""); nodeMetaInf.setAttribute("manifest:full-path", "META-INF/"); rootManifest.appendChild(nodeMetaInf); } } final String signatureId = UUID.randomUUID().toString(); final String signaturePropertyId = UUID.randomUUID().toString(); referenceList.add(fac.newReference("#" + signaturePropertyId, dm)); final String contentString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SS") .format(new Date()); final Document dateDocument = dbf.newDocumentBuilder().parse( new ByteArrayInputStream( ("<dc:date xmlns:dc=\"http: .getBytes())); final Element content = dateDocument.getDocumentElement(); final List<XMLStructure> contentList = new ArrayList<>(); contentList.add(new DOMStructure(content)); final List<SignatureProperty> spList = new ArrayList<>(); spList.add(fac.newSignatureProperty(contentList, "#" + signatureId, signaturePropertyId)); final List<SignatureProperties> spsList = new ArrayList<>(); spsList.add(fac.newSignatureProperties(spList, null)); final List<XMLObject> objectList = new ArrayList<>(); objectList.add(fac.newXMLObject(spsList, null, null, null)); final KeyInfoFactory kif = fac.getKeyInfoFactory(); final List<Object> x509Content = new ArrayList<>(); final X509Certificate cert = (X509Certificate) certChain[0]; x509Content.add(cert.getSubjectX500Principal().getName()); x509Content.add(cert); final javax.xml.crypto.dsig.XMLSignature xmlSignature = fac.newXMLSignature( fac.newSignedInfo( fac.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), referenceList ), kif.newKeyInfo( Collections.singletonList(kif.newX509Data(x509Content)), null ), objectList, signatureId, null ); final DOMSignContext context = new DOMSignContext(key, rootSignatures); xmlSignature.sign( context ); try ( final ZipOutputStream zos = new ZipOutputStream(baos); ) { final Enumeration<? extends ZipEntry> e = zf.entries(); ZipEntry ze; ZipEntry zeOut; while (e.hasMoreElements()) { ze = e.nextElement(); zeOut = new ZipEntry(ze.getName()); if (!ze.getName().equals(SIGNATURES_PATH) && !ze.getName().equals(MANIFEST_PATH)) { zos.putNextEntry(zeOut); try ( final InputStream zis = zf.getInputStream(ze) ) { zos.write( AOUtil.getDataFromInputStream( zis ) ); } } } zos.putNextEntry(new ZipEntry(SIGNATURES_PATH)); final ByteArrayOutputStream baosXML = new ByteArrayOutputStream(); writeXML(baosXML, rootSignatures, false); zos.write(baosXML.toByteArray()); zos.closeEntry(); zos.putNextEntry(new ZipEntry(MANIFEST_PATH)); final ByteArrayOutputStream baosManifest = new ByteArrayOutputStream(); writeXML(baosManifest, rootManifest, false); zos.write(baosManifest.toByteArray()); zos.closeEntry(); } } return baos.toByteArray(); } catch (final SAXException saxex) { throw new AOFormatFileException("Estructura de archivo no valida '" + fullPath + "': " + saxex); } catch (final Exception e) { if ("es.gob.jmulticard.CancelledOperationException".equals(e.getClass().getName())) { throw new AOCancelledOperationException(); } throw new AOException("No ha sido posible generar la firma ODF: " + e, e); } } }
AOODFSigner implements AOSigner { @Override public byte[] sign(final byte[] data, final String algorithm, final PrivateKey key, final java.security.cert.Certificate[] certChain, final Properties xParams) throws AOException { if (!AOSignConstants.SIGN_ALGORITHM_SHA1WITHRSA.equals(algorithm)) { LOGGER.warning( "Se ha indicado '" + algorithm + "' como algoritmo de firma, pero se usara 'SHA1withRSA' por necesidades del formato ODF" ); } final Properties extraParams = xParams != null ? xParams : new Properties(); final String digestMethodAlgorithm = extraParams.getProperty(AOODFExtraParams.REFERENCES_DIGEST_METHOD, DEFAULT_DIGEST_METHOD); final boolean useOpenOffice31Mode = Boolean.parseBoolean(extraParams.getProperty(AOODFExtraParams.USE_OPEN_OFFICE_31_MODE)); if (!isValidDataFile(data)) { throw new AOFormatFileException("Los datos introducidos no se corresponden con un documento ODF"); } String fullPath = MANIFEST_PATH; boolean isCofirm = false; try { final File zipFile = File.createTempFile("sign", ".zip"); try ( final FileOutputStream fos = new FileOutputStream(zipFile); ) { fos.write(data); fos.flush(); } zipFile.deleteOnExit(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try ( final ZipFile zf = new ZipFile(zipFile); ) { final byte[] manifestData; try ( final InputStream manifest = zf.getInputStream(zf.getEntry(fullPath)); ) { manifestData = AOUtil.getDataFromInputStream(manifest); } final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final Document docManifest = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(manifestData)); final Element rootManifest = docManifest.getDocumentElement(); final NodeList listFileEntry = rootManifest.getElementsByTagName("manifest:file-entry"); final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_METHOD_ALGORITHM_NAME); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo " + DIGEST_METHOD_ALGORITHM_NAME + ": " + e, e ); } final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); final DigestMethod dm; try { dm = fac.newDigestMethod(digestMethodAlgorithm, null); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo: " + digestMethodAlgorithm, e ); } final List<Transform> transformList = new ArrayList<>(1); transformList.add( fac.newTransform( Canonicalizer.CANONICAL_XML, (TransformParameterSpec) null ) ); final List<Reference> referenceList = new ArrayList<>(); if (!useOpenOffice31Mode) { try ( final InputStream zis = zf.getInputStream( zf.getEntry("mimetype") ) ) { referenceList.add( fac.newReference( "mimetype", dm, null, null, null, md.digest( AOUtil.getDataFromInputStream( zis ) ) ) ); } referenceList.add( fac.newReference( MANIFEST_PATH, dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse( new ByteArrayInputStream(manifestData) ).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ) ); } Reference reference; for (int i = 0; i < listFileEntry.getLength(); i++) { fullPath = ((Element) listFileEntry.item(i)).getAttribute("manifest:full-path"); if (!fullPath.endsWith("/")) { if (fullPath.equals("content.xml") || fullPath.equals("meta.xml") || fullPath.equals("styles.xml") || fullPath.equals("settings.xml")) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse(zis).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ); } } else { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, null, null, null, md.digest(AOUtil.getDataFromInputStream(zis)) ); } } if (!fullPath.equals(SIGNATURES_PATH)) { referenceList.add(reference); } else { isCofirm = true; } } } if (!isCofirm && zf.getEntry(SIGNATURES_PATH) != null) { isCofirm = true; } final Document docSignatures; final Element rootSignatures; if (isCofirm) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(SIGNATURES_PATH)) ) { docSignatures = dbf.newDocumentBuilder().parse( zis ); } rootSignatures = docSignatures.getDocumentElement(); } else { docSignatures = dbf.newDocumentBuilder().newDocument(); rootSignatures = docSignatures.createElement("document-signatures"); rootSignatures.setAttribute("xmlns", OPENOFFICE); docSignatures.appendChild(rootSignatures); if (useOpenOffice31Mode) { final Element nodeDocumentSignatures = docManifest.createElement("manifest:file-entry"); nodeDocumentSignatures.setAttribute("manifest:media-type", ""); nodeDocumentSignatures.setAttribute("manifest:full-path", SIGNATURES_PATH); rootManifest.appendChild(nodeDocumentSignatures); final Element nodeMetaInf = docManifest.createElement("manifest:file-entry"); nodeMetaInf.setAttribute("manifest:media-type", ""); nodeMetaInf.setAttribute("manifest:full-path", "META-INF/"); rootManifest.appendChild(nodeMetaInf); } } final String signatureId = UUID.randomUUID().toString(); final String signaturePropertyId = UUID.randomUUID().toString(); referenceList.add(fac.newReference("#" + signaturePropertyId, dm)); final String contentString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SS") .format(new Date()); final Document dateDocument = dbf.newDocumentBuilder().parse( new ByteArrayInputStream( ("<dc:date xmlns:dc=\"http: .getBytes())); final Element content = dateDocument.getDocumentElement(); final List<XMLStructure> contentList = new ArrayList<>(); contentList.add(new DOMStructure(content)); final List<SignatureProperty> spList = new ArrayList<>(); spList.add(fac.newSignatureProperty(contentList, "#" + signatureId, signaturePropertyId)); final List<SignatureProperties> spsList = new ArrayList<>(); spsList.add(fac.newSignatureProperties(spList, null)); final List<XMLObject> objectList = new ArrayList<>(); objectList.add(fac.newXMLObject(spsList, null, null, null)); final KeyInfoFactory kif = fac.getKeyInfoFactory(); final List<Object> x509Content = new ArrayList<>(); final X509Certificate cert = (X509Certificate) certChain[0]; x509Content.add(cert.getSubjectX500Principal().getName()); x509Content.add(cert); final javax.xml.crypto.dsig.XMLSignature xmlSignature = fac.newXMLSignature( fac.newSignedInfo( fac.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), referenceList ), kif.newKeyInfo( Collections.singletonList(kif.newX509Data(x509Content)), null ), objectList, signatureId, null ); final DOMSignContext context = new DOMSignContext(key, rootSignatures); xmlSignature.sign( context ); try ( final ZipOutputStream zos = new ZipOutputStream(baos); ) { final Enumeration<? extends ZipEntry> e = zf.entries(); ZipEntry ze; ZipEntry zeOut; while (e.hasMoreElements()) { ze = e.nextElement(); zeOut = new ZipEntry(ze.getName()); if (!ze.getName().equals(SIGNATURES_PATH) && !ze.getName().equals(MANIFEST_PATH)) { zos.putNextEntry(zeOut); try ( final InputStream zis = zf.getInputStream(ze) ) { zos.write( AOUtil.getDataFromInputStream( zis ) ); } } } zos.putNextEntry(new ZipEntry(SIGNATURES_PATH)); final ByteArrayOutputStream baosXML = new ByteArrayOutputStream(); writeXML(baosXML, rootSignatures, false); zos.write(baosXML.toByteArray()); zos.closeEntry(); zos.putNextEntry(new ZipEntry(MANIFEST_PATH)); final ByteArrayOutputStream baosManifest = new ByteArrayOutputStream(); writeXML(baosManifest, rootManifest, false); zos.write(baosManifest.toByteArray()); zos.closeEntry(); } } return baos.toByteArray(); } catch (final SAXException saxex) { throw new AOFormatFileException("Estructura de archivo no valida '" + fullPath + "': " + saxex); } catch (final Exception e) { if ("es.gob.jmulticard.CancelledOperationException".equals(e.getClass().getName())) { throw new AOCancelledOperationException(); } throw new AOException("No ha sido posible generar la firma ODF: " + e, e); } }  }
AOODFSigner implements AOSigner { @Override public byte[] sign(final byte[] data, final String algorithm, final PrivateKey key, final java.security.cert.Certificate[] certChain, final Properties xParams) throws AOException { if (!AOSignConstants.SIGN_ALGORITHM_SHA1WITHRSA.equals(algorithm)) { LOGGER.warning( "Se ha indicado '" + algorithm + "' como algoritmo de firma, pero se usara 'SHA1withRSA' por necesidades del formato ODF" ); } final Properties extraParams = xParams != null ? xParams : new Properties(); final String digestMethodAlgorithm = extraParams.getProperty(AOODFExtraParams.REFERENCES_DIGEST_METHOD, DEFAULT_DIGEST_METHOD); final boolean useOpenOffice31Mode = Boolean.parseBoolean(extraParams.getProperty(AOODFExtraParams.USE_OPEN_OFFICE_31_MODE)); if (!isValidDataFile(data)) { throw new AOFormatFileException("Los datos introducidos no se corresponden con un documento ODF"); } String fullPath = MANIFEST_PATH; boolean isCofirm = false; try { final File zipFile = File.createTempFile("sign", ".zip"); try ( final FileOutputStream fos = new FileOutputStream(zipFile); ) { fos.write(data); fos.flush(); } zipFile.deleteOnExit(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try ( final ZipFile zf = new ZipFile(zipFile); ) { final byte[] manifestData; try ( final InputStream manifest = zf.getInputStream(zf.getEntry(fullPath)); ) { manifestData = AOUtil.getDataFromInputStream(manifest); } final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final Document docManifest = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(manifestData)); final Element rootManifest = docManifest.getDocumentElement(); final NodeList listFileEntry = rootManifest.getElementsByTagName("manifest:file-entry"); final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_METHOD_ALGORITHM_NAME); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo " + DIGEST_METHOD_ALGORITHM_NAME + ": " + e, e ); } final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); final DigestMethod dm; try { dm = fac.newDigestMethod(digestMethodAlgorithm, null); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo: " + digestMethodAlgorithm, e ); } final List<Transform> transformList = new ArrayList<>(1); transformList.add( fac.newTransform( Canonicalizer.CANONICAL_XML, (TransformParameterSpec) null ) ); final List<Reference> referenceList = new ArrayList<>(); if (!useOpenOffice31Mode) { try ( final InputStream zis = zf.getInputStream( zf.getEntry("mimetype") ) ) { referenceList.add( fac.newReference( "mimetype", dm, null, null, null, md.digest( AOUtil.getDataFromInputStream( zis ) ) ) ); } referenceList.add( fac.newReference( MANIFEST_PATH, dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse( new ByteArrayInputStream(manifestData) ).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ) ); } Reference reference; for (int i = 0; i < listFileEntry.getLength(); i++) { fullPath = ((Element) listFileEntry.item(i)).getAttribute("manifest:full-path"); if (!fullPath.endsWith("/")) { if (fullPath.equals("content.xml") || fullPath.equals("meta.xml") || fullPath.equals("styles.xml") || fullPath.equals("settings.xml")) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse(zis).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ); } } else { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, null, null, null, md.digest(AOUtil.getDataFromInputStream(zis)) ); } } if (!fullPath.equals(SIGNATURES_PATH)) { referenceList.add(reference); } else { isCofirm = true; } } } if (!isCofirm && zf.getEntry(SIGNATURES_PATH) != null) { isCofirm = true; } final Document docSignatures; final Element rootSignatures; if (isCofirm) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(SIGNATURES_PATH)) ) { docSignatures = dbf.newDocumentBuilder().parse( zis ); } rootSignatures = docSignatures.getDocumentElement(); } else { docSignatures = dbf.newDocumentBuilder().newDocument(); rootSignatures = docSignatures.createElement("document-signatures"); rootSignatures.setAttribute("xmlns", OPENOFFICE); docSignatures.appendChild(rootSignatures); if (useOpenOffice31Mode) { final Element nodeDocumentSignatures = docManifest.createElement("manifest:file-entry"); nodeDocumentSignatures.setAttribute("manifest:media-type", ""); nodeDocumentSignatures.setAttribute("manifest:full-path", SIGNATURES_PATH); rootManifest.appendChild(nodeDocumentSignatures); final Element nodeMetaInf = docManifest.createElement("manifest:file-entry"); nodeMetaInf.setAttribute("manifest:media-type", ""); nodeMetaInf.setAttribute("manifest:full-path", "META-INF/"); rootManifest.appendChild(nodeMetaInf); } } final String signatureId = UUID.randomUUID().toString(); final String signaturePropertyId = UUID.randomUUID().toString(); referenceList.add(fac.newReference("#" + signaturePropertyId, dm)); final String contentString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SS") .format(new Date()); final Document dateDocument = dbf.newDocumentBuilder().parse( new ByteArrayInputStream( ("<dc:date xmlns:dc=\"http: .getBytes())); final Element content = dateDocument.getDocumentElement(); final List<XMLStructure> contentList = new ArrayList<>(); contentList.add(new DOMStructure(content)); final List<SignatureProperty> spList = new ArrayList<>(); spList.add(fac.newSignatureProperty(contentList, "#" + signatureId, signaturePropertyId)); final List<SignatureProperties> spsList = new ArrayList<>(); spsList.add(fac.newSignatureProperties(spList, null)); final List<XMLObject> objectList = new ArrayList<>(); objectList.add(fac.newXMLObject(spsList, null, null, null)); final KeyInfoFactory kif = fac.getKeyInfoFactory(); final List<Object> x509Content = new ArrayList<>(); final X509Certificate cert = (X509Certificate) certChain[0]; x509Content.add(cert.getSubjectX500Principal().getName()); x509Content.add(cert); final javax.xml.crypto.dsig.XMLSignature xmlSignature = fac.newXMLSignature( fac.newSignedInfo( fac.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), referenceList ), kif.newKeyInfo( Collections.singletonList(kif.newX509Data(x509Content)), null ), objectList, signatureId, null ); final DOMSignContext context = new DOMSignContext(key, rootSignatures); xmlSignature.sign( context ); try ( final ZipOutputStream zos = new ZipOutputStream(baos); ) { final Enumeration<? extends ZipEntry> e = zf.entries(); ZipEntry ze; ZipEntry zeOut; while (e.hasMoreElements()) { ze = e.nextElement(); zeOut = new ZipEntry(ze.getName()); if (!ze.getName().equals(SIGNATURES_PATH) && !ze.getName().equals(MANIFEST_PATH)) { zos.putNextEntry(zeOut); try ( final InputStream zis = zf.getInputStream(ze) ) { zos.write( AOUtil.getDataFromInputStream( zis ) ); } } } zos.putNextEntry(new ZipEntry(SIGNATURES_PATH)); final ByteArrayOutputStream baosXML = new ByteArrayOutputStream(); writeXML(baosXML, rootSignatures, false); zos.write(baosXML.toByteArray()); zos.closeEntry(); zos.putNextEntry(new ZipEntry(MANIFEST_PATH)); final ByteArrayOutputStream baosManifest = new ByteArrayOutputStream(); writeXML(baosManifest, rootManifest, false); zos.write(baosManifest.toByteArray()); zos.closeEntry(); } } return baos.toByteArray(); } catch (final SAXException saxex) { throw new AOFormatFileException("Estructura de archivo no valida '" + fullPath + "': " + saxex); } catch (final Exception e) { if ("es.gob.jmulticard.CancelledOperationException".equals(e.getClass().getName())) { throw new AOCancelledOperationException(); } throw new AOException("No ha sido posible generar la firma ODF: " + e, e); } }  @Override byte[] sign(final byte[] data,
                       final String algorithm,
                       final PrivateKey key,
                       final java.security.cert.Certificate[] certChain,
                       final Properties xParams); @Override byte[] cosign(final byte[] data,
                         final byte[] sign,
                         final String algorithm,
                         final PrivateKey key,
                         final java.security.cert.Certificate[] certChain,
                         final Properties extraParams); @Override byte[] cosign(final byte[] sign,
                         final String algorithm,
                         final PrivateKey key,
                         final java.security.cert.Certificate[] certChain,
                         final Properties extraParams); @Override byte[] countersign(final byte[] sign,
                              final String algorithm,
                              final CounterSignTarget targetType,
                              final Object[] targets,
                              final PrivateKey key,
                              final java.security.cert.Certificate[] certChain,
                              final Properties extraParams); @Override AOTreeModel getSignersStructure(final byte[] sign, final boolean asSimpleSignInfo); @Override boolean isSign(final byte[] signData); @Override boolean isValidDataFile(final byte[] data); @Override String getSignedName(final String originalName, final String inText); @Override byte[] getData(final byte[] signData); @Override AOSignInfo getSignInfo(final byte[] signData); }
AOODFSigner implements AOSigner { @Override public byte[] sign(final byte[] data, final String algorithm, final PrivateKey key, final java.security.cert.Certificate[] certChain, final Properties xParams) throws AOException { if (!AOSignConstants.SIGN_ALGORITHM_SHA1WITHRSA.equals(algorithm)) { LOGGER.warning( "Se ha indicado '" + algorithm + "' como algoritmo de firma, pero se usara 'SHA1withRSA' por necesidades del formato ODF" ); } final Properties extraParams = xParams != null ? xParams : new Properties(); final String digestMethodAlgorithm = extraParams.getProperty(AOODFExtraParams.REFERENCES_DIGEST_METHOD, DEFAULT_DIGEST_METHOD); final boolean useOpenOffice31Mode = Boolean.parseBoolean(extraParams.getProperty(AOODFExtraParams.USE_OPEN_OFFICE_31_MODE)); if (!isValidDataFile(data)) { throw new AOFormatFileException("Los datos introducidos no se corresponden con un documento ODF"); } String fullPath = MANIFEST_PATH; boolean isCofirm = false; try { final File zipFile = File.createTempFile("sign", ".zip"); try ( final FileOutputStream fos = new FileOutputStream(zipFile); ) { fos.write(data); fos.flush(); } zipFile.deleteOnExit(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try ( final ZipFile zf = new ZipFile(zipFile); ) { final byte[] manifestData; try ( final InputStream manifest = zf.getInputStream(zf.getEntry(fullPath)); ) { manifestData = AOUtil.getDataFromInputStream(manifest); } final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final Document docManifest = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(manifestData)); final Element rootManifest = docManifest.getDocumentElement(); final NodeList listFileEntry = rootManifest.getElementsByTagName("manifest:file-entry"); final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_METHOD_ALGORITHM_NAME); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo " + DIGEST_METHOD_ALGORITHM_NAME + ": " + e, e ); } final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); final DigestMethod dm; try { dm = fac.newDigestMethod(digestMethodAlgorithm, null); } catch (final Exception e) { throw new AOException( "No se ha podido obtener un generador de huellas digitales con el algoritmo: " + digestMethodAlgorithm, e ); } final List<Transform> transformList = new ArrayList<>(1); transformList.add( fac.newTransform( Canonicalizer.CANONICAL_XML, (TransformParameterSpec) null ) ); final List<Reference> referenceList = new ArrayList<>(); if (!useOpenOffice31Mode) { try ( final InputStream zis = zf.getInputStream( zf.getEntry("mimetype") ) ) { referenceList.add( fac.newReference( "mimetype", dm, null, null, null, md.digest( AOUtil.getDataFromInputStream( zis ) ) ) ); } referenceList.add( fac.newReference( MANIFEST_PATH, dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse( new ByteArrayInputStream(manifestData) ).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ) ); } Reference reference; for (int i = 0; i < listFileEntry.getLength(); i++) { fullPath = ((Element) listFileEntry.item(i)).getAttribute("manifest:full-path"); if (!fullPath.endsWith("/")) { if (fullPath.equals("content.xml") || fullPath.equals("meta.xml") || fullPath.equals("styles.xml") || fullPath.equals("settings.xml")) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, transformList, null, null, md.digest( OdfXmlUtil.canonicalizeXml( dbf.newDocumentBuilder().parse(zis).getDocumentElement(), CANONICAL_XML_ALGORITHM ) ) ); } } else { try ( final InputStream zis = zf.getInputStream(zf.getEntry(fullPath)) ) { reference = fac.newReference( fullPath.replaceAll(" ", "%20"), dm, null, null, null, md.digest(AOUtil.getDataFromInputStream(zis)) ); } } if (!fullPath.equals(SIGNATURES_PATH)) { referenceList.add(reference); } else { isCofirm = true; } } } if (!isCofirm && zf.getEntry(SIGNATURES_PATH) != null) { isCofirm = true; } final Document docSignatures; final Element rootSignatures; if (isCofirm) { try ( final InputStream zis = zf.getInputStream(zf.getEntry(SIGNATURES_PATH)) ) { docSignatures = dbf.newDocumentBuilder().parse( zis ); } rootSignatures = docSignatures.getDocumentElement(); } else { docSignatures = dbf.newDocumentBuilder().newDocument(); rootSignatures = docSignatures.createElement("document-signatures"); rootSignatures.setAttribute("xmlns", OPENOFFICE); docSignatures.appendChild(rootSignatures); if (useOpenOffice31Mode) { final Element nodeDocumentSignatures = docManifest.createElement("manifest:file-entry"); nodeDocumentSignatures.setAttribute("manifest:media-type", ""); nodeDocumentSignatures.setAttribute("manifest:full-path", SIGNATURES_PATH); rootManifest.appendChild(nodeDocumentSignatures); final Element nodeMetaInf = docManifest.createElement("manifest:file-entry"); nodeMetaInf.setAttribute("manifest:media-type", ""); nodeMetaInf.setAttribute("manifest:full-path", "META-INF/"); rootManifest.appendChild(nodeMetaInf); } } final String signatureId = UUID.randomUUID().toString(); final String signaturePropertyId = UUID.randomUUID().toString(); referenceList.add(fac.newReference("#" + signaturePropertyId, dm)); final String contentString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SS") .format(new Date()); final Document dateDocument = dbf.newDocumentBuilder().parse( new ByteArrayInputStream( ("<dc:date xmlns:dc=\"http: .getBytes())); final Element content = dateDocument.getDocumentElement(); final List<XMLStructure> contentList = new ArrayList<>(); contentList.add(new DOMStructure(content)); final List<SignatureProperty> spList = new ArrayList<>(); spList.add(fac.newSignatureProperty(contentList, "#" + signatureId, signaturePropertyId)); final List<SignatureProperties> spsList = new ArrayList<>(); spsList.add(fac.newSignatureProperties(spList, null)); final List<XMLObject> objectList = new ArrayList<>(); objectList.add(fac.newXMLObject(spsList, null, null, null)); final KeyInfoFactory kif = fac.getKeyInfoFactory(); final List<Object> x509Content = new ArrayList<>(); final X509Certificate cert = (X509Certificate) certChain[0]; x509Content.add(cert.getSubjectX500Principal().getName()); x509Content.add(cert); final javax.xml.crypto.dsig.XMLSignature xmlSignature = fac.newXMLSignature( fac.newSignedInfo( fac.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), referenceList ), kif.newKeyInfo( Collections.singletonList(kif.newX509Data(x509Content)), null ), objectList, signatureId, null ); final DOMSignContext context = new DOMSignContext(key, rootSignatures); xmlSignature.sign( context ); try ( final ZipOutputStream zos = new ZipOutputStream(baos); ) { final Enumeration<? extends ZipEntry> e = zf.entries(); ZipEntry ze; ZipEntry zeOut; while (e.hasMoreElements()) { ze = e.nextElement(); zeOut = new ZipEntry(ze.getName()); if (!ze.getName().equals(SIGNATURES_PATH) && !ze.getName().equals(MANIFEST_PATH)) { zos.putNextEntry(zeOut); try ( final InputStream zis = zf.getInputStream(ze) ) { zos.write( AOUtil.getDataFromInputStream( zis ) ); } } } zos.putNextEntry(new ZipEntry(SIGNATURES_PATH)); final ByteArrayOutputStream baosXML = new ByteArrayOutputStream(); writeXML(baosXML, rootSignatures, false); zos.write(baosXML.toByteArray()); zos.closeEntry(); zos.putNextEntry(new ZipEntry(MANIFEST_PATH)); final ByteArrayOutputStream baosManifest = new ByteArrayOutputStream(); writeXML(baosManifest, rootManifest, false); zos.write(baosManifest.toByteArray()); zos.closeEntry(); } } return baos.toByteArray(); } catch (final SAXException saxex) { throw new AOFormatFileException("Estructura de archivo no valida '" + fullPath + "': " + saxex); } catch (final Exception e) { if ("es.gob.jmulticard.CancelledOperationException".equals(e.getClass().getName())) { throw new AOCancelledOperationException(); } throw new AOException("No ha sido posible generar la firma ODF: " + e, e); } }  @Override byte[] sign(final byte[] data,
                       final String algorithm,
                       final PrivateKey key,
                       final java.security.cert.Certificate[] certChain,
                       final Properties xParams); @Override byte[] cosign(final byte[] data,
                         final byte[] sign,
                         final String algorithm,
                         final PrivateKey key,
                         final java.security.cert.Certificate[] certChain,
                         final Properties extraParams); @Override byte[] cosign(final byte[] sign,
                         final String algorithm,
                         final PrivateKey key,
                         final java.security.cert.Certificate[] certChain,
                         final Properties extraParams); @Override byte[] countersign(final byte[] sign,
                              final String algorithm,
                              final CounterSignTarget targetType,
                              final Object[] targets,
                              final PrivateKey key,
                              final java.security.cert.Certificate[] certChain,
                              final Properties extraParams); @Override AOTreeModel getSignersStructure(final byte[] sign, final boolean asSimpleSignInfo); @Override boolean isSign(final byte[] signData); @Override boolean isValidDataFile(final byte[] data); @Override String getSignedName(final String originalName, final String inText); @Override byte[] getData(final byte[] signData); @Override AOSignInfo getSignInfo(final byte[] signData);  }