/* * SAXDigiDocFactory.java * PROJECT: JDigiDoc * DESCRIPTION: Digi Doc functions for reading signed documents. * AUTHOR: Veiko Sinivee, Sunset Software O������ *================================================== * Copyright (C) AS Sertifitseerimiskeskus * This library 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. * This library 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. * GNU Lesser General Public Licence is available at * http://www.gnu.org/copyleft/lesser.html *================================================== */ package ee.sk.digidoc.factory; import ee.sk.digidoc.*; import ee.sk.utils.ConvertUtils; import ee.sk.utils.ConfigManager; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.Stack; import java.util.List; import java.util.ArrayList; import java.util.Hashtable; import java.util.TreeSet; import org.apache.commons.compress.archivers.zip.*; import org.xml.sax.SAXException; import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParser; import java.security.cert.X509Certificate; import org.apache.log4j.Logger; /** * SAX implementation of DigiDocFactory * Provides methods for reading a DigiDoc file * @author Veiko Sinivee * @version 1.0 */ public class SAXDigiDocFactory extends DefaultHandler implements DigiDocFactory { private Stack m_tags; private SignedDoc m_doc; private Signature m_sig; private String m_strSigValTs, m_strSigAndRefsTs; private StringBuffer m_sbCollectChars; private StringBuffer m_sbCollectItem; private StringBuffer m_sbCollectSignature; private boolean m_bCollectDigest; private String m_xmlnsAttr; /** This mode means collect SAX events into xml data * and is used to collect all <DataFile>, <SignedInfo> and * <SignedProperties> content. Also servers as level of * embedded DigiDoc files. Initially it should be 0. If * we start collecting data then it's 1 and if we find * another SignedDoc inside a DataFile then it will be incremented * in order to know which is the correct </DataFile> tag to leave * the collect mode */ private int m_nCollectMode; private long nMaxBdocFilCached; /** log4j logger */ private Logger m_logger = null; /** calculation of digest */ private MessageDigest m_digest, m_altDigest; /** temp output stream used to cache DataFile content */ private FileOutputStream m_dfCacheOutStream; private String m_tempDir; /** name of file being loaded */ private String m_fileName, m_sigComment; private String m_nsDsPref, m_nsXadesPref, m_nsAsicPref; private List m_errs; private XmlElemInfo m_elemRoot, m_elemCurrent; public static final String FILE_MIMETYPE = "mimetype"; public static final String FILE_MANIFEST = "META-INF/manifest.xml"; public static final String CONTENTS_MIMETYPE = "application/vnd.bdoc"; public static final String CONTENTS_MIMETYPE_1_0 = "application/vnd.bdoc-1.0"; public static final String MIME_SIGNATURE_BDOC = "signature/bdoc"; public static final String FILE_SIGNATURES = "META-INF/signature"; /** * Creates new SAXDigiDocFactory * and initializes the variables */ public SAXDigiDocFactory() { m_tags = new Stack(); m_doc = null; m_sig = null; m_sbCollectSignature = null; m_xmlnsAttr = null; m_nsAsicPref = null; m_sbCollectItem = null; m_digest = null; m_altDigest = null; m_bCollectDigest = false; m_dfCacheOutStream = null; m_tempDir = null; m_errs = null; m_elemRoot = null; m_elemCurrent = null; m_logger = Logger.getLogger(SAXDigiDocFactory.class); nMaxBdocFilCached = ConfigManager.instance(). getLongProperty("DIGIDOC_MAX_DATAFILE_CACHED", Long.MAX_VALUE); } /** * Helper method to update sha1 digest with some data * @param data */ private void updateDigest(byte[] data) { try { // if not inited yet then initialize digest if(m_digest == null) m_digest = MessageDigest.getInstance("SHA-1"); m_digest.update(data); } catch(Exception ex) { m_logger.error("Error calculating digest: " + ex); //ex.printStackTrace(); } } /** * Helper method to update alternate sha1 digest with some data * @param data */ private void updateAltDigest(byte[] data) { try { // if not inited yet then initialize digest if(m_altDigest == null) m_altDigest = MessageDigest.getInstance("SHA-1"); m_altDigest.update(data); } catch(Exception ex) { m_logger.error("Error calculating digest: " + ex); //ex.printStackTrace(); } } /** * Set temp dir used to cache data files. * @param s directory name */ public void setTempDir(String s) { m_tempDir = s; } /** * Helper method to calculate the digest result and * reset digest * @return sha-1 digest value */ private byte[] getDigest() { byte [] digest = null; try { // if not inited yet then initialize digest digest = m_digest.digest(); m_digest = null; // reset for next calculation } catch(Exception ex) { m_logger.error("Error calculating digest: " + ex); //ex.printStackTrace(); } return digest; } /** * Helper method to calculate the alternate digest result and * reset digest * @return sha-1 digest value */ private byte[] getAltDigest() { byte [] digest = null; try { // if not inited yet then initialize digest digest = m_altDigest.digest(); m_altDigest = null; // reset for next calculation } catch(Exception ex) { m_logger.error("Error calculating digest: " + ex); //ex.printStackTrace(); } return digest; } /** * initializes the implementation class */ public void init() throws DigiDocException { } /** * Checks filename extension if this is bdoc / asic-e * @param fname filename * @return true if this is bdoc / asic-e */ public boolean isBdocExtension(String fname) { return fname.endsWith(".bdoc") || fname.endsWith(".asice") || fname.endsWith(".sce"); } /** * Checks if this stream could be a bdoc input stream * @param is input stream, must support mark() and reset() operations! * @return true if bdoc */ private boolean isBdocFile(InputStream is) throws DigiDocException { try { if(is.markSupported()) is.mark(10); byte[] tdata = new byte[10]; int n = is.read(tdata); if(is.markSupported()) is.reset(); if(n >= 2 && tdata[0] == (byte)'P' && tdata[1] == (byte)'K') return true; // probably a zip file if(n >= 5 && tdata[0] == (byte)'<' && tdata[1] == (byte)'?' && tdata[2] == (byte)'x' && tdata[3] == (byte)'m' && tdata[4] == (byte)'l') return false; // an xml file - probably ddoc format? } catch(Exception ex) { m_logger.error("Error determining file type: " + ex); } return false; } /** * Checks if this file contains the correct bdoc mimetype * @param zis ZIP input stream * @return true if correct bdoc */ private boolean checkBdocMimetype(InputStream zis) throws DigiDocException { try { byte[] data = new byte[100]; int nRead = zis.read(data); if(nRead >= CONTENTS_MIMETYPE.length()) { //skip leading whitespace & BOM marks String s2 = new String(data, 0, nRead), s = null; for(int i = 0; i < nRead; i++) { if(s2.charAt(i) == 'a') { // search application/... s = s2.substring(i); break; } } if(s == null) s = new String(data); if(m_logger.isDebugEnabled()) m_logger.debug("MimeType: \'" + s + "\'" + " len: " + s.length()); if(s.trim().equals(SignedDoc.MIMET_FILE_CONTENT_10)) { m_doc.setVersion(SignedDoc.BDOC_VERSION_1_0); m_doc.setFormat(SignedDoc.FORMAT_BDOC); throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "Format BDOC supports only version 2.1", null); } else if(s.trim().equals(SignedDoc.MIMET_FILE_CONTENT_11)) { m_doc.setVersion(SignedDoc.BDOC_VERSION_1_1); m_doc.setFormat(SignedDoc.FORMAT_BDOC); throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "Format BDOC supports only version 2.1", null); } else if(s.trim().equals(SignedDoc.MIMET_FILE_CONTENT_20)) { m_doc.setVersion(SignedDoc.BDOC_VERSION_2_1); m_doc.setFormat(SignedDoc.FORMAT_BDOC); m_doc.setProfile(SignedDoc.BDOC_PROFILE_TM); return true; } else if(s.trim().startsWith(CONTENTS_MIMETYPE)) { throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "Invalid BDOC version!", null); } else { // no bdoc or wrong version if(m_logger.isDebugEnabled()) m_logger.debug("Invalid MimeType: \'" + s + "\'" + " len: " + s.length() + " expecting: " + CONTENTS_MIMETYPE.length()); throw new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "Not a BDOC format file!", null); } } else { if(m_logger.isDebugEnabled()) m_logger.debug("Invalid empty MimeType"); throw new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "Not a BDOC format file! MimeType file is empty!", null); } } catch(DigiDocException ex) { m_logger.error("Mimetype err: " + ex); //ex.printStackTrace(); throw ex; } catch(Exception ex) { m_logger.error("Error reading mimetype file: " + ex); } return false; } private void handleError(Exception err) throws DigiDocException { if(m_logger.isDebugEnabled()) m_logger.debug("Handle err: " + err + " list: " + (m_errs != null)); err.printStackTrace(); DigiDocException err1 = null; if(err instanceof SAXDigiDocException) { err1 = ((SAXDigiDocException)err).getDigiDocException(); } else if(err instanceof DigiDocException) { err1 = (DigiDocException)err; err1.printStackTrace(); if(err1.getNestedException() != null) err1.getNestedException().printStackTrace(); } else err1 = new DigiDocException(DigiDocException.ERR_PARSE_XML, "Invalid xml file!", err); if(m_errs != null) m_errs.add(err1); else throw err1; } private void handleSAXError(Exception err) throws SAXDigiDocException { if(m_logger.isDebugEnabled()) { m_logger.debug("Handle sa err: " + err + " list: " + (m_errs != null)); m_logger.debug("Trace: " + ConvertUtils.getTrace(err)); } DigiDocException err1 = null; SAXDigiDocException err2 = null; if(err instanceof SAXDigiDocException) { err1 = ((SAXDigiDocException)err).getDigiDocException(); err2 = (SAXDigiDocException)err; } else if(err instanceof DigiDocException) { err1 = (DigiDocException)err; err2 = new SAXDigiDocException(err.getMessage()); err2.setNestedException(err); } else { err1 = new DigiDocException(0, err.getMessage(), null); err2 = new SAXDigiDocException(err.getMessage()); err2.setNestedException(err); } if(m_errs != null) m_errs.add(err1); else throw err2; } /** * Reads in a DigiDoc file. One of fname or isSdoc must be given. * @param fname signed doc filename * @param isSdoc opened stream with DigiDoc data * The user must open and close it. * @param errs list of errors to fill with parsing errors. If given * then attempt is made to continue parsing on errors and return them in this list. * If not given (null) then the first error found will be thrown. * @return signed document object if successfully parsed */ private SignedDoc readSignedDocOfType(String fname, InputStream isSdoc, boolean isBdoc, List errs) throws DigiDocException { // Use an instance of ourselves as the SAX event handler SAXDigiDocFactory handler = this; m_errs = errs; DigiDocVerifyFactory.initProvider(); SAXParserFactory factory = SAXParserFactory.newInstance(); if(m_logger.isDebugEnabled()) m_logger.debug("Start reading ddoc/bdoc " + ((fname != null) ? "from file: " + fname : "from stream") + " bdoc: " + isBdoc); if(fname == null && isSdoc == null) { throw new DigiDocException(DigiDocException.ERR_READ_FILE, "No input file", null); } if(fname != null) { File inFile = new File(fname); if(!inFile.canRead() || inFile.length() == 0) { throw new DigiDocException(DigiDocException.ERR_READ_FILE, "Empty or unreadable input file", null); } } ZipFile zf = null; ZipArchiveInputStream zis = null; ZipArchiveEntry ze = null; InputStream isEntry = null; File fTmp = null; try { factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); if(isBdoc) { // bdoc parsing // must be a bdoc document ? m_doc = new SignedDoc(); m_doc.setVersion(SignedDoc.BDOC_VERSION_1_0); m_doc.setFormat(SignedDoc.FORMAT_BDOC); Enumeration eFiles = null; if(fname != null) { zf = new ZipFile(fname, "UTF-8"); eFiles = zf.getEntries(); } else if(isSdoc != null) { zis = new ZipArchiveInputStream(isSdoc, "UTF-8", true, true); } ArrayList lSigFnames = new ArrayList(); ArrayList lDataFnames = new ArrayList(); // read all entries boolean bHasMimetype = false, bManifest1 = false; int nFil = 0; while((zf != null && eFiles.hasMoreElements()) || (zis != null && ((ze = zis.getNextZipEntry()) != null)) ) { nFil++; // read entry if(zf != null) { // ZipFile ze = (ZipArchiveEntry)eFiles.nextElement(); isEntry = zf.getInputStream(ze); } else { // ZipArchiveInputStream int n = 0, nTot = 0; if((ze.getName().equals(FILE_MIMETYPE) || ze.getName().equals(FILE_MANIFEST) || (ze.getName().startsWith(FILE_SIGNATURES) && ze.getName().endsWith(".xml"))) || (nMaxBdocFilCached <= 0 || (ze.getSize() < nMaxBdocFilCached && ze.getSize() >= 0))) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] data = new byte[2048]; while((n = zis.read(data)) > 0) { bos.write(data, 0, n); nTot += n; } if(m_logger.isDebugEnabled()) m_logger.debug("Read: " + nTot + " bytes from zip"); data = bos.toByteArray(); bos = null; isEntry = new ByteArrayInputStream(data); } else { File fCacheDir = new File(ConfigManager.instance(). getStringProperty("DIGIDOC_DF_CACHE_DIR", System.getProperty("java.io.tmpdir"))); fTmp = File.createTempFile("bdoc-data", ".tmp", fCacheDir); FileOutputStream fos = new FileOutputStream(fTmp); byte[] data = new byte[2048]; while((n = zis.read(data)) > 0) { fos.write(data, 0, n); nTot += n; } if(m_logger.isDebugEnabled()) m_logger.debug("Read: " + nTot + " bytes from zip to: " + fTmp.getAbsolutePath()); fos.close(); isEntry = new FileInputStream(fTmp); } } if(m_logger.isDebugEnabled()) m_logger.debug("Entry: " + ze.getName() + " nlen: " + ze.getName().length() + " size: " + ze.getSize() + " dir: " + ze.isDirectory() + " comp-size: " + ze.getCompressedSize()); // mimetype file if(ze.getName().equals(FILE_MIMETYPE)) { if(m_logger.isDebugEnabled()) m_logger.debug("Check mimetype!"); checkBdocMimetype(isEntry); bHasMimetype = true; m_doc.setComment(ze.getComment()); if(nFil != 1) { m_logger.error("mimetype file is " + nFil + " file but must be first"); handleError(new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "mimetype file is not first zip entry", null)); } } else if(ze.getName().equals(FILE_MANIFEST)) { // manifest.xml file if(m_logger.isDebugEnabled()) m_logger.debug("Read manifest"); if(!bManifest1 && isEntry != null) { bManifest1 = true; BdocManifestParser mfparser = new BdocManifestParser(m_doc); mfparser.readManifest(isEntry); } else { m_logger.error("Found multiple manifest.xml files!"); throw new DigiDocException(DigiDocException.ERR_MULTIPLE_MANIFEST_FILES, "Found multiple manifest.xml files!", null); } } else if(ze.getName().startsWith(FILE_SIGNATURES) && ze.getName().endsWith(".xml")) { // some signature m_fileName = ze.getName(); if(m_logger.isDebugEnabled()) m_logger.debug("Reading bdoc siganture: " + m_fileName); boolean bExists = false; for(int j = 0; j < lSigFnames.size(); j++) { String s1 = (String)lSigFnames.get(j); if(s1.equals(m_fileName)) bExists = true; } if(bExists) { m_logger.error("Duplicate signature filename: " + m_fileName); handleError(new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "Duplicate signature filename: " + m_fileName, null)); } else lSigFnames.add(m_fileName); SAXParser saxParser = factory.newSAXParser(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int n = 0; byte[] data = new byte[2048]; while((n = isEntry.read(data)) > 0) bos.write(data, 0, n); data = bos.toByteArray(); bos = null; if(m_logger.isDebugEnabled()) m_logger.debug("Parsing bdoc: " + m_fileName + " size: " + ((data != null) ? data.length : 0)); saxParser.parse(new SignatureInputStream(new ByteArrayInputStream(data)), this); if(m_logger.isDebugEnabled()) m_logger.debug("Parsed bdoc: " + m_fileName); Signature sig1 = m_doc.getLastSignature(); m_sigComment = ze.getComment(); if(sig1 != null) { sig1.setPath(m_fileName); sig1.setComment(ze.getComment()); } } else { // probably a data file if(m_logger.isDebugEnabled()) m_logger.debug("Read data file: " + ze.getName()); if(!ze.isDirectory()) { boolean bExists = false; for(int j = 0; j < lDataFnames.size(); j++) { String s1 = (String)lDataFnames.get(j); if(s1.equals(ze.getName())) bExists = true; } if(bExists) { m_logger.error("Duplicate datafile filename: " + ze.getName()); handleError(new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "Duplicate datafile filename: " + ze.getName(), null)); } else lDataFnames.add(ze.getName()); DataFile df = m_doc.findDataFileById(ze.getName()); if(df != null) { if(ze.getSize() > 0) df.setSize(ze.getSize()); df.setContentType(DataFile.CONTENT_BINARY); df.setFileName(ze.getName()); } else { df = new DataFile(ze.getName(), DataFile.CONTENT_BINARY, ze.getName(), "application/binary", m_doc); if(m_doc.getDataFiles() == null) m_doc.setDataFiles(new ArrayList()); m_doc.getDataFiles().add(df); //m_doc.addDataFile(df); // this does some intiailization work unnecessary here } // enable caching if requested if(isEntry != null) df.setOrCacheBodyAndCalcHashes(isEntry); df.setComment(ze.getComment()); df.setLastModDt(new Date(ze.getTime())); // fix mime type according to DataObjectFormat Signature sig1 = m_doc.getLastSignature(); if(sig1 != null) { Reference dRef = sig1.getSignedInfo().getReferenceForDataFile(df); if(dRef != null) { DataObjectFormat dof = sig1.getSignedInfo().getDataObjectFormatForReference(dRef); if(dof != null) { df.setMimeType(dof.getMimeType()); } } } } } if(fTmp != null) { fTmp.delete(); fTmp = null; } } // while zip entries if(!bHasMimetype) { m_logger.error("No mimetype file"); handleError(new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "Not a BDOC format file! No mimetype file!", null)); } // if no signatures exist then copy mime-type from manifest.xml to DataFile -s if(m_doc.countSignatures() == 0) { for(int i = 0; i < m_doc.countDataFiles(); i++) { DataFile df = m_doc.getDataFile(i); if(m_doc.getManifest() != null) { for(int j = 0; j < m_doc.getManifest().getNumFileEntries(); j++) { ManifestFileEntry mfe = m_doc.getManifest().getFileEntry(j); if(mfe.getFullPath() != null && mfe.getFullPath().equals(df.getFileName())) { df.setMimeType(mfe.getMediaType()); } // if fullpath } // for } // if } // for i } } else { // ddoc parsing if(m_logger.isDebugEnabled()) m_logger.debug("Reading ddoc: " + fname + " file: " + m_fileName); m_fileName = fname; SAXParser saxParser = factory.newSAXParser(); if(fname != null) saxParser.parse(new SignatureInputStream(new FileInputStream(fname)), this); else if(isSdoc != null) saxParser.parse(isSdoc, this); } } catch(org.xml.sax.SAXParseException ex) { m_logger.error("SAX Error: " + ex); handleError(ex); } catch (Exception ex) { m_logger.error("Error reading3: " + ex); ex.printStackTrace(); /*if(ex instanceof DigiDocException){ DigiDocException dex = (DigiDocException)ex; m_logger.error("Dex: " + ex); if(dex.getNestedException() != null) { dex.getNestedException().printStackTrace(); m_logger.error("Trace: "); } }*/ handleError(ex); } finally { // cleanup try { if(isEntry != null) { isEntry.close(); isEntry = null; } if(zis != null) zis.close(); if(zf != null) zf.close(); if(fTmp != null) { fTmp.delete(); fTmp = null; } } catch(Exception ex) { m_logger.error("Error closing streams and files: " + ex); } } // compare Manifest and DataFiles boolean bErrList = (errs != null); if(errs == null) errs = new ArrayList(); boolean bOk = DigiDocVerifyFactory.verifyManifestEntries(m_doc, errs); if(m_doc == null) { m_logger.error("Error reading4: doc == null"); handleError(new DigiDocException(DigiDocException.ERR_DIGIDOC_BADXML, "This document is not in ddoc or bdoc format", null)); } if(!bErrList && errs.size() > 0) { // if error list was not used then we have to throw exception. So we will throw the first one since we can only do it once DigiDocException ex = (DigiDocException)errs.get(0); throw ex; } return m_doc; } /** * Reads in a DigiDoc or BDOC file * @param fname filename * @param isBdoc true if bdoc is read * @return signed document object if successfully parsed */ public SignedDoc readSignedDocOfType(String fname, boolean isBdoc) throws DigiDocException { return readSignedDocOfType(fname, null, isBdoc, null); } /** * Reads in a DigiDoc or BDOC from stream. In case of BDOC a Zip stream will be * constructed to read this input stream. In case of ddoc a normal saxparsing stream * will be used. * @param is opened stream with DigiDoc/BDOC data * The user must open and close it. * @param isBdoc true if bdoc is read * @return signed document object if successfully parsed * @deprecated use readSignedDocFromStreamOfType(InputStream is, boolean isBdoc, List lerr) */ public SignedDoc readSignedDocFromStreamOfType(InputStream is, boolean isBdoc) throws DigiDocException { return readSignedDocOfType(null, is, isBdoc, null); } /** * Reads in a DigiDoc or BDOC file * @param fname filename * @param isBdoc true if bdoc is read * @param lerr list of errors to be filled. If not null then no exceptions are thrown * but returned in this array * @return signed document object if successfully parsed */ public SignedDoc readSignedDocOfType(String fname, boolean isBdoc, List lerr) throws DigiDocException { return readSignedDocOfType(fname, null, isBdoc, lerr); } /** * Reads in a DigiDoc or BDOC from stream. In case of BDOC a Zip stream will be * constructed to read this input stream. In case of ddoc a normal saxparsing stream * will be used. * @param is opened stream with DigiDoc/BDOC data * The user must open and close it. * @param isBdoc true if bdoc is read * @param lerr list of errors to be filled. If not null then no exceptions are thrown * but returned in this array * @return signed document object if successfully parsed */ public SignedDoc readSignedDocFromStreamOfType(InputStream is, boolean isBdoc, List lerr) throws DigiDocException { return readSignedDocOfType(null, is, isBdoc, lerr); } /** * Reads in a DigiDoc file.This method reads only data in digidoc format. Not BDOC! * @param digiDocStream opened stream with DigiDoc data * The user must open and close it. * @return signed document object if successfully parsed */ public SignedDoc readDigiDocFromStream(InputStream digiDocStream) throws DigiDocException { DigiDocVerifyFactory.initProvider(); if(m_logger.isDebugEnabled()) m_logger.debug("Start reading ddoc/bdoc"); try { if(m_logger.isDebugEnabled()) m_logger.debug("Reading ddoc"); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(digiDocStream, this); } catch (SAXDigiDocException ex) { throw ex.getDigiDocException(); } catch (Exception ex) { DigiDocException.handleException(ex, DigiDocException.ERR_PARSE_XML); } if (m_doc == null) throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "This document is not in digidoc", null); return m_doc; } /** * Reads in a DigiDoc file * @param fileName file name * @return signed document object if successfully parsed */ public SignedDoc readSignedDoc(String fileName) throws DigiDocException { try { FileInputStream fis = new FileInputStream(fileName); boolean bdoc = isBdocFile(fis); fis.close(); SignedDoc sdoc = readSignedDocOfType(fileName, bdoc); File f = new File(fileName); m_fileName = fileName; sdoc.setFile(f.getName()); String s = f.getAbsolutePath(); int n = s.lastIndexOf(File.separator); if(n > 0) { s = s.substring(0,n); sdoc.setPath(s); } return sdoc; } catch (DigiDocException ex) { throw ex; } catch(java.io.FileNotFoundException ex) { throw new DigiDocException(DigiDocException.ERR_READ_FILE, "File not found: " + fileName, null); } catch(java.io.IOException ex) { throw new DigiDocException(DigiDocException.ERR_READ_FILE, "Error determning file type: " + fileName, null); } } /** * Reads in a DigiDoc file * @param digiSigStream opened stream with Signature data * The user must open and close it. * @return signed document object if successfully parsed */ public Signature readSignature(InputStream digiSigStream) throws DigiDocException { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(digiSigStream, this); } catch (SAXDigiDocException ex) { throw ex.getDigiDocException(); } catch (Exception ex) { DigiDocException.handleException(ex, DigiDocException.ERR_PARSE_XML); } if (m_sig == null) throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "This document is not in signature format", null); return m_sig; } /** * Reads in only one <Signature> * @param sdoc SignedDoc to add this signature to * @param sigStream opened stream with Signature data * The user must open and close it. * @return signed document object if successfully parsed */ public Signature readSignature(SignedDoc sdoc, InputStream sigStream) throws DigiDocException { m_doc = sdoc; m_nCollectMode = 0; try { // prepare validator to receive signature from xml file as root element if(sdoc != null && sdoc.getFormat() != null) { XmlElemInfo e = null; // for BDOC if(SignedDoc.FORMAT_BDOC.equals(sdoc.getFormat())) { e = new XmlElemInfo("XAdESSignatures", null, null); } else if(SignedDoc.FORMAT_DIGIDOC_XML.equals(sdoc.getFormat())) { e = new XmlElemInfo("SignedDoc", null, null); } if(e != null) m_elemRoot = m_elemCurrent = e; } SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(sigStream, this); } catch (SAXDigiDocException ex) { throw ex.getDigiDocException(); } catch (Exception ex) { DigiDocException.handleException(ex, DigiDocException.ERR_PARSE_XML); } if (m_doc.getLastSignature() == null) throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT, "This document is not in Signature format", null); return m_doc.getLastSignature(); } /** * Helper method to canonicalize a piece of xml * @param xml data to be canonicalized * @return canonicalized xml */ private String canonicalizeXml(String xml) { try { CanonicalizationFactory canFac = ConfigManager. instance().getCanonicalizationFactory(); byte[] tmp = canFac.canonicalize(xml.getBytes("UTF-8"), SignedDoc.CANONICALIZATION_METHOD_20010315); return new String(tmp, "UTF-8"); } catch(Exception ex) { m_logger.error("Canonicalizing exception: " + ex); } return null; } public SignedDoc getSignedDoc() { return m_doc; } public Signature getLastSignature() { if(m_doc != null) return m_doc.getLastSignature(); else return m_sig; } /** * Start Document handler */ public void startDocument() throws SAXException { m_nCollectMode = 0; m_xmlnsAttr = null; m_dfCacheOutStream = null; m_nsDsPref = null; m_nsXadesPref = null; m_nsAsicPref = null; } private void findCertIDandCertValueTypes(Signature sig) { if(m_logger.isDebugEnabled() && sig != null) m_logger.debug("Sig: " + sig.getId() + " certids: " + sig.countCertIDs()); for(int i = 0; (sig != null) && (i < sig.countCertIDs()); i++) { CertID cid = sig.getCertID(i); if(cid != null && cid.getType() == CertID.CERTID_TYPE_UNKNOWN) { if(m_logger.isDebugEnabled()) m_logger.debug("CertId: " + cid.getId() + " type: " + cid.getType() + " nr: " + cid.getSerial()); CertValue cval = sig.findCertValueWithSerial(cid.getSerial()); if(cval != null) { String cn = null; try { cn = SignedDoc. getCommonName(cval.getCert().getSubjectDN().getName()); if(m_logger.isDebugEnabled() && cid != null) m_logger.debug("CertId type: " + cid.getType() + " nr: " + cid.getSerial() + " cval: " + cval.getId() + " CN: " + cn); if(ConvertUtils.isKnownOCSPCert(cn)) { if(m_logger.isInfoEnabled()) m_logger.debug("Cert: " + cn + " is OCSP responders cert"); cid.setType(CertID.CERTID_TYPE_RESPONDER); cval.setType(CertValue.CERTVAL_TYPE_RESPONDER); } if(ConvertUtils.isKnownTSACert(cn)) { if(m_logger.isDebugEnabled()) m_logger.debug("Cert: " + cn + " is TSA cert"); cid.setType(CertID.CERTID_TYPE_TSA); cval.setType(CertValue.CERTVAL_TYPE_TSA); if(m_logger.isDebugEnabled()) m_logger.debug("CertId: " + cid.getId() + " type: " + cid.getType() + " nr: " + cid.getSerial()); } } catch(DigiDocException ex) { m_logger.error("Error setting type on certid or certval: " + cn); } } } } // for i < sig.countCertIDs() if(m_logger.isDebugEnabled()) m_logger.debug("Sig: " + sig.getId() + " certvals: " + sig.countCertValues()); for(int i = 0; (sig != null) && (i < sig.countCertValues()); i++) { CertValue cval = sig.getCertValue(i); if(m_logger.isDebugEnabled() && cval != null) m_logger.debug("CertValue: " + cval.getId() + " type: " + cval.getType()); if(cval.getType() == CertValue.CERTVAL_TYPE_UNKNOWN) { String cn = null; try { cn = SignedDoc. getCommonName(cval.getCert().getSubjectDN().getName()); if(ConvertUtils.isKnownOCSPCert(cn)) { if(m_logger.isDebugEnabled()) m_logger.debug("Cert: " + cn + " is OCSP responders cert"); cval.setType(CertValue.CERTVAL_TYPE_RESPONDER); } if(ConvertUtils.isKnownTSACert(cn)) { if(m_logger.isDebugEnabled()) m_logger.debug("Cert: " + cn + " is TSA cert"); cval.setType(CertValue.CERTVAL_TYPE_TSA); } } catch(DigiDocException ex) { m_logger.error("Error setting type on certid or certval: " + cn); } } } } private String findXmlElemContents(String str, String tag, String id) { String s1 = "<" + tag; String s2 = "</" + tag + ">"; int nIdx1 = 0, nIdx2 = 0, nIdx3 = 0, nIdx4 = 0; while((nIdx1 = str.indexOf(s1, nIdx1)) > 0) { nIdx2 = str.indexOf(">", nIdx1); if(nIdx2 > 0) { nIdx3 = str.indexOf("Id", nIdx1); if(nIdx3 > 0 && nIdx3 < nIdx2) { nIdx3 = str.indexOf("\"", nIdx3); nIdx4 = str.indexOf("\"", nIdx3+1); if(nIdx3 > nIdx1 && nIdx3 < nIdx2 && nIdx4 > nIdx1 && nIdx4 < nIdx2) { String sId = str.substring(nIdx3+1, nIdx4); if(sId.equals(id)) { nIdx2 = str.indexOf(s2, nIdx2); if(nIdx2 > nIdx1) { nIdx2 += s2.length() + 1; String sEl = str.substring(nIdx1, nIdx2); if(m_logger.isDebugEnabled()) m_logger.debug("Elem: " + tag + " id: " + id + "\n---\n" + sEl + "\n---\n"); return sEl; } } } } } } return null; } /** * End Document handler */ public void endDocument() throws SAXException { } private String findNsPrefForUri(Attributes attrs, String uri) { for(int i = 0; i < attrs.getLength(); i++) { String key = attrs.getQName(i); String val = attrs.getValue(i); if(val.equals(uri)) { int p = key.indexOf(':'); if(p > 0) return key.substring(p+1); else return null; } } return null; } private String findAttrValueByName(Attributes attrs, String aName) { for(int i = 0; i < attrs.getLength(); i++) { String key = attrs.getQName(i); if (key.equalsIgnoreCase(aName)) { return attrs.getValue(i); } } return null; } /** * Start Element handler * @param namespaceURI namespace URI * @param lName local name * @param qName qualified name * @param attrs attributes */ public void startElement(String namespaceURI, String lName, String qName, Attributes attrs) throws SAXDigiDocException { if(m_logger.isDebugEnabled()) m_logger.debug("Start Element: " + qName + " lname: " + lName + " uri: " + namespaceURI); String tag = qName; if(tag.indexOf(':') != -1) { tag = qName.substring(qName.indexOf(':') + 1); if(m_nsDsPref == null) { m_nsDsPref = findNsPrefForUri(attrs, xmlnsDs); if(m_logger.isDebugEnabled()) m_logger.debug("Element: " + qName + " xmldsig pref: " + ((m_nsDsPref != null) ? m_nsDsPref : "NULL")); } if(m_nsXadesPref == null) { m_nsXadesPref = findNsPrefForUri(attrs, xmlnsEtsi); if(m_logger.isDebugEnabled()) m_logger.debug("Element: " + qName + " xades pref: " + ((m_nsXadesPref != null) ? m_nsXadesPref : "NULL")); } if(m_nsAsicPref == null) { m_nsAsicPref = findNsPrefForUri(attrs, xmlnsAsic); if(m_logger.isDebugEnabled()) m_logger.debug("Element: " + qName + " asic pref: " + ((m_nsAsicPref != null) ? m_nsAsicPref : "NULL")); } } // record elements found XmlElemInfo e = new XmlElemInfo(tag, findAttrValueByName(attrs, "id"), (tag.equals("XAdESSignatures") || tag.equals("SignedDoc")) ? null : m_elemCurrent); // <XAdESSignatures> and <SignedDoc> cannot be child of another element, must be root elements if(m_elemCurrent != null && !tag.equals("XAdESSignatures") && !tag.equals("SignedDoc")) m_elemCurrent.addChild(e); m_elemCurrent = e; if(m_elemRoot == null || tag.equals("XAdESSignatures") || tag.equals("SignedDoc")) m_elemRoot = e; DigiDocException exv = DigiDocStructureValidator.validateElementPath(m_elemCurrent); if(exv != null) handleSAXError(exv); m_tags.push(tag); if(tag.equals("SigningTime") || tag.equals("IssuerSerial") || tag.equals("X509SerialNumber") || tag.equals("X509IssuerName") || tag.equals("ClaimedRole") || tag.equals("City") || tag.equals("StateOrProvince") || tag.equals("CountryName") || tag.equals("PostalCode") || tag.equals("SignatureValue") || tag.equals("DigestValue") || //qName.equals("EncapsulatedX509Certificate") || tag.equals("IssuerSerial") || (tag.equals("ResponderID") && !m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) || (tag.equals("ByName") && m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) ) || (tag.equals("ByKey") && m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) || tag.equals("X509SerialNumber") || tag.equals("ProducedAt") || tag.equals("EncapsulatedTimeStamp") || tag.equals("Identifier") || tag.equals("SPURI") || tag.equals("NonceAlgorithm") || tag.equals("MimeType") || tag.equals("EncapsulatedOCSPValue") ) { if(m_logger.isDebugEnabled()) m_logger.debug("Start collecting tag: " + tag); m_sbCollectItem = new StringBuffer(); } // <XAdESSignatures> if(tag.equals("XAdESSignatures")) { try { if (m_logger.isDebugEnabled()) m_logger.debug("BDOC 2.0 - ASIC-E"); m_doc.setFormatAndVersion(SignedDoc.FORMAT_BDOC, SignedDoc.BDOC_VERSION_2_1); } catch(DigiDocException ex) { handleSAXError(ex); } } // <X509Certificate> // Prepare CertValue object if(tag.equals("X509Certificate")) { Signature sig = getLastSignature(); CertValue cval = null; try { if (m_logger.isDebugEnabled()) m_logger.debug("Adding signers cert to: " + sig.getId()); cval = sig.getOrCreateCertValueOfType(CertValue.CERTVAL_TYPE_SIGNER); } catch(DigiDocException ex) { handleSAXError(ex); } m_sbCollectItem = new StringBuffer(); } // <EncapsulatedX509Certificate> // Prepare CertValue object and record it's id if(tag.equals("EncapsulatedX509Certificate")) { Signature sig = getLastSignature(); String id = null; for(int i = 0; i < attrs.getLength(); i++) { String key = attrs.getQName(i); if (key.equalsIgnoreCase("Id")) { id = attrs.getValue(i); } } CertValue cval = new CertValue(); if(id != null) { cval.setId(id); try { if(id.indexOf("RESPONDER_CERT") != -1 || id.indexOf("RESPONDER-CERT") != -1) cval.setType(CertValue.CERTVAL_TYPE_RESPONDER); } catch(DigiDocException ex) { handleSAXError(ex); } } if(m_logger.isDebugEnabled() && cval != null) m_logger.debug("Adding cval " + cval.getId() + " type: " + cval.getType() + " to: " + sig.getId()); sig.addCertValue(cval); m_sbCollectItem = new StringBuffer(); } // the following elements switch collect mode // in and out // <DataFile> boolean bDfDdoc13Bad = false; if(tag.equals("DataFile")) { String ContentType = null, Filename = null, Id = null, MimeType = null, Size = null, DigestType = null, Codepage = null; byte[] DigestValue = null; m_digest = null; // init to null if (m_doc != null && m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) && m_doc.getVersion().equals(SignedDoc.VERSION_1_3)) { m_xmlnsAttr = SignedDoc.xmlns_digidoc13; bDfDdoc13Bad = true; // possible case for ddoc 1.3 invalid namespace problem } else m_xmlnsAttr = null; ArrayList dfAttrs = new ArrayList(); for (int i = 0; i < attrs.getLength(); i++) { String key = attrs.getQName(i); if (key.equals("ContentType")) { ContentType = attrs.getValue(i); } else if (key.equals("Filename")) { Filename = attrs.getValue(i); if(Filename.indexOf('/') != -1 || Filename.indexOf('\\') != -1) { DigiDocException ex = new DigiDocException(DigiDocException.ERR_DF_NAME, "Failed to parse DataFile name. Invalid file name!", null); handleSAXError(ex); } } else if (key.equals("Id")) { Id = attrs.getValue(i); } else if (key.equals("MimeType")) { MimeType = attrs.getValue(i); } else if (key.equals("Size")) { Size = attrs.getValue(i); } else if (key.equals("DigestType")) { DigestType = attrs.getValue(i); } else if (key.equals("Codepage")) { Codepage = attrs.getValue(i); } else if (key.equals("DigestValue")) { DigestValue = Base64Util.decode(attrs.getValue(i)); } else { try { if (!key.equals("xmlns")) { DataFileAttribute attr = new DataFileAttribute(key, attrs.getValue(i)); dfAttrs.add(attr); } else { bDfDdoc13Bad = false; // nope, this one has it's own xmlns } } catch (DigiDocException ex) { handleSAXError(ex); } } // else } // for if(m_nCollectMode == 0) { try { DataFile df = new DataFile(Id, ContentType, Filename, MimeType, m_doc); m_dfCacheOutStream = null; // default is don't use cache file if (Size != null) df.setSize(Long.parseLong(Size)); if (DigestValue != null) { if(m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) df.setAltDigest(DigestValue); else if(ContentType != null && ContentType.equals(DataFile.CONTENT_HASHCODE)) df.setDigestValue(DigestValue); } if (Codepage != null) df.setInitialCodepage(Codepage); for (int i = 0; i < dfAttrs.size(); i++) df.addAttribute((DataFileAttribute) dfAttrs.get(i)); // enable caching if requested if(m_tempDir != null) { File fCache = new File(m_tempDir + File.separator + df.getFileName()); if(m_logger.isDebugEnabled()) m_logger.debug("Parser temp DF: " + Id + " size: " + df.getSize() + " cache-file: " + fCache.getAbsolutePath()); m_dfCacheOutStream = new FileOutputStream(fCache); df.setCacheFile(fCache); } else if(df.schouldUseTempFile()) { File fCache = df.createCacheFile(); if(m_logger.isDebugEnabled()) m_logger.debug("Df-temp DF: " + Id + " size: " + df.getSize() + " cache-file: " + fCache.getAbsolutePath()); df.setCacheFile(fCache); m_dfCacheOutStream = new FileOutputStream(fCache); } m_doc.addDataFile(df); } catch (IOException ex) { handleSAXError(ex); } catch (DigiDocException ex) { handleSAXError(ex); } } m_nCollectMode++; // try to anticipate how much memory we need for collecting this <DataFile> try { if(Size != null) { int nSize = Integer.parseInt(Size); if(!ContentType.equals(DataFile.CONTENT_HASHCODE)) { if(ContentType.equals(DataFile.CONTENT_EMBEDDED_BASE64)) { nSize *= 2; m_bCollectDigest = true; if(m_logger.isDebugEnabled()) m_logger.debug("Start collecting digest"); } if(m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) m_bCollectDigest = false; if(m_logger.isDebugEnabled()) m_logger.debug("Allocating buf: " + nSize + " Element: " + qName + " lname: " + lName + " uri: " + namespaceURI); if(m_dfCacheOutStream == null) // if we use temp files then we don't cache in memory m_sbCollectChars = new StringBuffer(nSize); } } } catch(Exception ex) { m_logger.error("Error: " + ex); } } // if(tag.equals("SignedInfo")) { if (m_nCollectMode == 0) { try { if (m_doc != null && (m_doc.getVersion().equals(SignedDoc.VERSION_1_3) || m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) || m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML))) m_xmlnsAttr = null; else m_xmlnsAttr = SignedDoc.xmlns_xmldsig; Signature sig = getLastSignature(); SignedInfo si = new SignedInfo(sig); if(sig != null) { sig.setSignedInfo(si); String Id = attrs.getValue("Id"); if(Id != null) si.setId(Id); } } catch (DigiDocException ex) { handleSAXError(ex); } } m_nCollectMode++; m_sbCollectChars = new StringBuffer(1024); } // <SignedProperties> if(tag.equals("SignedProperties")) { String Id = attrs.getValue("Id"); String Target = attrs.getValue("Target"); if (m_nCollectMode == 0) { try { if(m_doc != null && (m_doc.getVersion().equals(SignedDoc.VERSION_1_3) || m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC))) m_xmlnsAttr = null; else m_xmlnsAttr = SignedDoc.xmlns_xmldsig; Signature sig = getLastSignature(); SignedProperties sp = new SignedProperties(sig); sp.setId(Id); if(Target != null) sp.setTarget(Target); sig.setSignedProperties(sp); } catch (DigiDocException ex) { handleSAXError(ex); } } m_nCollectMode++; m_sbCollectChars = new StringBuffer(2048); } // <XAdESSignatures> if(tag.equals("XAdESSignatures") && m_nCollectMode == 0) { if (m_logger.isDebugEnabled()) m_logger.debug("Start collecting <XAdESSignatures>"); m_sbCollectSignature = new StringBuffer(); } // <Signature> if(tag.equals("Signature") && m_nCollectMode == 0) { if (m_logger.isDebugEnabled()) m_logger.debug("Start collecting <Signature>"); if(m_doc == null) { DigiDocException ex = new DigiDocException(DigiDocException.ERR_PARSE_XML, "Invalid signature format. Missing signed container root element.", null); handleSAXError(ex); // now stop parsing SAXDigiDocException sex1 = new SAXDigiDocException("Invalid signature format. Missing signed container root element."); throw sex1; } String str1 = attrs.getValue("Id"); Signature sig = null; // in case of ddoc-s try find existing signature but not in case of bdoc-s. // to support libc++ buggy implementation with non-unique id atributes if(m_doc != null && !m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) sig = m_doc.findSignatureById(str1); if(m_doc != null && m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) && m_doc.getVersion().equals(SignedDoc.BDOC_VERSION_2_1)) { m_doc.addSignatureProfile(str1, SignedDoc.BDOC_PROFILE_TM); if(m_doc.getProfile() == null || !m_doc.getProfile().equals(SignedDoc.BDOC_PROFILE_TM)) m_doc.setProfile(SignedDoc.BDOC_PROFILE_TM); } if(sig == null || (sig.getId() != null && !sig.getId().equals(str1))) { if (m_logger.isDebugEnabled()) m_logger.debug("Create signature: " + str1); if(m_doc != null) { sig = new Signature(m_doc); try { sig.setId(str1); } catch (DigiDocException ex) { handleSAXError(ex); } sig.setPath(m_fileName); sig.setComment(m_sigComment); String sProfile = m_doc.findSignatureProfile(m_fileName); if(sProfile == null) sProfile = m_doc.findSignatureProfile(sig.getId()); if(sProfile != null) sig.setProfile(sProfile); /*if(sProfile == null && (m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML))) sig.setProfile(SignedDoc.BDOC_PROFILE_TM);*/ m_doc.addSignature(sig); if (m_logger.isDebugEnabled()) m_logger.debug("Sig1: " + m_fileName + " profile: " + sProfile + " doc: " + ((m_doc != null) ? "OK" : "NULL")); } else { m_sig = new Signature(null); m_sig.setPath(m_fileName); m_sig.setComment(m_sigComment); String sProfile = null; if(m_doc != null && m_fileName != null) sProfile = m_doc.findSignatureProfile(m_fileName); if(sProfile != null) m_sig.setProfile(sProfile); if (m_logger.isDebugEnabled()) m_logger.debug("Sig2: " + m_fileName + " profile: " + sProfile); sig = m_sig; } for(int j = 0; (m_doc != null) && (j < m_doc.countSignatures()); j++) { Signature sig2 = m_doc.getSignature(j); if(sig2 != null && sig != null && m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) && sig2.getId() != null && sig.getId() != null && !sig2.getId().equals(sig.getId()) && sig2.getPath() != null && sig.getPath() != null && sig2.getPath().equals(sig.getPath())) { m_logger.error("Signatures: " + sig.getId() + " and " + sig2.getId() + " are in same file: " + sig.getPath()); DigiDocException ex = new DigiDocException(DigiDocException.ERR_PARSE_XML, "More than one signature in signatures.xml file is unsupported", null); handleSAXError(ex); } } } if(m_sbCollectSignature == null) m_sbCollectSignature = new StringBuffer(); } // <SignatureValue> if(tag.equals("SignatureValue") && m_nCollectMode == 0) { m_strSigValTs = null; m_nCollectMode++; m_sbCollectChars = new StringBuffer(1024); } // <SignatureTimeStamp> if(tag.equals("SignatureTimeStamp") && m_nCollectMode == 0) { if(m_sig != null) m_sig.setProfile(SignedDoc.BDOC_PROFILE_TS); m_doc.setProfile(SignedDoc.BDOC_PROFILE_TS); m_strSigAndRefsTs = null; m_nCollectMode++; m_sbCollectChars = new StringBuffer(2048); } // collect <Signature> data if(m_sbCollectSignature != null) { m_sbCollectSignature.append("<"); m_sbCollectSignature.append(qName); for (int i = 0; i < attrs.getLength(); i++) { m_sbCollectSignature.append(" "); m_sbCollectSignature.append(attrs.getQName(i)); m_sbCollectSignature.append("=\""); String s = attrs.getValue(i); s = s.replaceAll("&", "&"); m_sbCollectSignature.append(s); m_sbCollectSignature.append("\""); } m_sbCollectSignature.append(">"); } // if we just switched to collect-mode // collect SAX event data to original XML data // for <DataFile> we don't collect the begin and // end tags unless this an embedded <DataFile> if(m_nCollectMode > 0 || m_sbCollectChars != null) { StringBuffer sb = new StringBuffer(); String sDfTagBad = null; sb.append("<"); sb.append(qName); for (int i = 0; i < attrs.getLength(); i++) { if(attrs.getQName(i).equals("xmlns")) { m_xmlnsAttr = null; // allready have it from document bDfDdoc13Bad = false; } sb.append(" "); sb.append(attrs.getQName(i)); sb.append("=\""); if(m_logger.isDebugEnabled()) m_logger.debug("Attr: " + attrs.getQName(i) + " =\'" + attrs.getValue(i) + "\'"); if(!m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) { sb.append(ConvertUtils.escapeXmlSymbols(attrs.getValue(i))); } else { String sv = attrs.getValue(i); if(attrs.getQName(i).equals("DigestValue") && sv.endsWith(" ")) sv = sv.replaceAll(" ", "\n"); sb.append(sv); } sb.append("\""); } if(bDfDdoc13Bad) sDfTagBad = sb.toString() + ">"; if (m_xmlnsAttr != null) { sb.append(" xmlns=\"" + m_xmlnsAttr + "\""); m_xmlnsAttr = null; } sb.append(">"); //canonicalize & calculate digest over DataFile begin-tag without content if(tag.equals("DataFile") && m_nCollectMode == 1) { String strCan = null; if(!m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) { strCan = sb.toString() + "</DataFile>"; strCan = canonicalizeXml(strCan); strCan = strCan.substring(0, strCan.length() - 11); if(m_logger.isDebugEnabled()) m_logger.debug("Canonicalized: \'" + strCan + "\'"); if(sDfTagBad != null) { strCan = sDfTagBad + "</DataFile>"; strCan = canonicalizeXml(strCan); sDfTagBad = strCan.substring(0, strCan.length() - 11); if(m_logger.isDebugEnabled()) m_logger.debug("Canonicalized alternative: \'" + sDfTagBad + "\'"); } try { updateDigest(ConvertUtils.str2data(strCan)); if(sDfTagBad != null) updateAltDigest(ConvertUtils.str2data(sDfTagBad)); } catch (DigiDocException ex) { handleSAXError(ex); } } } // we don't collect <DataFile> begin and end - tags and we don't collect if we use temp files else { if(m_sbCollectChars != null) m_sbCollectChars.append(sb.toString()); try { if(m_dfCacheOutStream != null) m_dfCacheOutStream.write(ConvertUtils.str2data(sb.toString())); } catch (IOException ex) { handleSAXError(ex); } catch (DigiDocException ex) { handleSAXError(ex); } } } // the following stuff is used also on level 1 // because it can be part of SignedInfo or SignedProperties if(m_nCollectMode == 1) { // <CanonicalizationMethod> if(tag.equals("CanonicalizationMethod")) { String Algorithm = attrs.getValue("Algorithm"); try { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); si.setCanonicalizationMethod(Algorithm); } catch (DigiDocException ex) { handleSAXError(ex); } } // <SignatureMethod> if(tag.equals("SignatureMethod")) { String Algorithm = attrs.getValue("Algorithm"); try { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); si.setSignatureMethod(Algorithm); } catch (DigiDocException ex) { handleSAXError(ex); } } // <Reference> if(tag.equals("Reference")) { String URI = attrs.getValue("URI"); try { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); Reference ref = new Reference(si); String Id = attrs.getValue("Id"); if(Id != null) ref.setId(Id); ref.setUri(ConvertUtils.unescapeXmlSymbols(ConvertUtils.uriDecode(URI))); String sType = attrs.getValue("Type"); if(sType != null) ref.setType(sType); si.addReference(ref); } catch (DigiDocException ex) { handleSAXError(ex); } } // <Transform> /*if(tag.equals("Transform")) { String Algorithm = attrs.getValue("Algorithm"); if(m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) || m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML)) { DigiDocException ex = new DigiDocException(DigiDocException.ERR_TRANSFORMS, "Transform elements are currently not supported ", null); handleSAXError(ex); } }*/ // <X509SerialNumber> if(tag.equals("X509SerialNumber") && m_doc != null && m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML)) { String sXmlns = attrs.getValue("xmlns"); if(sXmlns == null || !sXmlns.equals(SignedDoc.xmlns_xmldsig)) { if(m_logger.isDebugEnabled()) m_logger.debug("X509SerialNumber has none or invalid namespace: " + sXmlns); DigiDocException ex = new DigiDocException(DigiDocException.ERR_ISSUER_XMLNS, "X509SerialNumber has none or invalid namespace: " + sXmlns, null); handleSAXError(ex); } } // <X509IssuerName> if(tag.equals("X509IssuerName") && m_doc != null && m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML)) { String sXmlns = attrs.getValue("xmlns"); if(sXmlns == null || !sXmlns.equals(SignedDoc.xmlns_xmldsig)) { if(m_logger.isDebugEnabled()) m_logger.debug("X509IssuerName has none or invalid namespace: " + sXmlns); DigiDocException ex = new DigiDocException(DigiDocException.ERR_ISSUER_XMLNS, "X509IssuerName has none or invalid namespace: " + sXmlns, null); handleSAXError(ex); } } // <SignatureProductionPlace> if(tag.equals("SignatureProductionPlace")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignatureProductionPlace spp = new SignatureProductionPlace(); sp.setSignatureProductionPlace(spp); } catch (DigiDocException ex) { handleSAXError(ex); } } } // the following is collected anyway independent of collect mode // <SignatureValue> if(tag.equals("SignatureValue")) { String Id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); SignatureValue sv = new SignatureValue(sig, Id); } catch (DigiDocException ex) { handleSAXError(ex); } } // <OCSPRef> if(tag.equals("OCSPRef")) { OcspRef orf = new OcspRef(); Signature sig = getLastSignature(); UnsignedProperties usp = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = usp.getCompleteRevocationRefs(); rrefs.addOcspRef(orf); } // <DigestMethod> if(tag.equals("DigestMethod")) { String Algorithm = attrs.getValue("Algorithm"); try { if(m_tags.search("Reference") != -1) { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); Reference ref = si.getLastReference(); ref.setDigestAlgorithm(Algorithm); } else if(m_tags.search("SigningCertificate") != -1) { Signature sig = getLastSignature(); CertID cid = sig.getOrCreateCertIdOfType(CertID.CERTID_TYPE_SIGNER); cid.setDigestAlgorithm(Algorithm); } else if(m_tags.search("CompleteCertificateRefs") != -1) { Signature sig = getLastSignature(); CertID cid = sig.getLastCertId(); // initially set to unknown type ! cid.setDigestAlgorithm(Algorithm); } else if(m_tags.search("CompleteRevocationRefs") != -1) { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); OcspRef orf = rrefs.getLastOcspRef(); if(orf != null) orf.setDigestAlgorithm(Algorithm); } else if(m_tags.search("SigPolicyHash") != -1) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spi = sp.getSignaturePolicyIdentifier(); SignaturePolicyId sppi = spi.getSignaturePolicyId(); sppi.setDigestAlgorithm(Algorithm); } } catch (DigiDocException ex) { handleSAXError(ex); } } // <Cert> if(tag.equals("Cert")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); if(m_tags.search("SigningCertificate") != -1) { CertID cid = sig.getOrCreateCertIdOfType(CertID.CERTID_TYPE_SIGNER); if(id != null) cid.setId(id); } if(m_tags.search("CompleteCertificateRefs") != -1) { //CertID cid = new CertID(); CertID cid = sig.getOrCreateCertIdOfType(CertID.CERTID_TYPE_RESPONDER); if(id != null) cid.setId(id); sig.addCertID(cid); } } catch (DigiDocException ex) { handleSAXError(ex); } } // <AllDataObjectsTimeStamp> if(tag.equals("AllDataObjectsTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_ALL_DATA_OBJECTS); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <IndividualDataObjectsTimeStamp> if(tag.equals("IndividualDataObjectsTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_INDIVIDUAL_DATA_OBJECTS); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <SignatureTimeStamp> if(tag.equals("SignatureTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_SIGNATURE); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <SigAndRefsTimeStamp> if(tag.equals("SigAndRefsTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <RefsOnlyTimeStamp> if(tag.equals("RefsOnlyTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_REFS_ONLY); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <ArchiveTimeStamp> if(tag.equals("ArchiveTimeStamp")) { String id = attrs.getValue("Id"); try { Signature sig = getLastSignature(); TimestampInfo ts = new TimestampInfo(id, TimestampInfo.TIMESTAMP_TYPE_ARCHIVE); sig.addTimestampInfo(ts); } catch (DigiDocException ex) { handleSAXError(ex); } } // <Include> if(tag.equals("Include")) { String uri = attrs.getValue("URI"); try { Signature sig = getLastSignature(); TimestampInfo ts = sig.getLastTimestampInfo(); IncludeInfo inc = new IncludeInfo(uri); ts.addIncludeInfo(inc); } catch (DigiDocException ex) { handleSAXError(ex); } } // <CompleteCertificateRefs> if(tag.equals("CompleteCertificateRefs")) { String Target = attrs.getValue("Target"); try { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteCertificateRefs crefs = new CompleteCertificateRefs(); up.setCompleteCertificateRefs(crefs); crefs.setUnsignedProperties(up); } catch (DigiDocException ex) { handleSAXError(ex); } } // <CompleteRevocationRefs> if(tag.equals("CompleteRevocationRefs")) { try { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = new CompleteRevocationRefs(); up.setCompleteRevocationRefs(rrefs); rrefs.setUnsignedProperties(up); } catch (DigiDocException ex) { handleSAXError(ex); } } // <OCSPIdentifier> if(tag.equals("OCSPIdentifier")) { String URI = attrs.getValue("URI"); try { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); OcspRef orf = rrefs.getLastOcspRef(); orf.setUri(URI); } catch (DigiDocException ex) { handleSAXError(ex); } } // <SignaturePolicyIdentifier> if(tag.equals("SignaturePolicyIdentifier")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid == null) { spid = new SignaturePolicyIdentifier(null); sp.setSignaturePolicyIdentifier(spid); } } catch (DigiDocException ex) { handleSAXError(ex); } } // <SignaturePolicyId> if(tag.equals("SignaturePolicyId")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid == null) { spid = new SignaturePolicyIdentifier(null); sp.setSignaturePolicyIdentifier(spid); } SignaturePolicyId spi = spid.getSignaturePolicyId(); if(spi == null) { spi = new SignaturePolicyId(null); spid.setSignaturePolicyId(spi); } } catch (DigiDocException ex) { handleSAXError(ex); } } // <SigPolicyId> // cannot handle alone because we need mandatory Identifier value // <Identifier> if(tag.equals("Identifier")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid == null) { spid = new SignaturePolicyIdentifier(null); sp.setSignaturePolicyIdentifier(spid); } SignaturePolicyId spi = spid.getSignaturePolicyId(); if(spi == null) { spi = new SignaturePolicyId(null); spid.setSignaturePolicyId(spi); } String sQualifier = attrs.getValue("Qualifier"); Identifier id = new Identifier(sQualifier); ObjectIdentifier oi = spi.getSigPolicyId(); if(oi == null) oi = new ObjectIdentifier(id); else oi.setIdentifier(id); spi.setSigPolicyId(oi); } catch (DigiDocException ex) { handleSAXError(ex); } } // <SigPolicyQualifier> if(tag.equals("SigPolicyQualifier")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid == null) { spid = new SignaturePolicyIdentifier(null); sp.setSignaturePolicyIdentifier(spid); } SignaturePolicyId spi = spid.getSignaturePolicyId(); if(spi == null) { spi = new SignaturePolicyId(null); spid.setSignaturePolicyId(spi); } SigPolicyQualifier spq = new SigPolicyQualifier(); spi.addSigPolicyQualifier(spq); } catch (DigiDocException ex) { handleSAXError(ex); } } // <DataObjectFormat> if(tag.equals("DataObjectFormat")) { Signature sig = getLastSignature(); try { if(sig != null) { SignedProperties sp = sig.getSignedProperties(); if(sp != null) { SignedDataObjectProperties sdps = sp.getSignedDataObjectProperties(); if(sdps == null) { sdps = new SignedDataObjectProperties(); sp.setSignedDataObjectProperties(sdps); } String sObjectReference = attrs.getValue("ObjectReference"); DataObjectFormat dof = new DataObjectFormat(sObjectReference); sdps.addDataObjectFormat(dof); } } } catch (DigiDocException ex) { handleSAXError(ex); } } // <NonceAlgorithm> - give error? if(tag.equals("NonceAlgorithm")) { } // the following stuff is ignored in collect mode // because it can only be the content of a higher element if(m_nCollectMode == 0) { // <SignedDoc> if(tag.equals("SignedDoc")) { String format = null, version = null; for(int i = 0; i < attrs.getLength(); i++) { String key = attrs.getQName(i); if(key.equals("format")) format = attrs.getValue(i); if(key.equals("version")) version = attrs.getValue(i); } try { m_doc = new SignedDoc(); m_doc.setFormat(format); m_doc.setVersion(version); if(format != null && (format.equals(SignedDoc.FORMAT_SK_XML) || format.equals(SignedDoc.FORMAT_DIGIDOC_XML))) { m_doc.setProfile(SignedDoc.BDOC_PROFILE_TM); // in ddoc format we used only TM } } catch (DigiDocException ex) { handleSAXError(ex); } } // <Signature> /*if(qName.equals("Signature")) { String Id = attrs.getValue("Id"); try { Signature sig = new Signature(m_doc); if(Id != null) sig.setId(Id); m_doc.addSignature(sig); } catch (DigiDocException ex) { handleSAXError(ex); } }*/ // <KeyInfo> if(tag.equals("KeyInfo")) { try { KeyInfo ki = new KeyInfo(); String Id = attrs.getValue("Id"); if(Id != null) ki.setId(Id); Signature sig = getLastSignature(); sig.setKeyInfo(ki); ki.setSignature(sig); } catch (DigiDocException ex) { handleSAXError(ex); } } // <UnsignedProperties> if(tag.equals("UnsignedProperties")) { String Target = attrs.getValue("Target"); try { Signature sig = getLastSignature(); UnsignedProperties up = new UnsignedProperties(sig); sig.setUnsignedProperties(up); } catch (DigiDocException ex) { handleSAXError(ex); } } // <EncapsulatedOCSPValue> if(tag.equals("EncapsulatedOCSPValue")) { String Id = attrs.getValue("Id"); Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); Notary not = new Notary(); if(Id != null) not.setId(Id); not.setId(Id); up.addNotary(not); if(sig.getProfile() == null && (m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML))) sig.setProfile(SignedDoc.BDOC_PROFILE_TM); } } // if(m_nCollectMode == 0) } private static final String xmlnsEtsi = "http://uri.etsi.org/01903/v1.3.2#"; private static final String xmlnsDs = "http://www.w3.org/2000/09/xmldsig#"; private static final String xmlnsAsic = "http://uri.etsi.org/02918/v1.2.1#"; //private static final String xmlnsNonce = "http://www.sk.ee/repository/NonceAlgorithm"; private TreeSet collectNamespaces(String sCanInfo, TreeSet tsOtherAttr) { TreeSet ts = new TreeSet(); // find element header int p1 = -1, p2 = -1; p1 = sCanInfo.indexOf('>'); if(p1 != -1) { String sHdr = sCanInfo.substring(0, p1); if(m_logger.isDebugEnabled()) m_logger.debug("Header: " + sHdr); String[] toks = sHdr.split(" "); for(int i = 0; (toks != null) && (i < toks.length); i++) { String tok = toks[i]; if(tok != null && tok.trim().length() > 0 && tok.charAt(0) != '<') { if(tok.indexOf("xmlns") != -1) ts.add(tok); else tsOtherAttr.add(tok); } } } return ts; } private void addNamespaceIfMissing(TreeSet ts, String ns, String pref) { boolean bF = false; Iterator iNs = ts.iterator(); while(iNs.hasNext()) { String s = (String)iNs.next(); if(s != null && s.indexOf(ns) != -1) { bF = true; break; } } if(!bF) { StringBuffer sb = new StringBuffer("xmlns"); if(pref != null) { sb.append(":"); sb.append(pref); } sb.append("=\""); sb.append(ns); sb.append("\""); ts.add(sb.toString()); } } private String getPrefOfNs(String ns) { if(ns.indexOf(xmlnsDs) != -1) return m_nsDsPref; if(ns.indexOf(xmlnsEtsi) != -1) return m_nsXadesPref; if(ns.indexOf(xmlnsAsic) != -1) return m_nsAsicPref; return null; } private byte[] addNamespaces(byte[] bCanInfo, boolean bDsNs, boolean bEtsiNs, String dsNsPref, String xadesNsPref, boolean bAsicNs, String asicPref) { byte[] bInfo = bCanInfo; try { String s1 = new String(bCanInfo, "UTF-8"); if(m_logger.isDebugEnabled()) m_logger.debug("Input xml:\n------\n" + s1 + "\n------\n"); TreeSet tsOtherAttr = new TreeSet(); TreeSet tsNs = collectNamespaces(s1, tsOtherAttr); Iterator iNs = tsNs.iterator(); while(iNs.hasNext()) { String s = (String)iNs.next(); m_logger.debug("Has ns: " + s); } iNs = tsOtherAttr.iterator(); while(iNs.hasNext()) { String s = (String)iNs.next(); m_logger.debug("Other attr: " + s); } if(bDsNs) addNamespaceIfMissing(tsNs, xmlnsDs, dsNsPref); if(bEtsiNs) addNamespaceIfMissing(tsNs, xmlnsEtsi, xadesNsPref); if(bAsicNs) addNamespaceIfMissing(tsNs, xmlnsAsic, asicPref); iNs = tsNs.iterator(); while(iNs.hasNext()) { String s = (String)iNs.next(); m_logger.debug("Now has ns: " + s); } // put back in header int p1 = s1.indexOf(' '); int p2 = s1.indexOf('>'); if(p1 > p2) p1 = p2; // if <SignedInfo> has no atributes String sRest = s1.substring(p2); StringBuffer sb = new StringBuffer(); sb.append(s1.substring(0, p1)); iNs = tsNs.iterator(); while(iNs.hasNext()) { sb.append(" "); String s = (String)iNs.next(); sb.append(s); } iNs = tsOtherAttr.iterator(); while(iNs.hasNext()) { sb.append(" "); String s = (String)iNs.next(); sb.append(s); } sb.append(sRest); bInfo = sb.toString().getBytes("UTF-8"); if(m_logger.isDebugEnabled()) m_logger.debug("Modified xml:\n------\n" + sb.toString() + "\n------\n"); } catch(Exception ex) { m_logger.error("Error adding namespaces: " + ex); } return bInfo; // default is to return original content } private byte[] addNamespaceOnChildElems(byte[] bCanInfo, String nsPref, String nsUri) { byte[] bInfo = bCanInfo; try { String s1 = new String(bCanInfo, "UTF-8"); if(m_logger.isDebugEnabled()) m_logger.debug("AddChildNs: " + nsPref + "=" + nsUri + " Input xml:\n------\n" + s1 + "\n------\n"); // find boundarys of root elem int p1 = s1.indexOf('>')+1; int p2 = s1.lastIndexOf('<'); String sRest = s1.substring(p2); StringBuffer sb = new StringBuffer(); sb.append(s1.substring(0, p1)); int p3 = p1, p4 = 0, p5 = 0, p6 = 0; do { boolean bCopy = true; p4 = s1.indexOf('<', p3); // possible whitespace if(p4 > p3+1) sb.append(s1.substring(p3, p4)); p3 = p4; p4 = s1.indexOf('>', p3) + 1; if(s1.charAt(p3) == '<' && s1.charAt(p3+1) != '/') { p5 = s1.indexOf(':', p3); if(p5 > p3 && p5 < p4) { String pref = s1.substring(p3+1, p5); if(pref != null && pref.equals(nsPref)) { p6 = s1.indexOf(' ', p5); if(p6 > p4) p6 = p4 - 1; sb.append(s1.substring(p3, p6)); sb.append(" xmlns:"); sb.append(nsPref); sb.append("=\""); sb.append(nsUri); sb.append("\""); bCopy = false; sb.append(s1.substring(p6, p4)); } } } if(bCopy) sb.append(s1.substring(p3, p4)); if(p4 > 0 && p4 < p2) p3 = p4; } while (p4 > 0 && p4 < p2); sb.append(sRest); bInfo = sb.toString().getBytes("UTF-8"); if(m_logger.isDebugEnabled()) m_logger.debug("Modified xml:\n------\n" + sb.toString() + "\n------\n"); } catch(Exception ex) { m_logger.error("Error adding namespaces: " + ex); } return bInfo; // default is to return original content } /** * End Element handler * @param namespaceURI namespace URI * @param lName local name * @param qName qualified name */ public void endElement(String namespaceURI, String sName, String qName) throws SAXException { if(m_logger.isDebugEnabled()) m_logger.debug("End Element: " + qName + " collect: " + m_nCollectMode); // remove last tag from stack String tag = qName; String nsPref = null; if(tag.indexOf(':') != -1) { tag = qName.substring(qName.indexOf(':') + 1); nsPref = qName.substring(0, qName.indexOf(':')); } if(m_elemCurrent != null) m_elemCurrent = m_elemCurrent.getParent(); String currTag = (String) m_tags.pop(); // collect SAX event data to original XML data // for <DataFile> we don't collect the begin and // end tags unless this an embedded <DataFile> StringBuffer sb = null; if (m_nCollectMode > 0 && (!tag.equals("DataFile") || m_nCollectMode > 1)) { sb = new StringBuffer(); sb.append("</"); sb.append(qName); sb.append(">"); } if (m_sbCollectSignature != null) { m_sbCollectSignature.append("</"); m_sbCollectSignature.append(qName); m_sbCollectSignature.append(">"); } // if we do cache in mem if(m_sbCollectChars != null && sb != null) m_sbCollectChars.append(sb.toString()); // </DataFile> if(tag.equals("DataFile")) { m_nCollectMode--; if (m_nCollectMode == 0) { // close DataFile cache if necessary try { if(m_dfCacheOutStream != null) { if(sb != null) m_dfCacheOutStream.write(ConvertUtils.str2data(sb.toString())); m_dfCacheOutStream.close(); m_dfCacheOutStream = null; } } catch (IOException ex) { handleSAXError(ex); } catch (DigiDocException ex) { handleSAXError(ex); } DataFile df = m_doc.getLastDataFile(); if(df != null && df.getContentType().equals(DataFile.CONTENT_EMBEDDED_BASE64)) { try { if(m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) { String sDf = null; if(m_sbCollectChars != null) { sDf = m_sbCollectChars.toString(); m_sbCollectChars = null; } else if(df.getDfCacheFile() != null) { byte[] data = null; try { data = SignedDoc.readFile(df.getDfCacheFile()); sDf = new String(data); } catch(Exception ex) { m_logger.error("Error reading cache file: " + df.getDfCacheFile() + " - " + ex); } } if(sDf != null) { byte[] bDf = Base64Util.decode(sDf); updateDigest(bDf); } df.setDigest(getDigest()); if(m_logger.isDebugEnabled()) m_logger.debug("Digest: " + df.getId() + " - " + Base64Util.encode(df.getDigest()) + " size: " + df.getSize()); } else { long nSize = df.getSize(); if(m_logger.isDebugEnabled()) m_logger.debug("DF: " + df.getId() + " cache-file: " + df.getDfCacheFile()); if(df.getDfCacheFile() == null) { byte[] b = Base64Util.decode(m_sbCollectChars.toString()); if(m_logger.isDebugEnabled()) m_logger.debug("DF: " + df.getId() + " orig-size: " + nSize + " new size: " + b.length); if(b != null && nSize == 0) nSize = b.length; df.setBodyAsData(ConvertUtils.str2data(m_sbCollectChars.toString(), "UTF-8"), true, nSize); } // calc digest over end tag updateDigest("</DataFile>".getBytes()); //df.setDigestType(SignedDoc.SHA1_DIGEST_TYPE); df.setDigest(getDigest()); //df.setDigestValue(df.getDigest()); if(m_logger.isDebugEnabled()) m_logger.debug("Digest: " + df.getId() + " - " + Base64Util.encode(df.getDigest()) + " size: " + df.getSize()); } if(m_altDigest != null) { //calc digest over end tag updateAltDigest(ConvertUtils.str2data("</DataFile>")); //df.setDigestType(SignedDoc.SHA1_DIGEST_TYPE); df.setAltDigest(getAltDigest()); //df.setDigestValue(df.getDigest()); } m_sbCollectChars = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } // this would throw away whitespace so calculate digest before it //df.setBody(Base64Util.decode(m_sbCollectChars.toString())); } m_bCollectDigest = false; } } // </SignedInfo> if(tag.equals("SignedInfo")) { if(m_nCollectMode > 0) m_nCollectMode--; // calculate digest over the original // XML form of SignedInfo block and save it try { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); String sSigInf = m_sbCollectChars.toString(); if(m_logger.isDebugEnabled()) m_logger.debug("SigInf:\n------\n" + sSigInf + "\n------\n"); //debugWriteFile("SigInfo1.xml", m_sbCollectChars.toString()); byte[] bCanSI = null; if(m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) { bCanSI = sSigInf.getBytes(); } else { CanonicalizationFactory canFac = ConfigManager.instance().getCanonicalizationFactory(); if(si.getCanonicalizationMethod().equals(SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC)) bCanSI = canFac.canonicalize(ConvertUtils.str2data(sSigInf, "UTF-8"), SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC); else bCanSI = canFac.canonicalize(ConvertUtils.str2data(sSigInf, "UTF-8"), SignedDoc.CANONICALIZATION_METHOD_20010315); } si.setOrigDigest(SignedDoc.digestOfType(bCanSI, (m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE))); if(m_logger.isDebugEnabled()) m_logger.debug("SigInf:\n------\n" + new String(bCanSI) + "\n------\nHASH: " + Base64Util.encode(si.getOrigDigest())); if(m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) /*|| m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)*/) { boolean bEtsiNs = false, bAsicNs = false; if(m_nsXadesPref != null && m_nsXadesPref.length() > 0) bEtsiNs = true; if(m_nsAsicPref != null && m_nsAsicPref.length() > 0) bAsicNs = true; if(si.getCanonicalizationMethod().equals(SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC)) { bAsicNs = false; } bCanSI = addNamespaces(bCanSI, true, bEtsiNs, m_nsDsPref, m_nsXadesPref, bAsicNs, m_nsAsicPref); si.setOrigXml(bCanSI); String sDigType = ConfigManager.sigMeth2Type(si.getSignatureMethod()); if(sDigType != null) si.setOrigDigest(SignedDoc.digestOfType(bCanSI, sDigType)); else throw new DigiDocException(DigiDocException.ERR_SIGNATURE_METHOD, "Invalid signature method: " + si.getSignatureMethod(), null); if(m_logger.isDebugEnabled()) m_logger.debug("\nHASH: " + Base64Util.encode(si.getOrigDigest())); } m_sbCollectChars = null; // stop collecting //debugWriteFile("SigInfo2.xml", si.toString()); } catch (DigiDocException ex) { handleSAXError(ex); } } // </SignedProperties> if(tag.equals("SignedProperties")) { if(m_nCollectMode > 0) m_nCollectMode--; // calculate digest over the original // XML form of SignedInfo block and save it //debugWriteFile("SigProps-orig.xml", m_sbCollectChars.toString()); try { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); SignedProperties sp = sig.getSignedProperties(); String sigProp = m_sbCollectChars.toString(); //debugWriteFile("SigProp1.xml", sigProp); byte[] bSigProp = ConvertUtils.str2data(sigProp, "UTF-8"); byte[] bDig0 = SignedDoc.digestOfType(bSigProp, SignedDoc.SHA1_DIGEST_TYPE); if(m_logger.isDebugEnabled()) m_logger.debug("SigProp0:\n------\n" + sigProp + "\n------" + " len: " + sigProp.length() + " sha1 HASH0: " + Base64Util.encode(bDig0)); CanonicalizationFactory canFac = ConfigManager.instance().getCanonicalizationFactory(); byte[] bCanProp = null; if(si.getCanonicalizationMethod().equals(SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC)) bCanProp = canFac.canonicalize(bSigProp, SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC); else bCanProp = canFac.canonicalize(bSigProp, SignedDoc.CANONICALIZATION_METHOD_20010315); if(m_logger.isDebugEnabled()) m_logger.debug("SigProp can:\n------\n" + new String(bCanProp, "UTF-8") + "\n------" + " len: " + bCanProp.length); if(m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) { boolean bNeedDsNs = false; String st1 = new String(bCanProp); if(st1.indexOf("<ds:X509IssuerName>") != -1) { bNeedDsNs = true; } boolean bEtsiNs = false, bAsicNs = false; if(m_nsXadesPref != null && m_nsXadesPref.length() > 0) bEtsiNs = true; if(m_nsAsicPref != null && m_nsAsicPref.length() > 0) bAsicNs = true; if(si.getCanonicalizationMethod().equals(SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC)) { bAsicNs = false; bNeedDsNs = false; } bCanProp = addNamespaces(bCanProp, bNeedDsNs, bEtsiNs, m_nsDsPref, m_nsXadesPref, bAsicNs, m_nsAsicPref); if(si.getCanonicalizationMethod().equals(SignedDoc.CANONICALIZATION_METHOD_2010_10_EXC)) bCanProp = addNamespaceOnChildElems(bCanProp, m_nsDsPref, xmlnsDs); Reference spRef = sig.getSignedInfo().getReferenceForSignedProperties(sp); if(spRef != null) { String sDigType = ConfigManager.digAlg2Type(spRef.getDigestAlgorithm()); if(sDigType != null) sp.setOrigDigest(SignedDoc.digestOfType(bCanProp, sDigType)); if(m_logger.isDebugEnabled()) m_logger.debug("\nHASH: " + Base64Util.encode(sp.getOrigDigest()) + " REF-HASH: " + Base64Util.encode(spRef.getDigestValue())); } } m_sbCollectChars = null; // stop collecting CertID cid = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER); if(cid != null) { if(cid.getId() != null) sp.setCertId(cid.getId()); else if(!sig.getSignedDoc().getVersion().equals(SignedDoc.VERSION_1_3) && !m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) sp.setCertId(sig.getId() + "-CERTINFO"); sp.setCertSerial(cid.getSerial()); sp.setCertDigestAlgorithm(cid.getDigestAlgorithm()); if(cid.getDigestValue() != null) sp.setCertDigestValue(cid.getDigestValue()); if(m_logger.isDebugEnabled()) m_logger.debug("CID: " + cid.getId() + " ser: " + cid.getSerial() + " alg: " + cid.getDigestAlgorithm()); } if(m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || m_doc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) { String sDigType1 = ConfigManager.digAlg2Type(sp.getCertDigestAlgorithm()); if(sDigType1 != null) sp.setOrigDigest(SignedDoc.digestOfType(bCanProp, sDigType1)); if(m_logger.isDebugEnabled()) m_logger.debug("SigProp2:\n------\n" + new String(bCanProp) + "\n------\n" + " len: " + bCanProp.length + " digtype: " + sDigType1 + " HASH: " + Base64Util.encode(sp.getOrigDigest())); } } catch (DigiDocException ex) { handleSAXError(ex); } catch(UnsupportedEncodingException ex) { handleSAXError(ex); } } // </SignatureValue> if(tag.equals("SignatureValue")) { if(m_nCollectMode > 0) m_nCollectMode--; m_strSigValTs = m_sbCollectChars.toString(); m_sbCollectChars = null; // stop collecting } // </CompleteRevocationRefs> if(tag.equals("CompleteRevocationRefs")) { if(m_nCollectMode > 0) m_nCollectMode--; if(m_sbCollectChars != null) m_strSigAndRefsTs = m_strSigValTs + m_sbCollectChars.toString(); m_sbCollectChars = null; // stop collecting } // </Signature> if(tag.equals("Signature")) { if (m_nCollectMode == 0) { if (m_logger.isDebugEnabled()) m_logger.debug("End collecting <Signature>"); try { Signature sig = getLastSignature(); //if (m_logger.isDebugEnabled()) // m_logger.debug("Set sig content:\n---\n" + m_sbCollectSignature.toString() + "\n---\n"); if (m_sbCollectSignature != null && !sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) { sig.setOrigContent(ConvertUtils.str2data(m_sbCollectSignature.toString(), "UTF-8")); //if (m_logger.isDebugEnabled()) // m_logger.debug("SIG orig content set: " + sig.getId() + " len: " + ((sig.getOrigContent() == null) ? 0 : sig.getOrigContent().length)); //debugWriteFile("SIG-" + sig.getId() + ".txt", m_sbCollectSignature.toString()); m_sbCollectSignature = null; // reset collecting } } catch (DigiDocException ex) { handleSAXError(ex); } } } // </XAdESSignatures> if(tag.equals("XAdESSignatures")) { if (m_nCollectMode == 0) { if (m_logger.isDebugEnabled()) m_logger.debug("End collecting <XAdESSignatures>"); try { Signature sig = getLastSignature(); //if (m_logger.isDebugEnabled()) // m_logger.debug("Set sig content:\n---\n" + m_sbCollectSignature.toString() + "\n---\n"); if (m_sbCollectSignature != null) { sig.setOrigContent(ConvertUtils.str2data(m_sbCollectSignature.toString(), "UTF-8")); //if (m_logger.isDebugEnabled()) // m_logger.debug("SIG orig content set: " + sig.getId() + " len: " + ((sig.getOrigContent() == null) ? 0 : sig.getOrigContent().length)); //debugWriteFile("SIG-" + sig.getId() + ".txt", m_sbCollectSignature.toString()); m_sbCollectSignature = null; // reset collecting } } catch (DigiDocException ex) { handleSAXError(ex); } } } // </SignatureTimeStamp> if(tag.equals("SignatureTimeStamp")) { if (m_logger.isDebugEnabled()) m_logger.debug("End collecting <SignatureTimeStamp>"); try { Signature sig = getLastSignature(); TimestampInfo ts = sig.getTimestampInfoOfType(TimestampInfo.TIMESTAMP_TYPE_SIGNATURE); if(ts != null && m_strSigValTs != null) { CanonicalizationFactory canFac = ConfigManager.instance().getCanonicalizationFactory(); byte[] bCanXml = canFac.canonicalize(ConvertUtils.str2data(m_strSigValTs, "UTF-8"), SignedDoc.CANONICALIZATION_METHOD_20010315); //TODO: other diges types for timestamps? byte[] hash = SignedDoc.digest(bCanXml); if(m_logger.isDebugEnabled()) m_logger.debug("SigValTS \n---\n" + new String(bCanXml) + "\n---\nHASH: " + Base64Util.encode(hash)); //debugWriteFile("SigProp2.xml", new String(bCanProp)); ts.setHash(hash); } } catch (DigiDocException ex) { handleSAXError(ex); } } // </SigAndRefsTimeStamp> if(tag.equals("SigAndRefsTimeStamp")) { if (m_logger.isDebugEnabled()) m_logger.debug("End collecting <SigAndRefsTimeStamp>"); try { Signature sig = getLastSignature(); TimestampInfo ts = sig.getTimestampInfoOfType(TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS); if(ts != null && m_strSigAndRefsTs != null) { String canXml = "<a>" + m_strSigAndRefsTs + "</a>"; CanonicalizationFactory canFac = ConfigManager.instance().getCanonicalizationFactory(); byte[] bCanXml = canFac.canonicalize(ConvertUtils.str2data(canXml, "UTF-8"), SignedDoc.CANONICALIZATION_METHOD_20010315); canXml = new String(bCanXml, "UTF-8"); canXml = canXml.substring(3, canXml.length() - 4); //TODO: other diges types for timestamps? byte[] hash = SignedDoc.digest(ConvertUtils.str2data(canXml, "UTF-8")); if(m_logger.isDebugEnabled()) m_logger.debug("SigAndRefsTimeStamp \n---\n" + canXml + "\n---\n" + Base64Util.encode(hash)); //debugWriteFile("SigProp2.xml", new String(bCanProp)); ts.setHash(hash); } } catch (DigiDocException ex) { handleSAXError(ex); } catch(Exception ex) { handleSAXError(ex); } } // the following stuff is used also in // collect mode level 1 because it can be part // of SignedInfo or SignedProperties if (m_nCollectMode == 1) { // </SigningTime> if(tag.equals("SigningTime")) { try { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); sp.setSigningTime(ConvertUtils.string2date(m_sbCollectItem.toString(), m_doc)); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </ClaimedRole> if(tag.equals("ClaimedRole")) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); sp.addClaimedRole(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } // </City> if(tag.equals("City")) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignatureProductionPlace spp = sp.getSignatureProductionPlace(); spp.setCity(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } // </StateOrProvince> if(tag.equals("StateOrProvince")) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignatureProductionPlace spp = sp.getSignatureProductionPlace(); spp.setStateOrProvince(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } // </CountryName> if(tag.equals("CountryName")) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignatureProductionPlace spp = sp.getSignatureProductionPlace(); spp.setCountryName(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } // </PostalCode> if(tag.equals("PostalCode")) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignatureProductionPlace spp = sp.getSignatureProductionPlace(); spp.setPostalCode(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } } // level 1 // the following is collected on any level // </DigestValue> if(tag.equals("DigestValue")) { try { if(m_tags.search("Reference") != -1) { Signature sig = getLastSignature(); SignedInfo si = sig.getSignedInfo(); Reference ref = si.getLastReference(); ref.setDigestValue(Base64Util.decode(m_sbCollectItem.toString())); m_sbCollectItem = null; // stop collecting } else if(m_tags.search("SigningCertificate") != -1) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); sp.setCertDigestValue(Base64Util.decode(m_sbCollectItem.toString())); CertID cid = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER); if(cid != null) cid.setDigestValue(Base64Util.decode(m_sbCollectItem.toString())); m_sbCollectItem = null; // stop collecting } else if(m_tags.search("CompleteCertificateRefs") != -1) { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteCertificateRefs crefs = up.getCompleteCertificateRefs(); CertID cid = crefs.getLastCertId(); if(cid != null) cid.setDigestValue(Base64Util.decode(m_sbCollectItem.toString())); if(m_logger.isDebugEnabled()) m_logger.debug("CertID: " + cid.getId() + " digest: " + m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } else if(m_tags.search("CompleteRevocationRefs") != -1) { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); //if(rrefs.getDigestValue() == null) // ignore sub and root ca ocsp digests OcspRef orf = rrefs.getLastOcspRef(); orf.setDigestValue(Base64Util.decode(m_sbCollectItem.toString())); if(m_logger.isDebugEnabled()) m_logger.debug("Revoc ref: " + m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } else if(m_tags.search("SigPolicyHash") != -1) { Signature sig = getLastSignature(); SignedProperties sp = sig.getSignedProperties(); SignaturePolicyIdentifier spi = sp.getSignaturePolicyIdentifier(); SignaturePolicyId sppi = spi.getSignaturePolicyId(); sppi.setDigestValue(Base64Util.decode(m_sbCollectItem.toString())); if(m_logger.isDebugEnabled()) m_logger.debug("SignaturePolicyId hash: " + m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } } catch(DigiDocException ex) { handleSAXError(ex); } } // </IssuerSerial> if(tag.equals("IssuerSerial") && m_doc != null && !m_doc.getVersion().equals(SignedDoc.VERSION_1_3) && !m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) { try { Signature sig = getLastSignature(); CertID cid = sig.getLastCertId(); if(m_logger.isDebugEnabled()) m_logger.debug("X509SerialNumber 0: " + m_sbCollectItem.toString()); if(cid != null) cid.setSerial(ConvertUtils.string2bigint(m_sbCollectItem.toString())); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </X509SerialNumber> if(tag.equals("X509SerialNumber") && m_doc != null && (m_doc.getVersion().equals(SignedDoc.VERSION_1_3) || m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC))) { try { Signature sig = getLastSignature(); CertID cid = sig.getLastCertId(); if(m_logger.isDebugEnabled()) m_logger.debug("X509SerialNumber: " + m_sbCollectItem.toString()); if(cid != null) cid.setSerial(ConvertUtils.string2bigint(m_sbCollectItem.toString())); if(m_logger.isDebugEnabled()) m_logger.debug("X509SerialNumber: " + cid.getSerial() + " type: " + cid.getType()); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </X509IssuerName> if(tag.equals("X509IssuerName") && m_doc != null && (m_doc.getVersion().equals(SignedDoc.VERSION_1_3) || m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC))) { try { Signature sig = getLastSignature(); CertID cid = sig.getLastCertId(); String s = m_sbCollectItem.toString(); if(cid != null) cid.setIssuer(s); if(m_logger.isDebugEnabled() && cid != null) m_logger.debug("X509IssuerName: " + s + " type: " + cid.getType() + " nr: " + cid.getSerial()); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } //</EncapsulatedTimeStamp> if(tag.equals("EncapsulatedTimeStamp")) { Signature sig = getLastSignature(); TimestampInfo ts = sig.getLastTimestampInfo(); try { //ts.setTimeStampToken(new TimeStampToken(new CMSSignedData(Base64Util.decode(m_sbCollectItem.toString())))); BouncyCastleTimestampFactory tfac = new BouncyCastleTimestampFactory(); ts.setTimeStampToken(tfac.readTsTok(Base64Util.decode(m_sbCollectItem.toString()))); if(m_logger.isDebugEnabled() && ts != null) m_logger.debug("TS: " + ts.getId() + " type: " + ts.getType() + " time: " + ts.getTime() + " digest: " + Base64Util.encode(ts.getMessageImprint())); } catch(Exception ex) { handleSAXError(new DigiDocException(DigiDocException.ERR_TIMESTAMP_RESP, "Invalid timestamp token", ex)); } m_sbCollectItem = null; // stop collecting } // </ResponderID> if(tag.equals("ResponderID")) { try { if(!m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); if(m_logger.isDebugEnabled()) m_logger.debug("ResponderID: " + m_sbCollectItem.toString()); OcspRef orf = rrefs.getLastOcspRef(); orf.setResponderId(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } } catch (DigiDocException ex) { handleSAXError(ex); } } // </ByName> if(tag.equals("ByName")) { try { if(m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC)) { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); if(m_logger.isDebugEnabled()) m_logger.debug("ResponderID by-name: " + m_sbCollectItem.toString()); OcspRef orf = rrefs.getLastOcspRef(); orf.setResponderId(m_sbCollectItem.toString()); m_sbCollectItem = null; // stop collecting } } catch (DigiDocException ex) { handleSAXError(ex); } } // </ProducedAt> if(tag.equals("ProducedAt")) { try { Signature sig = getLastSignature(); UnsignedProperties up = sig.getUnsignedProperties(); CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); OcspRef orf = rrefs.getLastOcspRef(); orf.setProducedAt(ConvertUtils.string2date(m_sbCollectItem.toString(), m_doc)); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // the following stuff is ignored in collect mode // because it can only be the content of a higher element //if (m_nCollectMode == 0) { // </SignatureValue> if(tag.equals("SignatureValue")) { try { Signature sig = getLastSignature(); SignatureValue sv = sig.getSignatureValue(); //debugWriteFile("SigVal.txt", m_sbCollectItem.toString()); if(m_sbCollectItem != null && m_sbCollectItem.length() > 0) sig.setSignatureValue(Base64Util.decode(m_sbCollectItem.toString().trim())); //sv.setValue(Base64Util.decode(m_sbCollectItem.toString().trim())); if(m_logger.isDebugEnabled()) m_logger.debug("SIGVAL mode: " + m_nCollectMode + ":\n--\n" + (m_sbCollectItem != null ? m_sbCollectItem.toString() : "NULL") + "\n---\n len: " + ((sv.getValue() != null) ? sv.getValue().length : 0)); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </X509Certificate> if(tag.equals("X509Certificate")) { try { Signature sig = getLastSignature(); CertValue cval = sig.getLastCertValue(); cval.setCert(SignedDoc.readCertificate(Base64Util.decode(m_sbCollectItem.toString()))); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </EncapsulatedX509Certificate> if(tag.equals("EncapsulatedX509Certificate")) { try { Signature sig = getLastSignature(); CertValue cval = sig.getLastCertValue(); cval.setCert(SignedDoc.readCertificate(Base64Util.decode(m_sbCollectItem.toString()))); m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } // </EncapsulatedOCSPValue> if(tag.equals("EncapsulatedOCSPValue")) { try { Signature sig = getLastSignature(); // first we have to find correct certid and certvalue types findCertIDandCertValueTypes(sig); UnsignedProperties up = sig.getUnsignedProperties(); Notary not = up.getLastNotary(); //if(m_logger.isDebugEnabled()) // m_logger.debug("Notary: " + not.getId() + " resp: " + m_sbCollectItem.toString()); not.setOcspResponseData(Base64Util.decode(m_sbCollectItem.toString())); NotaryFactory notFac = ConfigManager.instance().getNotaryFactory(); notFac.parseAndVerifyResponse(sig, not); // in 1.1 we had bad OCPS digest if (m_doc != null && m_doc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) && m_doc.getVersion().equals(SignedDoc.VERSION_1_1)) { CompleteRevocationRefs rrefs = up.getCompleteRevocationRefs(); OcspRef orf = rrefs.getLastOcspRef(); orf.setDigestValue(SignedDoc.digestOfType(not.getOcspResponseData(), (m_doc.getFormat().equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE))); } m_sbCollectItem = null; // stop collecting } catch (Exception ex) { handleSAXError(ex); } } // bdoc 2.0 // </Identifier> if(tag.equals("Identifier")) { //try { Signature sig = getLastSignature(); if(sig != null) { SignedProperties sp = sig.getSignedProperties(); if(sp != null) { SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid != null) { SignaturePolicyId spi = spid.getSignaturePolicyId(); ObjectIdentifier oi = spi.getSigPolicyId(); if(oi != null) { Identifier id = oi.getIdentifier(); id.setUri(m_sbCollectItem.toString().trim()); if(oi.getIdentifier().getUri().equals(DigiDocGenFactory.BDOC_210_OID)) { try { m_doc.setVersion(SignedDoc.BDOC_VERSION_2_1); } catch(Exception ex) { m_logger.error("Error setting 2.1 ver: " + ex); } } } } } } m_sbCollectItem = null; // stop collecting /*} catch (DigiDocException ex) { handleSAXError(ex); }*/ } // </SPURI> if(tag.equals("SPURI")) { //try { Signature sig = getLastSignature(); if(sig != null) { SignedProperties sp = sig.getSignedProperties(); if(sp != null) { SignaturePolicyIdentifier spid = sp.getSignaturePolicyIdentifier(); if(spid != null) { SignaturePolicyId spi = spid.getSignaturePolicyId(); if(spi != null) spi.addSigPolicyQualifier(new SpUri(m_sbCollectItem.toString().trim())); } } } m_sbCollectItem = null; // stop collecting /*} catch (DigiDocException ex) { handleSAXError(ex); }*/ } // </MimeType> if(tag.equals("MimeType")) { try { Signature sig = getLastSignature(); if(sig != null) { SignedProperties sp = sig.getSignedProperties(); if(sp != null) { SignedDataObjectProperties sdps = sp.getSignedDataObjectProperties(); DataObjectFormat dof = sdps.getLastDataObjectFormat(); if(dof != null) { dof.setMimeType(m_sbCollectItem.toString()); Reference ref = sig.getSignedInfo().getReferenceForDataObjectFormat(dof); if(ref != null) { for(int d = 0; d < sig.getSignedDoc().countDataFiles(); d++) { DataFile df = sig.getSignedDoc().getDataFile(d); if(df.getFileName() != null && df.getFileName().length() > 1 && ref.getUri() != null && ref.getUri().length() > 1) { // normalize uri and filename String sFileName = df.getFileName(); if(sFileName.charAt(0) == '/') sFileName = sFileName.substring(1); String sUri = ref.getUri(); if(sUri.charAt(0) == '/') sUri = sUri.substring(1); if(sFileName.equals(sUri)) { df.setMimeType(m_sbCollectItem.toString()); } } } } } } } m_sbCollectItem = null; // stop collecting } catch (DigiDocException ex) { handleSAXError(ex); } } //} // if(m_nCollectMode == 0) } /** * SAX characters event handler * @param buf received bytes array * @param offset offset to the array * @param len length of data */ public void characters(char buf[], int offset, int len) throws SAXException { String s = new String(buf, offset, len); // just collect the data since it could // be on many lines and be processed in many events if (s != null) { if (m_sbCollectItem != null) { m_sbCollectItem.append(s); //if(m_logger.isDebugEnabled()) // m_logger.debug("IN:\n---\n" + s + "\n---\nCollected:\n---\n" + m_sbCollectItem.toString() + "\n---\n"); } if (m_sbCollectChars != null) { //m_sbCollectChars.append(s); if(m_logger.isDebugEnabled() && m_sbCollectChars.indexOf("SignedInfo") != -1) m_logger.debug("IN: \'" + s + "\' escaped: \'" + ConvertUtils.escapeTextNode(s) + "\'"); m_sbCollectChars.append(ConvertUtils.escapeTextNode(s)); } if (m_sbCollectSignature != null) m_sbCollectSignature.append(ConvertUtils.escapeTextNode(s)); if(m_digest != null && m_bCollectDigest) updateDigest(s.getBytes()); if(m_altDigest != null && m_bCollectDigest) updateAltDigest(s.getBytes()); try { if(m_dfCacheOutStream != null) m_dfCacheOutStream.write(ConvertUtils.str2data(s)); } catch(DigiDocException ex) { handleSAXError(ex); } catch(IOException ex) { handleSAXError(ex); } } } }