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

ee.sk.digidoc.factory.SAXDigiDocFactory Maven / Gradle / Ivy

/*
 * 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 ,  and
	 *  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  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 
	 * @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 = "";
		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);
        //  and  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();
		}
		// 
		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);
			}
		}
		
		// 
		// 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();
		}
		// 
		// 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
		// 
		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 
			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);
		}
		// 
		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);
		}
		// 
		if(tag.equals("XAdESSignatures") && m_nCollectMode == 0) {
			if (m_logger.isDebugEnabled())
				m_logger.debug("Start collecting ");
			m_sbCollectSignature = new StringBuffer();
		}
		// 
		if(tag.equals("Signature") && m_nCollectMode == 0) {
			if (m_logger.isDebugEnabled())
				m_logger.debug("Start collecting ");
			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();
		}
		// 
		if(tag.equals("SignatureValue") && m_nCollectMode == 0) {
			m_strSigValTs = null; 
			m_nCollectMode++;
			m_sbCollectChars = new StringBuffer(1024);
		}
		// 
		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  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  we don't collect the begin and
		// end tags unless this an embedded 
		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() + "";
					strCan = canonicalizeXml(strCan);
					strCan = strCan.substring(0, strCan.length() - 11);
					if(m_logger.isDebugEnabled())
						m_logger.debug("Canonicalized: \'" + strCan + "\'");
					if(sDfTagBad != null) {
						strCan = sDfTagBad + "";
						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  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)  {
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			/*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);
				}
			}*/
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			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
		// 
		if(tag.equals("SignatureValue")) {
			String Id = attrs.getValue("Id");
			try {
				Signature sig = getLastSignature();
				SignatureValue sv = new SignatureValue(sig, Id);
			} catch (DigiDocException ex) {
				handleSAXError(ex);
			}
		}
		// 
		if(tag.equals("OCSPRef")) {
			OcspRef orf = new OcspRef();
			Signature sig = getLastSignature();
			UnsignedProperties usp = sig.getUnsignedProperties();
			CompleteRevocationRefs rrefs = usp.getCompleteRevocationRefs();
			rrefs.addOcspRef(orf);
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		// cannot handle alone because we need mandatory Identifier value
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		
		// 
		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);
			}
		}
		//  - 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) {
			// 
			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);
				}
			}
			// 
			/*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);
				}
			}*/
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			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  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  we don't collect the begin and
		// end tags unless this an embedded 
		StringBuffer sb = null;
		if (m_nCollectMode > 0
			&& (!tag.equals("DataFile") || m_nCollectMode > 1)) {
			sb = new StringBuffer();
			sb.append("");
		}
		if (m_sbCollectSignature != null) {
			m_sbCollectSignature.append("");
		}
		// if we do cache in mem
		if(m_sbCollectChars != null && sb != null)
			m_sbCollectChars.append(sb.toString());
		
		// 
		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("".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(""));
							//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;
			}
		}
		// 
		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);
			}
		}
		// 
		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("") != -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);
			}
		}
		// 
		if(tag.equals("SignatureValue")) {
			if(m_nCollectMode > 0) m_nCollectMode--;
			m_strSigValTs = m_sbCollectChars.toString();
			m_sbCollectChars = null; // stop collecting				
		}		
		// 
		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			
		}
		// 
		if(tag.equals("Signature")) {
			if (m_nCollectMode == 0) {
				if (m_logger.isDebugEnabled()) 
					m_logger.debug("End collecting ");
				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);
				}
			}
		}
		// 
		if(tag.equals("XAdESSignatures")) {
			if (m_nCollectMode == 0) {
				if (m_logger.isDebugEnabled()) 
					m_logger.debug("End collecting ");
				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);
				}
			}
		}
		// 
		if(tag.equals("SignatureTimeStamp")) {
			if (m_logger.isDebugEnabled())
					m_logger.debug("End collecting ");
			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);
			}
		}
		// 
		if(tag.equals("SigAndRefsTimeStamp")) {
			if (m_logger.isDebugEnabled())
					m_logger.debug("End collecting ");
			try {
				Signature sig = getLastSignature();
				TimestampInfo ts = sig.getTimestampInfoOfType(TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS);
				if(ts != null && m_strSigAndRefsTs != null) {
					String canXml = "" + m_strSigAndRefsTs + "";
					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) {
			// 
			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);
				}
			}
			// 
			if(tag.equals("ClaimedRole")) {
				Signature sig = getLastSignature();
				SignedProperties sp = sig.getSignedProperties();
				sp.addClaimedRole(m_sbCollectItem.toString());
				m_sbCollectItem = null; // stop collecting
			}
			// 
			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
			}
			// 
			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
			}
			// 
			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
			}
			// 
			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
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		//
		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
		}
		// 
		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);
			}
		}
		// 
		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);
			}
		}
		
		// 
		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) {
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			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);
				}
			}
			// 
			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
		// 
		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);
			}*/
		}
		// 
		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);
			}*/
		}
		// 
		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);
			}
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy