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

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

package ee.sk.digidoc.factory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigInteger;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;

import javax.crypto.Cipher;

import org.apache.commons.compress.archivers.*;
import org.apache.log4j.Logger;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TimeStampResponse;

import ee.sk.digidoc.*;
import ee.sk.utils.ConfigManager;
import ee.sk.utils.ConvertUtils;

/**
 * Factory class to handle verification of signatures and container 
 * @author Veiko Sinivee
 */
public class DigiDocVerifyFactory {
	//private SignedDoc m_sdoc;
	private static Logger m_logger = Logger.getLogger(DigiDocVerifyFactory.class);
	private static boolean m_prvInited = false;
	
	public static void initProvider() {
		try {
			if(!m_prvInited) {
			// only need this if we must sign the requests
			Provider prv = (Provider)Class.forName(ConfigManager.
					instance().getProperty("DIGIDOC_SECURITY_PROVIDER")).newInstance();
			//prv.list(System.out);
			Security.addProvider(prv);
			m_prvInited = true;
			}
		} catch(Exception ex) {
			m_logger.error("Error initting provider: " + ex);
		}
	}
	
	/**
     * Helper method for comparing
     * digest values
     * @param dig1 first digest value
     * @param dig2 second digest value
     * @return true if they are equal
     */
    public static boolean compareDigests(byte[] dig1, byte[] dig2)
    {
        boolean ok = (dig1 != null) && (dig2 != null) && 
            (dig1.length == dig2.length);
        for(int i = 0; ok && (i < dig1.length); i++)
            if(dig1[i] != dig2[i])
                ok = false;
        return ok;
    }
    
    public static boolean verifyManifestEntries(SignedDoc sdoc, List lerrs)
			throws DigiDocException
	{
    	boolean bOk = true;
    	if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying manifest entries");
    	if(sdoc != null && sdoc.getFormat() != null && sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
			// compare ManifestFileEntry-s and DataFile-s match
			for(int i = 0; i < sdoc.countDataFiles(); i++) {
				DataFile df = sdoc.getDataFile(i);
				boolean bF = false;
				String sFileName = df.getFileName();
				File ft1 = new File(df.getFileName());
				sFileName = ft1.getName();
				if(sdoc.getManifest() != null) {
				  for(int j = 0; j < sdoc.getManifest().getNumFileEntries(); j++) {
					ManifestFileEntry mfe = sdoc.getManifest().getFileEntry(j);
					if(mfe != null) {
					  if(m_logger.isDebugEnabled()) 
			           	m_logger.debug("Manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType() + " df: " + df.getId() + " df-mime: " + df.getMimeType());
					  if(mfe.getFullPath() != null && mfe.getFullPath().equals(sFileName)) {
						if(bF) {
							lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
									"Duplicate ManifestFileEntry for: " + df.getFileName(), null));
							if(m_logger.isDebugEnabled()) 
				            	m_logger.error("Duplicate ManifestFileEntry for: " + df.getFileName());
							bOk = false;
						} else {
							bF = true;
						}
						if(mfe.getMediaType() == null || df.getMimeType() == null || 
							!mfe.getMediaType().equals(df.getMimeType())) {
							lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_MIME_TYPE,
									"DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() + 
									" does not match manifest mime type: " + mfe.getMediaType(), null));
							if(m_logger.isDebugEnabled()) 
				            	m_logger.error("DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() + 
										" does not match manifest mime type: " + mfe.getMediaType());
							bOk = false;
						}
					  }
					}
				  } // for j
				  for(int s = 0; s < sdoc.countSignatures(); s++) {
					  Signature sig = sdoc.getSignature(s);
					  Reference dRef = sig.getSignedInfo().getReferenceForDataFile(df);
					  if(dRef != null) {
						  DataObjectFormat dof = sig.getSignedInfo().getDataObjectFormatForReference(dRef);
						  if(dof != null) {
						    if(df.getMimeType() != null && dof.getMimeType() != null && !dof.getMimeType().equals(df.getMimeType())) {
						    	lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_MIME_TYPE,
										"DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() + 
										" does not match signature: " + sig.getId() + " mime type: " + dof.getMimeType(), null));
						    	if(m_logger.isDebugEnabled()) 
					            	m_logger.error("DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() + 
											" does not match signature: " + sig.getId() + " mime type: " + dof.getMimeType());
								bOk = false;
						    }
						  }
					  }
				  }
				} // for s
				if(!bF) {
					lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
							"Missing ManifestFileEntry for: " + df.getFileName(), null));
					if(m_logger.isDebugEnabled()) 
		            	m_logger.error("Missing ManifestFileEntry1 for: " + sFileName);
				}
			}
			for(int j = 0; j < sdoc.getManifest().getNumFileEntries(); j++) {
				ManifestFileEntry mfe = sdoc.getManifest().getFileEntry(j);
				if(mfe == null) {
					m_logger.error("Invalid manifest entry");
					continue;
				}
				if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("Check manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType());
				if(mfe.getFullPath() != null && mfe.getFullPath().equals("/")) continue; // container root element
				boolean bF = false;
				for(int i = 0; i < sdoc.countDataFiles(); i++) {
				 DataFile df = sdoc.getDataFile(i);
				 String sFileName = df.getFileName();
				 File ft1 = new File(df.getFileName());
				 sFileName = ft1.getName();
				 if(m_logger.isDebugEnabled()) 
		            	m_logger.debug("Manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType() + " found df: " + df.getId() + " df-mime: " + df.getMimeType());
				 if(mfe.getFullPath() != null && mfe.getFullPath().equals(sFileName)) {
					if(bF) {
						lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
								"Duplicate DataFile: " + df.getId() + " with name: " + df.getFileName(), null));
						if(m_logger.isDebugEnabled()) 
			            	m_logger.error("Duplicate DataFile: " + df.getId() + " with name: " + df.getFileName());
						bOk = false;
					} else {
						bF = true;
					}
				 }
				}
				if(!bF) {
					lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
							"Missing DataFile for ManifestFileEntry: " + mfe.getFullPath(), null));
					if(m_logger.isDebugEnabled()) 
		            	m_logger.error("Missing DataFile for ManifestFileEntry: " + mfe.getFullPath());
				}
			}
		}
    	return bOk;
	}
    
    private static final String DIG_TYPE_WARNING = "The current BDoc container uses weaker encryption method than officialy accepted in Estonia. "+
    	"We do not recommend you to add signature to this document. There is an option to re-sign this document in a new container.";
    private static final String DIGIDOC_VERIFY_ALGORITHM = "RSA/NONE/PKCS1Padding";
    
    
    /**
     * Verifies the hash of one data-file
     * @param df DataFile object
     * @param ref Reference object
     * @param lerrs list of errors
     * @return true if ok
     */
	private static boolean verifyDataFileHash(SignedDoc sdoc, DataFile df, Reference ref, List lerrs)
	{
		boolean bOk = true;
		if(df != null) {
			if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Check digest for DF: " + df.getId() + " ref: " + ((ref != null) ? ref.getUri() : "NULL"));
			String sDigType = null;
			if(ref != null)
			sDigType = ConfigManager.digAlg2Type(ref.getDigestAlgorithm());
			if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Check digest for DF: " + df.getId() + " type: " + sDigType);
			byte[] dfDig = null;
            try {
            	if(sDigType != null)
            	dfDig = df.getDigestValueOfType(sDigType);
            } catch(DigiDocException ex) {
                lerrs.add(ex);
                bOk = false;
                m_logger.error("Error calculating hash for df: " + df.getId() + " - " + ex);
                ex.printStackTrace();
                if(ex.getNestedException() != null)
                ex.getNestedException().printStackTrace();
            }
            if(ref != null) {
            	if(m_logger.isDebugEnabled()) 
                	m_logger.debug("Compare digest: " + ((dfDig != null) ? Base64Util.encode(dfDig, 0) : "NONE") + 
                			" hex: " + ((dfDig != null) ? ConvertUtils.bin2hex(dfDig) : "NONE") +
                			" alt digest: " + ((df.getAltDigest() != null) ? Base64Util.encode(df.getAltDigest(), 0) : "NONE") +
                			" to: " + ((ref.getDigestValue() != null) ? Base64Util.encode(ref.getDigestValue()) : "NONE") +
                			" hex: " + ((ref.getDigestValue() != null) ? ConvertUtils.bin2hex(ref.getDigestValue()) : "NONE"));
            	DigiDocException exd = null;
                if(!SignedDoc.compareDigests(ref.getDigestValue(), dfDig)) {
                   lerrs.add((exd = new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE,
                		   "Bad digest for DataFile: " + df.getId(), null)));
                   if(m_logger.isDebugEnabled()) 
                       m_logger.debug("BAD DIGEST for DF: " + df.getId());
                   bOk = false;
                }
                if(!bOk && df.getAltDigest() != null) {
                	if(SignedDoc.compareDigests(ref.getDigestValue(), df.getAltDigest())) {
                		if(m_logger.isDebugEnabled()) {
                          m_logger.debug("DF: " + df.getId() + " alternate digest matches!");
                		  m_logger.debug("GOOD ALT DIGEST for DF: " + df.getId());
                		}
                		if(exd != null)
                		  lerrs.remove(exd);
                		ref.getSignedInfo().getSignature().setAltDigestMatch(true);
                		if(!ref.getSignedInfo().getSignature().getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML))
                		lerrs.add((exd = new DigiDocException(DigiDocException.ERR_DF_INV_HASH_GOOD_ALT_HASH,
                     		   "Bad digest for DataFile: " + df.getId() + " alternate digest matches!", null)));
                		bOk = false;
                	}
                }
                else if(m_logger.isDebugEnabled()) 
                	m_logger.debug("GOOD DIGEST");
            } else {
            	if(m_logger.isDebugEnabled()) 
                    m_logger.debug("No Reference");
                lerrs.add(new DigiDocException(
                    DigiDocException.ERR_DATA_FILE_NOT_SIGNED,
                    "No Reference element for DataFile: " + df.getId(), null));
                bOk = false;
            }
            if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
            	String sFile = df.getFileName();
            	if(sFile != null && (sFile.indexOf('/') != -1 || sFile.indexOf('\\') != -1)) {
            		File fT = new File(sFile);
            		sFile = fT.getName();
            	}
            	ManifestFileEntry mfe = sdoc.getManifest().findFileEntryByPath(sFile);
            	if(m_logger.isDebugEnabled()) {
                    m_logger.debug("DF: " + df.getId() + " file: " + sFile + " manifest-entry: "+ ((mfe != null) ? "OK" : "NULL"));
            	if(mfe == null) {
            		if(m_logger.isDebugEnabled()) 
                        m_logger.debug("No manifest.xml entry for: " + df.getFileName());
                    lerrs.add(new DigiDocException(
                        DigiDocException.ERR_DATA_FILE_FILE_NAME,
                        "No manifest.xml entry for: " + df.getFileName(), null));
                    bOk = false;
            	}
            	}
            }
		} else {
			if(m_logger.isDebugEnabled()) 
                m_logger.debug("Invalid data-file");
			bOk = false;
		}
		return bOk;
	}
	
	/**
	 * Verifies SignedProperties digest
	 * @param sig Signature object
	 * @param lerrs list of errors
	 * @return true if ok
	 */
	private static boolean verifySignedPropretiesHash(Signature sig, List lerrs)
	{
		boolean bOk = true;
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying signed-props of: " + sig.getId());
		SignedProperties sp = sig.getSignedProperties();
		boolean bSha1Check = ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true);
		if(sp != null) {
			Reference ref2 = sig.getSignedInfo().getReferenceForSignedProperties(sp);
			if(ref2 != null && sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
		      ref2.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
		      ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
			  lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
		      if(m_logger.isInfoEnabled()) 
	            m_logger.info("SignedProperties for signature: " + sig.getId() + " has weak digest type: " + ref2.getDigestAlgorithm());
			}
	        if(ref2 != null) {
	            byte[] spDig = null;
	            try {
	                spDig = sp.calculateDigest();
	                if(m_logger.isDebugEnabled()) 
	                	m_logger.debug("SignedProp real digest: " + Base64Util.encode(spDig, 0));
	            } catch(DigiDocException ex) {
	                lerrs.add(ex);
	                bOk = false;
	            }
	            if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("Compare it to: " + Base64Util.encode(ref2.getDigestValue(), 0));
	            if(!SignedDoc.compareDigests(ref2.getDigestValue(), spDig)) {
	              lerrs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE,
	                    "Bad digest for SignedProperties: " + sp.getId(), null));
	              if(m_logger.isDebugEnabled()) 
                      m_logger.debug("BAD DIGEST for sig-prop");
	              bOk = false;
	            }
	            else if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("GOOD DIGEST");
	        } else {
	        	if(m_logger.isDebugEnabled())
                    m_logger.debug("No Reference element for SignedProperties: " + sp.getId());
	            lerrs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED,
	                    "No Reference element for SignedProperties: " + sp.getId(), null));
	            bOk = false;
	        }
	        
		} else {
			if(m_logger.isDebugEnabled()) 
                m_logger.debug("No Reference element for SignedProperties of sig: " + sig.getId());
            lerrs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED,
                    "No Reference element for SignedProperties sig: " + sig.getId(), null));
            bOk = false;
		}
		return bOk;
	}
	
	
	
	/**
     * Verifies the siganture
     * @param digest input data digest
     * @param signature signature value
     * @param cert certificate to be used on verify
     * @param bSoftCert use Sun verificateion api instead
     * @return true if signature verifies
     */
    public static boolean verify(byte[] digest, byte[] signature, X509Certificate cert, boolean bSoftCert, String sigMethod) 
        throws DigiDocException
    {
        boolean rc = false;
        try {
        	if(cert == null)
        		throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid or missing signers cert!", null); 
        	if(bSoftCert) {
        	  String sigType = ConfigManager.instance().sigMeth2SigType(sigMethod, true);
          	  if(m_logger.isDebugEnabled()) 
                	m_logger.debug("Verify xml:\n---\n" + new String(digest) + "\n---\n len: " + 
                			digest.length + " method: " + sigMethod + " sig-type: " + sigType + "\n---\n" + 
                			ConvertUtils.bin2hex(signature) + " sig-len: " + signature.length);
        	  if(sigType == null) 
          		  throw new DigiDocException(DigiDocException.ERR_VERIFY, "Signature method: " + sigMethod + " not provided!", null);
        	  java.security.Signature sig = java.security.Signature.getInstance(sigType, ConfigManager.addProvider());
        	  sig.initVerify(cert.getPublicKey());
              sig.update(digest);
              rc = sig.verify(signature);
            } else {
              if(m_logger.isDebugEnabled()) 
                	m_logger.debug("Verify sig: " + signature.length + " bytes, alg: " + DIGIDOC_VERIFY_ALGORITHM + " sig-alg: " + sigMethod);
              Cipher cryptoEngine = Cipher.getInstance(DIGIDOC_VERIFY_ALGORITHM, "BC");
        	  cryptoEngine.init(Cipher.DECRYPT_MODE, cert);
        	  byte[] decdig = null;
        	  try {
        	   decdig = cryptoEngine.doFinal(signature);
        	  } catch(java.lang.ArrayIndexOutOfBoundsException ex2) {
        		  if(m_logger.isDebugEnabled()) 
                      m_logger.debug("Invalid signature value. Signers cert and signature value don't match! - " + ex2);
        		  throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value! Signers cert and signature value don't match!", ex2);
        	  }
        	  String digType2 = ConfigManager.sigMeth2Type(sigMethod);
          	  String digType1 = ConvertUtils.findDigType(decdig);
        	  if(m_logger.isDebugEnabled()) 
                m_logger.debug("Decrypted digest: \'" + SignedDoc.bin2hex(decdig) + "\' len: " + decdig.length + " has-pref: " + digType1 + 
                		  " must-have: " + digType2 + " alg: " + sigMethod);
        	  if(digType1 != null && digType1.equals(SignedDoc.SHA1_DIGEST_TYPE_BAD)) {
        		  if(m_logger.isDebugEnabled()) 
                  	m_logger.debug("Invalid signature asn.1 prefix with 0x00 byte");
        		  throw new DigiDocException(DigiDocException.ERR_SIGVAL_00, "Invalid signature asn.1 prefix with 0x00 byte", null);
        	  }
        	  if((digType1 == null) ||
        		   (digType2 != null && digType1 != null && !digType2.equals(digType1))) {
            		if(m_logger.isDebugEnabled()) 
                    	m_logger.debug("Signature asn.1 prefix: " + digType1 + " does not match: " + digType2);
            		throw new DigiDocException(DigiDocException.ERR_SIGVAL_ASN1, "Signature asn.1 prefix: " + digType1 + " does not match: " + digType2, null);
              }
        	  byte[] cdigest = ConvertUtils.removePrefix(decdig);
              if(m_logger.isDebugEnabled()) 
              	m_logger.debug("Signed digest: \'" + ((cdigest != null) ? SignedDoc.bin2hex(cdigest) : "NULL") + "\' calc-digest: \'" + SignedDoc.bin2hex(digest) + "\'");
          	  if(decdig != null && cdigest != null &&
          			decdig.length == cdigest.length) {
          		if(m_logger.isDebugEnabled()) 
                  	m_logger.debug("Signature value decrypted len: " + decdig.length + " missing ASN.1 structure prefix");
          		throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value! Signature value decrypted len: " + decdig.length + " missing ASN.1 structure prefix", null);
          	  }
        	  rc = compareDigests(digest, cdigest); 
            }
        	if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Result: " + rc);
            if(!rc)
            	throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value!", null);
        } catch(DigiDocException ex) {
        	throw ex; // pass it on, but check other exceptions
        } catch(Exception ex) {
            DigiDocException.handleException(ex, DigiDocException.ERR_VERIFY);
        }
        return rc;
    }
	
	public static boolean verifySignatureValue(SignedDoc sdoc, Signature sig, List lerrs)
	{
		boolean bOk = true;
		if(sdoc == null) {
			m_logger.error("SignedDoc is null");
			return false;
		}
		if(sig == null) {
			m_logger.error("Signature is null");
			return false;
		}
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying signature value of: " + sig.getId());
		// verify signature value
        try {
            byte[] dig = sig.getSignedInfo().calculateDigest();
            if(m_logger.isDebugEnabled()) 
            	m_logger.debug("SignedInfo real digest: " + Base64Util.encode(dig, 0) + " hex: " + SignedDoc.bin2hex(dig) +
            			" sig: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()) + 
            			" len: " + sig.getSignatureValue().getValue().length);
            if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
            	(sig.getSignedInfo().getSignatureMethod().equals(SignedDoc.RSA_SHA1_SIGNATURE_METHOD) ||
            	 sig.getSignedInfo().getSignatureMethod().equals(SignedDoc.ECDSA_SHA1_SIGNATURE_METHOD)) &&
            	ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
      		    lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
              if(m_logger.isInfoEnabled()) 
            	m_logger.info("Signature: " + sig.getId() + " has weak signature method: " + sig.getSignedInfo().getSignatureMethod());
            }
            if(sig.getSignatureValue() != null && sig.getSignatureValue().getValue() != null && dig != null) {
            	if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && sig.isEllipticCurveSiganture()) {
            		if(m_logger.isDebugEnabled()) 
                    	m_logger.debug("Verify sdoc: " + sdoc.getFormat() + "/" + sdoc.getVersion() + " prefs: " + sdoc.getXmlDsigNs() + "/" + sdoc.getAsicNs() + "/" + sdoc.getXadesNs());
            		//DigiDocXmlGenFactory genFac = new DigiDocXmlGenFactory(sdoc);
            		byte[] xml = sig.getSignedInfo().getOrigXml(); //genFac.signedInfoToXML(sig, sig.getSignedInfo());
            		if(m_logger.isDebugEnabled()) 
                    	m_logger.debug("Verify xml:\n---\n" + new String(xml) + "\n---\n");
            		bOk = verify(xml, sig.getSignatureValue().getValue(), sig.getKeyInfo().getSignersCertificate(), true, sig.getSignedInfo().getSignatureMethod());
            	} else {
            		if(m_logger.isDebugEnabled()) 
                    	m_logger.debug("Verify sig: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()) + 
                    			" len: " + sig.getSignatureValue().getValue().length + " hlen: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()).length());
            		bOk = verify(dig, sig.getSignatureValue().getValue(), sig.getKeyInfo().getSignersCertificate(), false, sig.getSignedInfo().getSignatureMethod());
            	}
            	if(m_logger.isDebugEnabled()) 
            		m_logger.debug("GOOD DIGEST");
            } else {
            	if(m_logger.isDebugEnabled()) 
                    m_logger.debug("Missing signature value!");
            	lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNATURE_VALUE_VALUE, "Missing signature value!", null));
            	bOk = false;
            }
        } catch(DigiDocException ex) {
          lerrs.add(ex);
          if(m_logger.isDebugEnabled()) {
              m_logger.debug("BAD DIGEST for sig-inf: " + sig.getId() + " - " + ex.toString());
          m_logger.debug("TRACE: " + ConvertUtils.getTrace(ex));
          //ex.printStackTrace();
          m_logger.debug("sig-val-len: " + ((sig.getSignatureValue() != null && sig.getSignatureValue().getValue() != null) ? sig.getSignatureValue().getValue().length : 0));
          m_logger.debug("signer: " + ((sig.getKeyInfo() != null && sig.getKeyInfo().getSignersCertificate() != null) ? sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName() : "NULL"));
          }
          bOk = false;  
        }
		return bOk;
	}
	
	/**
	 * Verifies that the signers cert
	 * has been signed by at least one
	 * of the known root certs
	 * @param cert certificate to check
	 */
	public static boolean verifyCertificate(X509Certificate cert, X509Certificate caCert)
		throws DigiDocException {
		boolean rc = false;
		try {
			if (caCert != null) {
				cert.verify(caCert.getPublicKey());
				rc = true;
			}
		} catch (Exception ex) {
			DigiDocException.handleException(ex, DigiDocException.ERR_UNKNOWN_CA_CERT);
		}
		return rc;
	}
	
	/**
	 * Verifies signers cerificate by a trusted CA cert
	 * @param sig Signature object
	 * @param lerrs list for errors
	 * @return
	 */
	public static boolean verifySignersCerificate(Signature sig, List lerrs)
	{
		boolean bOk = true;
		try {
			if(m_logger.isDebugEnabled()) 
	        	m_logger.debug("Verifying CA of signature: " + sig.getId() + " signed-at: " + ConvertUtils.date2string(sig.getSignedProperties().getSigningTime(), sig.getSignedDoc()) + " produced: " + ConvertUtils.date2string(sig.getSignatureProducedAtTime(), sig.getSignedDoc()));
			TrustServiceFactory tslFac = ConfigManager.instance().getTslFactory();
	        if(sig.getKeyInfo().getSignersCertificate() == null) {
	        	lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT, "Signers cert missing!", null));
	        	return false;
	        }
	        X509Certificate caCert = tslFac.findCaForCert(sig.getKeyInfo().getSignersCertificate(), true, sig.getSignatureProducedAtTime());
	        X509Certificate cert = sig.getKeyInfo().getSignersCertificate();
	        if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Check signer: " + cert.getSubjectDN().getName() +
            			" issued by: " + cert.getIssuerDN().getName() +
            			" SUB from: " + ConvertUtils.date2string(cert.getNotBefore(), sig.getSignedDoc()) +
            			" to: " + ConvertUtils.date2string(cert.getNotAfter(), sig.getSignedDoc()) +
            			" by CA: " + ((caCert != null) ? caCert.getSubjectDN().getName() : "NOT FOUND") +
            			" CA from: " + ((caCert != null) ? ConvertUtils.date2string(caCert.getNotBefore(), sig.getSignedDoc()) : "?") +
            			" to: " + ((caCert != null) ? ConvertUtils.date2string(caCert.getNotAfter(), sig.getSignedDoc()) : "?") +
            			" ca-ahel: " + (DigiDocGenFactory.isTestCard(cert) ? "TEST" : "LIVE"));
	        if(caCert != null) {
              bOk = verifyCertificate(cert, caCert);
              if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Signer: " + ConvertUtils.getCommonName(sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName()) +
            			" is issued by trusted CA: " + ConvertUtils.getCommonName(caCert.getSubjectDN().getName()));
	        } else {
	        	if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("CA not found for: " + ConvertUtils.getCommonName(cert.getSubjectDN().getName()));
	        	lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT, "Signers cert not trusted, missing CA cert!", null));
	        	bOk = false;
	        }
	        if(!ConfigManager.isSignatureKey(cert)) {
	        	if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("Signers cert does not have non-repudiation bit set!");
	        	lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT_NONREPUD, "Signers cert does not have non-repudiation bit set!", null));
	        	bOk = false;
	        }
	        
	        CertID cid = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER);
	        if(cert != null && cid != null) {
	        	// verify DN using RDN
	          boolean bMatch = true;
	          List aCertRdns = parseDN(ConvertUtils.convX509Name(cert.getIssuerX500Principal()));
	          List aCertIdRdns = parseDN(cid.getIssuer());
	          if(m_logger.isDebugEnabled()) 
		            m_logger.debug("Signed: " + cid.getIssuer() + " cert: " + ConvertUtils.convX509Name(cert.getIssuerX500Principal()) +
		            		" cert rdn-s: " + aCertRdns.size() + " signed rdn-s: " + aCertIdRdns.size());
	          // don't have to match all RDN entries in cert. Just the signed ones agains the cert. Not opposite
	          for(int i = 0; i < aCertIdRdns.size(); i++) {
	        	  Rdn r1 = (Rdn)aCertIdRdns.get(i);
	        	  if(m_logger.isDebugEnabled()) 
		            m_logger.debug("Signed RDN: " + r1.getId() + "/" +  r1.getValue());
	        	  boolean bF = false;
	        	  for(int j = 0; j < aCertRdns.size(); j++) {
		        	  Rdn r2 = (Rdn)aCertRdns.get(j);
		        	  if(r1.getId() != null && r2.getId() != null && r1.getId().equalsIgnoreCase(r2.getId()) &&
		        		 r1.getValue() != null && r2.getValue() != null && r1.getValue().equalsIgnoreCase(r2.getValue())) {
		        		  bF = true;
		        	  }
	        	  }
	        	  if(!bF) {
	        		  if(m_logger.isDebugEnabled()) 
	  		            m_logger.debug("Different for signed: " + r1.getId() + "/" + r1.getValue());
	        	  } 
	        	  if(!bF && r1.getId() != null && 
	        		(r1.getId().equals("CN") ||
	        		 r1.getId().equals("LT") ||
	        		 r1.getId().equals("ST") ||
	        		 r1.getId().equals("O") ||
	        		 r1.getId().equals("OU") ||
	        		 r1.getId().equals("C") ||
	        		 r1.getId().equals("STREET") ||
	        		 r1.getId().equals("DC") ||
	        		 r1.getId().equals("UID")) ) {
	        		  m_logger.error("No match for signed: " + r1.getId() + "/" + r1.getValue());
	        		  bMatch = false;
	        	  }
	          }
	          if(!bMatch) {
	        	  m_logger.error("Signers cert issuer DN: " + ConvertUtils.convX509Name(cert.getIssuerX500Principal()) + 
	        			  " and signed Issuername: " + cid.getIssuer() + " don't match");
	        	  lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Signing certificate issuer information does not match", null));
	          }
	          // verify cer issuer serial
	          if(cid.getSerial() != null) {
	        	  if(m_logger.isDebugEnabled()) 
			            m_logger.debug("Signed IssuerSerial: " + cid.getSerial().toString() + " cert serial: " + cert.getSerialNumber().toString());
	        	  if(!cid.getSerial().equals(cert.getSerialNumber())) {
	        		  m_logger.error("Signers cert issuer serial: " + cert.getSerialNumber().toString() + 
		        			  " and signed IssuerSerial: " + cid.getSerial().toString() + " don't match");
		        	  lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Signing certificate issuer information does not match", null));
	        	  }
	          }
	        }
        } catch(DigiDocException ex) {
        	if(m_logger.isDebugEnabled()) 
        		m_logger.debug("Signers certificate not trusted for: " + sig.getId());
            lerrs.add(ex);
            bOk = false;
        }
		return bOk;
	}
	
	/**
	 * Verifies signing time of signature (as stored in signed properties)
	 * @param sig Signature object
	 * @param lerrs list of errors
	 * @return true if ok
	 */
	public static boolean verifySigningTime(Signature sig, List lerrs)
	{
		boolean bOk = true;
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying signing time signature: " + sig.getId());
		try {
			Date dProdAt = null;
			if(sig != null && sig.getUnsignedProperties() != null && sig.getUnsignedProperties().getNotary() != null)
				dProdAt = sig.getUnsignedProperties().getNotary().getProducedAt();
			if(dProdAt != null)
				sig.getKeyInfo().getSignersCertificate().checkValidity(dProdAt);
			if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Signers cert: " + ConvertUtils.getCommonName(sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName()) +
            			" was valid on: " + ConvertUtils.date2string(dProdAt, sig.getSignedDoc()));
		} catch(Exception ex) {
        	m_logger.error("Signers certificate has expired for: " + sig.getId());
        	lerrs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED,
                    "Signers certificate has expired!", null));
            bOk = false;
        }
		return bOk;
	}
	
	public static boolean verifySignatureFromLiveAndOcspFromTest(Signature sig, List lerrs)
	{
		boolean bOk = true;
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying live/test for signature: " + sig.getId());
		X509Certificate cert = null, rCert = null;
		if(sig != null) {
		  CertValue cvOcsp = sig.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER);
		  if(sig.getKeyInfo() != null && cvOcsp != null) {
			cert = sig.getKeyInfo().getSignersCertificate();
			rCert = cvOcsp.getCert();
			if(cert != null && rCert != null && 
		        	DigiDocGenFactory.isTestCard(rCert) &&
		        	!DigiDocGenFactory.isTestCard(cert)) {
		        	if(m_logger.isDebugEnabled())
			        	m_logger.debug("Signer from LIVE CA-chain but OCSP from TEST CA-chain!");
		            lerrs.add(new DigiDocException(DigiDocException.ERR_TEST_SIGNATURE, 
		            		"Signer from LIVE CA-chain but OCSP from TEST CA-chain!", null));
		            bOk = false;
			}
		  }
		}
		return bOk;
	}
	
	/**
	 * Verifies OCSP confirmation for signature
	 * @param sig Signature object
	 * @param lerrs list of errors
	 * @return true if ok
	 */
	public static boolean verifySignatureOCSP(Signature sig, List lerrs)
	{
		boolean bOk = true;
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying OCSP for signature: " + sig.getId());
		try {
			if(sig.getUnsignedProperties() != null && sig.getUnsignedProperties().countNotaries() > 0) {
				CertValue cvOcsp = sig.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER);
				CertID cidOcsp = sig.getCertIdOfType(CertID.CERTID_TYPE_RESPONDER);
				X509Certificate rCert = null;
				String sIssuer = null;
				BigInteger sSerial = null;
				byte [] cHash = null;
				if(cvOcsp != null)
					rCert = cvOcsp.getCert();
				//if(cidOcsp == null)
				//	cidOcsp = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER);
				if(cidOcsp != null) {
					sIssuer = cidOcsp.getIssuer();
					sSerial = cidOcsp.getSerial();
					cHash = cidOcsp.getDigestValue();
				}
				X509Certificate cert = sig.getKeyInfo().getSignersCertificate();
		        if(m_logger.isDebugEnabled())
		        	m_logger.debug("Responders cert: " + ((rCert != null) ? rCert.getSerialNumber().toString() : "NULL") + " - " + 
		        			((rCert != null) ? rCert.getSubjectDN().getName() : "NULL") + 
		            	           " complete cert refs nr: " + sSerial + " - " + sIssuer +
		            	           " ca-ahel: " + ((rCert != null) ? (DigiDocGenFactory.isTestCard(rCert) ? "TEST" : "LIVE") : "?"));
		        // signer/ocsp live/test verification moved to utility
		        if(rCert != null && !rCert.getSerialNumber().equals(sSerial) &&
		        	!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
		        	if(m_logger.isDebugEnabled())
			        	m_logger.debug("Wrong notarys certificate: " + rCert.getSerialNumber() + " ref: " + sSerial);
		            lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, 
		                "Wrong notarys certificate: " + rCert.getSerialNumber() + " ref: " + sSerial, null));
		            bOk = false;
		        }
		        // verify notary certs digest using CompleteCertificateRefs
		        try {
		        	if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
		        	   byte[] digest = SignedDoc.digestOfType(rCert.getEncoded(), (sig.getSignedDoc().getFormat().
		                	equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE));
		        	  if(m_logger.isDebugEnabled())
		            	  m_logger.debug("Not cert calc hash: " + Base64Util.encode(digest, 0) + 
		            	  " cert-ref hash: " + Base64Util.encode(sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestValue(), 0));
		              if(!compareDigests(digest, sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestValue())) {
		                lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, 
		                "Notary certificates digest doesn't match!", null));
		                if(m_logger.isDebugEnabled()) 
		            		m_logger.debug("Notary certificates digest doesn't match!");
		                bOk = false;
		              }
		              if(sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
		            	 sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
		            	 ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
		        		 lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
		        		 if(m_logger.isInfoEnabled()) 
		        	       m_logger.info("CompleteCertificateRefs for signature: " + sig.getId() + " has weak digest type: " + 
		        	            sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestAlgorithm());
		        	  }
		        	}
		        	// TODO: in bdoc verify responders ca hash - verify all hashes in certrefs
		        	
		        } catch(DigiDocException ex) {
		            lerrs.add(ex);
		            bOk = false;
		        } catch(Exception ex) {
		        	bOk = false;
		            lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, 
		                "Error calculating notary certificate digest!", null));
		        }
		        // we support only 1 ocsp per signature
		        if(sig.getUnsignedProperties().countNotaries() > 1) {
		        	if(m_logger.isDebugEnabled()) 
	            		m_logger.debug("Currently supports only one OCSP");
        			lerrs.add(new DigiDocException(DigiDocException.ERR_OCSP_VERIFY,
        					"Currently supports only one OCSP", null));
        			bOk = false;
        		}
		        
		        // verify notarys digest using CompleteRevocationRefs
		        if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
		        try {
		        	for(int i = 0; i < sig.getUnsignedProperties().countNotaries(); i++) {
		        		if(m_logger.isDebugEnabled())
		        			m_logger.debug("Signature: " + sig.getId() + " not: " + i + " notaries: " + sig.getUnsignedProperties().countNotaries());
		        		Notary not = sig.getUnsignedProperties().getNotaryById(i);
		        		
		        		byte[] ocspData = not.getOcspResponseData();
		        		if(m_logger.isDebugEnabled())
		        			m_logger.debug("OCSP value: " + not.getId() + " data: " + ((ocspData != null) ? ocspData.length : 0) + " bytes");
		        		if(ocspData == null || ocspData.length == 0) {
		        			lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "OCSP value is empty!", null));
		        			bOk = false;
		        			continue;
		        		}
		        		OcspRef orf = sig.getUnsignedProperties().getCompleteRevocationRefs().getOcspRefByUri("#" + not.getId());
		        		if(m_logger.isDebugEnabled())
		        			m_logger.debug("OCSP ref: " + ((orf != null) ? orf.getUri() : "NULL"));
		        		if(orf == null) {
		        			lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "No OCSP ref for uri: #" + not.getId(), null));
		        			bOk = false;
		        			continue;
		        		}
		        		if(m_logger.isDebugEnabled())
		        			m_logger.debug("OCSP data len: " + ocspData.length); 
		        		byte[] digest1 = SignedDoc.digestOfType(ocspData, ((sig.getSignedDoc().getFormat().
		            	  equals(SignedDoc.FORMAT_BDOC) && (orf.getDigestAlgorithm().equals(SignedDoc.SHA256_DIGEST_ALGORITHM_1) ||
		            			  orf.getDigestAlgorithm().equals(SignedDoc.SHA256_DIGEST_ALGORITHM_2))) ? 
		            			  SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE));
		        		//if(m_logger.isDebugEnabled())
		        		//	m_logger.debug("Calculated digest: " + Base64Util.encode(digest1, 0));
		                byte[] digest2 = orf.getDigestValue();
		                //if(m_logger.isDebugEnabled())
		        		//	m_logger.debug("Real digest: " + Base64Util.encode(digest2, 0));
		                if(m_logger.isDebugEnabled())
		            	  m_logger.debug("Check ocsp: " + not.getId() + 
		            	    " calc hash: " + Base64Util.encode(digest1, 0) + 
		            	    " refs-hash: " + Base64Util.encode(digest2, 0));
		               if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML) &&
		            	 !compareDigests(digest1, digest2)) {
		                lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, 
		                "Notarys digest doesn't match!", null));
		                if(m_logger.isDebugEnabled()) 
		            		m_logger.debug("Notarys digest doesn't match!");
		                bOk = false;
		               }
		               if(sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
		                 orf.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
		                 ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
				         lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
				         if(m_logger.isInfoEnabled()) 
				           m_logger.info("CompleteRevocationRefs for signature: " + sig.getId() + " has weak digest type: " + 
				        		   orf.getDigestAlgorithm());
				       }
		               if(m_logger.isDebugEnabled())
			            	  m_logger.debug("Check ocsp: " + not.getId() + " prodAt: " + 
			            	((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
			            	" orf prodAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"));
		               //if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML)) {
			                if(not.getProducedAt() != null && orf.getProducedAt() != null && 
			            	!ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()).
			            	equals(ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()))) {
			                if(m_logger.isDebugEnabled()) 
			                		m_logger.debug("Notary: " + not.getId() + " producedAt: " + 
					            	((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
					            	" does not match OcpsRef-s producedAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"));
			            	lerrs.add(new DigiDocException(DigiDocException.ERR_OCSP_VERIFY, "Notary: " + not.getId() + " producedAt: " + 
					            	((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
					            	" does not match OcpsRef-s producedAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"), null));
			            }
		               //}
		        	}
		        } catch(DigiDocException ex) {
		            lerrs.add(ex);
		            bOk = false;
		        } 
		        } // don't verify complete revocation refs in bdoc
		        // verify notary status
		        try {
		            NotaryFactory notFac = ConfigManager.instance().getNotaryFactory();
		            for(int i = 0; i < sig.getUnsignedProperties().countNotaries(); i++) {
		        	  Notary not = sig.getUnsignedProperties().getNotaryById(i);
		        	  if(m_logger.isDebugEnabled())
		            	  m_logger.debug("Verify notary: " + not.getId() + " ocsp: " + 
		            			  ((not.getOcspResponseData() != null) ? not.getOcspResponseData().length : 0) + 
		            			  " responder: " + not.getResponderId());
		              notFac.parseAndVerifyResponse(sig, not);
		            }
		        } catch(DigiDocException ex) {
		            lerrs.add(ex);
		            bOk = false;
		        } 
		        
			} else {
				bOk = false;
				if(m_logger.isDebugEnabled()) 
	            	m_logger.debug("Signature has no OCSP confirmation!");
				lerrs.add(new DigiDocException(DigiDocException.ERR_NO_CONFIRMATION,
	                        "Signature has no OCSP confirmation!", null));
			}
        } catch(Exception ex) {
        	if(m_logger.isDebugEnabled()) 
        		m_logger.debug("Failed to verify OCSP for: " + sig.getId());
        	lerrs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED,
                    "Failed to verify OCSP for: " + sig.getId(), null));
            bOk = false;
        }
		return bOk;
	}
	
	/**
	 * Verifies signature
	 * @param sdoc SignedDoc object
	 * @param sig Signature object
	 * @param lerrs list of errors
	 * @return true if ok
	 */
	public static boolean verifySignature(SignedDoc sdoc, Signature sig, List lerrs)
	{
		boolean bOk = true, b = false;
		initProvider();
		if(m_logger.isDebugEnabled()) 
        	m_logger.debug("Verifying signature: " + sig.getId() + " profile: " + sig.getProfile());
		if(sig.getProfile() != null &&
				(sig.getProfile().equals(SignedDoc.BDOC_PROFILE_T) ||
				sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TS) ||
				sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TSA))) {
			if(m_logger.isDebugEnabled()) 
        		m_logger.debug("T, TS and TSA profiles are currently not supported!");
			lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "T, TS and TSA profiles are currently not supported!", null));
		}
		// verify DataFile hashes
		for(int i = 0; i < sdoc.countDataFiles(); i++) {
            DataFile df = sdoc.getDataFile(i);
            if(m_logger.isDebugEnabled()) 
            	m_logger.debug("Verifying DF: " + df.getId() + " file: " + df.getFileName());
            Reference ref = sig.getSignedInfo().getReferenceForDataFile(df);
            if(ref != null && ref.getDigestAlgorithm() != null &&
               sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
               ref.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
               ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
               lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
               if(m_logger.isInfoEnabled()) 
               	m_logger.info("DataFile: " + df.getId() + " has weak digest type: " + ref.getDigestAlgorithm());
            } // kontrolli kas on Referencet millele ei ole andmefaili
            if(ref != null) {
              b = verifyDataFileHash(sdoc, df, ref, lerrs);
            } else {
              b = false;
              lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Missing Reference for file: " + df.getFileName(), null));
            }
            if(!b) bOk = false;
        }
		// check DF/Ref 
		for(int i = 0; i < sdoc.countSignatures(); i++) {
			Signature sig1 = sdoc.getSignature(i);
			for(int j = 0; j < sig.getSignedInfo().countReferences(); j++) {
				Reference ref1 = sig.getSignedInfo().getReference(j);
				if(ref1.getType() != null ||
					(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // bdoc 2.0-s ei ole manifest.xml-i r��si!
					ref1.getUri().indexOf("META-INF/manifest.xml") != -1)) continue;
				if((sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || 
				    sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) && // ddoc 1.0 formaadis erijuhtumid
				  (ref1.getUri().indexOf("-MIME") != -1 || ref1.getUri().indexOf("-SignedProperties") != -1)) continue;
				boolean bFound = false;
				for(int l = 0; l < sdoc.countDataFiles(); l++) {
					DataFile df = sdoc.getDataFile(l);
					String sFile = df.getFileName();
					if(sFile != null && sFile.indexOf('/') != -1 || sFile.indexOf('\\') != -1) {
						File fT = new File(sFile);
						sFile = fT.getName();
					}
				    if(ref1.getUri() != null) {
				       if((sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) || 
				           sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) &&
				           ref1.getUri().startsWith("#") &&
				           df.getId().equals(ref1.getUri().substring(1)))
				            bFound = true;
				       if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) &&
					       ref1.getUri().indexOf(sFile) != -1)
					        bFound = true;
				    }
				}
				if(!bFound) {
					if(m_logger.isInfoEnabled()) 
						m_logger.info("Missing DataFile for signature: " + sig.getId() + " reference " +ref1.getUri());
					lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Missing DataFile for signature: " + sig.getId() + " reference " +ref1.getUri(), null));
				}
			}
		}
		// verify mime-type hashes  
		if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
		for(int i = 0; i < sig.getSignedInfo().countReferences(); i++) {
			Reference ref = sig.getSignedInfo().getReference(i);
			if(!ref.getUri().startsWith("#")) {
				DataObjectFormat dof = sig.getSignedInfo().getDataObjectFormatForReference(ref);
				if(dof == null) {
					if(m_logger.isDebugEnabled()) 
		            	m_logger.debug("No DataObjectFormat element for Reference: " + ref.getId());
					lerrs.add(new DigiDocException(DigiDocException.ERR_DATA_FILE_MIME_TYPE,
			                "No DataObjectFormat element for Reference: " + ref.getId(), null));
				}
			}
		}
		}
		// verify 
		if(!sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML))
		b = verifySignedPropretiesHash(sig, lerrs);
		if(!b) bOk = false;
        // verify signature value
		b = verifySignatureValue(sdoc, sig, lerrs);
		if(!b) bOk = false;
        // verify signers cert...
        // check the certs validity dates
		b = verifySigningTime(sig, lerrs);
		if(!b) bOk = false;
        // check certificates CA
		b = verifySignersCerificate(sig, lerrs);
        if(!b) bOk = false;
        // TODO: Profile T & CL verify Timestamp T0
        
        if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
    		b = verifySignaturePolicies(sdoc, sig, lerrs);
    		if(!b) bOk = false;
        }
        // verify OCSP
        if(sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML) ||
        	sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) ||
          (sig.getProfile() != null &&
          (sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TM) ||
           sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TMA) ||
           sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TS) ||
           sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TSA)))) {
          b = verifySignatureOCSP(sig, lerrs);
          if(!b) bOk = false;
        }
        
        // verify timestamps
        /*ArrayList tsaCerts = findTSACerts();
        if(m_timestamps != null && m_timestamps.size() > 0) {
        	TimestampFactory tsFac = null;
        	try {
        		tsFac = ConfigManager.instance().getTimestampFactory();
        	} catch(DigiDocException ex) {
        		//m_logger.error("Failed to get TimestampFactory: " + ex);
        		errs.add(ex);
        	}
        	ArrayList e = tsFac.verifySignaturesTimestamps(this);
        	if(!e.isEmpty())
                errs.addAll(e);
        	for(int i = 0; i < m_timestamps.size(); i++) {
        		TimestampInfo ts = (TimestampInfo)m_timestamps.get(i);
        		if(m_logger.isDebugEnabled())
        			m_logger.debug("TS: " + ts.getId() + " type: " + ts.getType() + " time: " + ts.getTime());
        		if(ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIGNATURE)
        			dt1 = ts.getTime();
        		if(ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS)
        			dt2 = ts.getTime();
        	}
        	int nMaxTsTimeErrSecs = ConfigManager.instance().getIntProperty("MAX_TSA_TIME_ERR_SECS", 0);
        	if(dt1 != null && dt2 != null) {
        	  dt1 = new Date(dt1.getTime() - (nMaxTsTimeErrSecs * 1000));
        	  dt2 = new Date(dt2.getTime() + (nMaxTsTimeErrSecs * 1000));
        	  if(dt2.before(dt1))
        		errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "SignAndRefsTimeStamp is before SignatureTimeStamp", null));
        	  if(do1.before(dt1) || do1.after(dt2))
        		errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "OCSP time is not between SignAndRefsTimeStamp and SignatureTimeStamp", null));
        	}
        }
		} // profiles T/C/TM/TS
	*/
	
		return bOk;
	}
	
	/**
	 * Verifies signature policies
	 * @param sdoc SignedDoc object
	 * @param sig Signature object
	 * @param lerrs list of errors
	 * @return true if signature declares valid bdoc 2.0 nonce policy
	 */
	public static boolean verifySignaturePolicies(SignedDoc sdoc, Signature sig, List lerrs)
	{
		boolean bOk = false;
		if(m_logger.isInfoEnabled()) 
			m_logger.debug("Check signature: " + sig.getId() + " profile: " + sig.getProfile() + " format: " + sdoc.getFormat() + " policies");
		try {
			if(sig.getSignedProperties() != null &&
			   sig.getSignedProperties().getSignaturePolicyIdentifier() != null &&
			   sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId() != null &&
			   sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId() != null &&
			   sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId().getIdentifier() != null) {
			   Identifier id = sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId().getIdentifier();
			   if(m_logger.isInfoEnabled()) 
					m_logger.debug("Signature: " + sig.getId() + " has policy: " + id.getQualifier() + 
							" uri: " + id.getUri() + " hash: " + 
					Base64Util.encode(sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue()));
			   if(id.getQualifier().equals(Identifier.OIDAsURN) && 
			      id.getUri().equals(DigiDocGenFactory.BDOC_210_OID)) { // has bdoc 2.0 nonce policy
			    bOk = true;
			    // check policy hash
			    if(sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue() == null || 
			       sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue().length == 0) {
			    	if(m_logger.isDebugEnabled()) 
	            		m_logger.debug("Signature: " + sig.getId() + " has no signature policy hash");
					lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_HASH,
							   "Signature: " + sig.getId() + " has invalid signature policy hash", null));
			    }
			    // check policy uri
			    boolean bUriOk = false;
			    for(int i = 0; i < sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().countSigPolicyQualifiers(); i++) {
			    	SigPolicyQualifier spq = sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyQualifier(i);
			    	if(spq instanceof SpUri) {
			    		SpUri sna = (SpUri)spq;
			    		if(sna.getUri() != null && sna.getUri().trim().length() > 0) {
			    			bUriOk = true;
			    		}
			    	}
			    }
			    if(!bUriOk) { // invalid uri
			    	bOk = false;
			    	if(m_logger.isDebugEnabled()) 
	            		m_logger.debug("Signature: " + sig.getId() + " has no signature policy uri!");
					lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_URL,
							"Signature: " + sig.getId() + " has no nonce policy uri!", null));
			    }
			    
			   } else { // unknown policy
				   if(m_logger.isDebugEnabled()) 
	            		m_logger.debug("Signature: " + sig.getId() + " has unknown policy: " + id.getQualifier() + " uri: " + id.getUri());
				   lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_OID,
							"Signature: " + sig.getId() + " has unknown policy: " + id.getQualifier() + " uri: " + id.getUri(), null));
			   }
			} else { // no policy
				if(m_logger.isDebugEnabled()) 
            		m_logger.debug("No signature policy for sig: " + sig.getId());
				lerrs.add(new DigiDocException(DigiDocException.ERR_POLICY_NONE,
						"Signature: " + sig.getId() + " has no policy!", null));
			}
			
			
		} catch(Exception ex) {
			if(m_logger.isDebugEnabled()) 
        		m_logger.debug("Failed to verify sig policies: " + sig.getId() + " - " + ex);
        	lerrs.add(new DigiDocException(DigiDocException.ERR_POLICY_NONE,
        			"Failed to verify sig policies: " + sig.getId() + " - " + ex, null));
            bOk = false;
        }
		return bOk;
	}
	
	/**
	 * Helper method to parse DN
	 * @param dn certificate DN
	 * @param chSep separator used
	 * @return list of RDN entries
	 */
	private static List findRdns(String dn, char chSep)
	{
		List lrdn = new ArrayList();
		StringBuffer sbId = new StringBuffer();
		StringBuffer sbVal = new StringBuffer();
		boolean bId = true; // parsing stage - id or value
		for(int i = 0; (dn != null) && (i < dn.length()); i++) {
			char ch = dn.charAt(i);
			// RDN end found
			if(( (ch == chSep) && 
				 (i == 0 || dn.charAt(i-1) != '\\')) || (i == dn.length()-1)) {
				if(i == dn.length()-1 && !bId)
					sbVal.append(ch);
				if(sbId.length() > 0 && sbVal.length() > 0) 
					lrdn.add(new Rdn(sbId.toString().trim(), null, sbVal.toString().trim()));
				sbId = new StringBuffer();
				sbVal = new StringBuffer();
				bId = true;
			} else if(ch == '=' && (i == 0 || dn.charAt(i-1) != '\\')) {
				bId = false; 
			} else { // handle content
				if(bId)
					sbId.append(ch);
				else
					sbVal.append(ch);
			}
		}
		return lrdn;
	}
	
	/**
	 * Parses a DN normalized by rules of RFC2253 and returns a set of
	 * Rdn objects containing RDN-s and values found in this DN
	 * @param dn normalized DN string
	 * @return array of Rdn objects
	 */
	public static List parseDN(String dn)
	{
		// try first according to RFC2253
		List al = findRdns( dn, ',');
		if(al.size() < 3) // if not successfull try RFC1770
			al = findRdns( dn, '/');
		return al;
	}
	

	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy