/* * Signature.java * PROJECT: JDigiDoc * DESCRIPTION: Digi Doc functions for creating * and reading signed documents. * AUTHOR: Veiko Sinivee, S|E|B IT Partner Estonia *================================================== * 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 es.uji.security.crypto.openxades.digidoc; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import org.apache.log4j.Logger; import es.uji.security.crypto.config.ConfigManager; import es.uji.security.crypto.openxades.digidoc.factory.CRLFactory; import es.uji.security.crypto.openxades.digidoc.factory.DigiDocFactory; import es.uji.security.crypto.openxades.digidoc.factory.FactoryManager; import es.uji.security.crypto.openxades.digidoc.factory.NotaryFactory; import es.uji.security.crypto.openxades.digidoc.factory.TimestampFactory; import es.uji.security.crypto.openxades.digidoc.utils.ConvertUtils; /** * Models an XML-DSIG/ETSI Signature. A signature can contain references SignedInfo (truly signed * data) and signed and unsigned properties. * * @author Veiko Sinivee * @version 1.0 */ public class Signature implements Serializable { /** reference to the parent SignedDoc object */ private SignedDoc m_sigDoc; /** signature id */ private String m_id; /** SignedInfo object */ private SignedInfo m_signedInfo; /** SignatureValue object */ private SignatureValue m_signatureValue; /** KeyInfo object */ private KeyInfo m_keyInfo; /** SignedProperties object */ private SignedProperties m_sigProp; /** UnsignedProperties object */ private UnsignedProperties m_unsigProp; /** original bytes read from XML file */ private byte[] m_origContent; /** CertID elements */ private ArrayList m_certIds; /** CertValue elements */ private ArrayList m_certValues; /** TimestampInfo elements */ private ArrayList m_timestamps; /** Logger */ private Logger m_logger = Logger.getLogger(Signature.class); private ConfigManager conf = ConfigManager.getInstance(); /** * Creates new Signature */ public Signature(SignedDoc sigDoc) { m_sigDoc = sigDoc; m_id = null; m_signedInfo = null; m_signatureValue = null; m_keyInfo = null; m_sigProp = null; m_unsigProp = null; m_origContent = null; m_certIds = null; m_certValues = null; m_timestamps = null; } /** * Accessor for sigDoc attribute * * @return value of sigDoc attribute */ public SignedDoc getSignedDoc() { return m_sigDoc; } /** * Mutator for sigDoc attribute * * @param sigDoc * new value for sigDoc attribute */ public void setSignedDoc(SignedDoc sigDoc) { m_sigDoc = sigDoc; } /** * Accessor for id attribute * * @return value of id attribute */ public String getId() { return m_id; } /** * Mutator for id attribute * * @param str * new value for id attribute * @throws DigiDocException * for validation errors */ public void setId(String str) throws DigiDocException { DigiDocException ex = validateId(str); if (ex != null) throw ex; m_id = str; } /** * Accessor for origContent attribute * * @return value of origContent attribute */ public byte[] getOrigContent() { return m_origContent; } /** * Mutator for origContent attribute * * @param str * new value for origContent attribute */ public void setOrigContent(byte[] data) { m_origContent = data; } /** * Helper method to validate an id * * @param str * input data * @return exception or null for ok */ private DigiDocException validateId(String str) { DigiDocException ex = null; if (str == null) ex = new DigiDocException(DigiDocException.ERR_SIGNATURE_ID, "Id is a required attribute", null); return ex; } /** * Accessor for signedInfo attribute * * @return value of signedInfo attribute */ public SignedInfo getSignedInfo() { return m_signedInfo; } /** * Mutator for signedInfo attribute * * @param str * new value for signedInfo attribute * @throws DigiDocException * for validation errors */ public void setSignedInfo(SignedInfo si) throws DigiDocException { // ArrayList errs = si.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_signedInfo = si; } /** * Calculates the SignedInfo digest * * @return SignedInfo digest */ public byte[] calculateSignedInfoDigest() throws DigiDocException { return m_signedInfo.calculateDigest(); } /** * Get the content to be signed * * @return Content to be signed */ public byte[] getSignedContent() throws DigiDocException { return m_signedInfo.getSignedContent(); } /** * Accessor for signatureValue attribute * * @return value of signatureValue attribute */ public SignatureValue getSignatureValue() { return m_signatureValue; } /** * Mutator for signatureValue attribute * * @param str * new value for signatureValue attribute * @throws DigiDocException * for validation errors */ public void setSignatureValue(SignatureValue sv) throws DigiDocException { // ArrayList errs = sv.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_signatureValue = sv; } /** * Creates a new SignatureValue object of this signature * * @param sigv * signatures byte data * @throws DigiDocException * for validation errors */ public void setSignatureValue(byte[] sigv) throws DigiDocException { SignatureValue sv = new SignatureValue(this, sigv); setSignatureValue(sv); } /** * Accessor for keyInfo attribute * * @return value of keyInfo attribute */ public KeyInfo getKeyInfo() { return m_keyInfo; } /** * Mutator for keyInfo attribute * * @param str * new value for keyInfo attribute * @throws DigiDocException * for validation errors */ public void setKeyInfo(KeyInfo ki) throws DigiDocException { // ArrayList errs = ki.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_keyInfo = ki; } /** * Accessor for signedProperties attribute * * @return value of signedProperties attribute */ public SignedProperties getSignedProperties() { return m_sigProp; } /** * Mutator for signedProperties attribute * * @param str * new value for signedProperties attribute * @throws DigiDocException * for validation errors */ public void setSignedProperties(SignedProperties sp) throws DigiDocException { // ArrayList errs = sp.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_sigProp = sp; } /** * Accessor for unsignedProperties attribute * * @return value of unsignedProperties attribute */ public UnsignedProperties getUnsignedProperties() { return m_unsigProp; } /** * Mutator for unsignedProperties attribute * * @param str * new value for unsignedProperties attribute * @throws DigiDocException * for validation errors */ public void setUnsignedProperties(UnsignedProperties usp) throws DigiDocException { // ArrayList errs = usp.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_unsigProp = usp; } /** * return the count of CertID objects * * @return count of CertID objects */ public int countCertIDs() { return ((m_certIds == null) ? 0 : m_certIds.size()); } /** * Adds a new CertID object * * @param cid * new object to be added */ public void addCertID(CertID cid) { if (m_certIds == null) m_certIds = new ArrayList(); cid.setSignature(this); m_certIds.add(cid); } /** * Retrieves CertID element with the desired index * * @param idx * CertID index * @return CertID element or null if not found */ public CertID getCertID(int idx) { if (m_certIds != null && idx < m_certIds.size()) { return (CertID) m_certIds.get(idx); } return null; // not found } /** * Retrieves the last CertID element * * @return CertID element or null if not found */ public CertID getLastCertId() { if (m_certIds != null && m_certIds.size() > 0) { return (CertID) m_certIds.get(m_certIds.size() - 1); } return null; // not found } /** * Retrieves CertID element with the desired type * * @param type * CertID type * @return CertID element or null if not found */ public CertID getCertIdOfType(int type) { for (int i = 0; (m_certIds != null) && (i < m_certIds.size()); i++) { CertID cid = (CertID) m_certIds.get(i); if (cid.getType() == type) return cid; } return null; // not found } /** * Retrieves CertID element with the desired type. If not found creates a new one with this * type. * * @param type * CertID type * @return CertID element * @throws DigiDocException * for validation errors */ public CertID getOrCreateCertIdOfType(int type) throws DigiDocException { CertID cid = getCertIdOfType(type); if (cid == null) { cid = new CertID(); cid.setType(type); addCertID(cid); } return cid; // not found } /** * return the count of CertValue objects * * @return count of CertValues objects */ public int countCertValues() { return ((m_certValues == null) ? 0 : m_certValues.size()); } /** * Adds a new CertValue object * * @param cval * new object to be added */ public void addCertValue(CertValue cval) { if (m_certValues == null) m_certValues = new ArrayList(); cval.setSignature(this); m_certValues.add(cval); } /** * Retrieves CertValue element with the desired index * * @param idx * CertValue index * @return CertValue element or null if not found */ public CertValue getCertValue(int idx) { if (m_certValues != null && idx < m_certValues.size()) { return (CertValue) m_certValues.get(idx); } else return null; // not found } /** * Retrieves the last CertValue element * * @return CertValue element or null if not found */ public CertValue getLastCertValue() { if (m_certValues != null && m_certValues.size() > 0) { return (CertValue) m_certValues.get(m_certValues.size() - 1); } else return null; // not found } /** * Retrieves CertValue element with the desired type * * @param type * CertValue type * @return CertValue element or null if not found */ public CertValue getCertValueOfType(int type) { for (int i = 0; (m_certValues != null) && (i < m_certValues.size()); i++) { CertValue cval = (CertValue) m_certValues.get(i); if (cval.getType() == type) return cval; } return null; // not found } /** * Retrieves CertValue element with the desired type. If not found creates a new one with this * type. * * @param type * CertValue type * @return CertValue element * @throws DigiDocException * for validation errors */ public CertValue getOrCreateCertValueOfType(int type) throws DigiDocException { CertValue cval = getCertValueOfType(type); if (cval == null) { cval = new CertValue(); cval.setType(type); addCertValue(cval); } return cval; // not found } /** * Returns the first CertValue with the given serial number that has been attached to this * signature in digidoc document. This could be either the signers cert, OCSP responders cert or * one of the TSA certs. * * @param serNo * certificates serial number * @return found CertValue or null */ public CertValue findCertValueWithSerial(BigInteger serNo) { for (int i = 0; (m_certValues != null) && (i < m_certValues.size()); i++) { CertValue cval = (CertValue) m_certValues.get(i); // System.out.println("Serach cert: " + serNo + " found: " + // cval.getCert().getSerialNumber()); if (cval.getCert().getSerialNumber().equals(serNo)) return cval; } return null; } /** * Retrieves OCSP respoinders certificate * * @return OCSP respoinders certificate */ public X509Certificate findResponderCert() { CertValue cval = getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER); if (cval != null) return cval.getCert(); else return null; } /** * Retrieves TSA certificates * * @return TSA certificates */ public ArrayList findTSACerts() { ArrayList vec = new ArrayList(); for (int i = 0; (m_certValues != null) && (i < m_certValues.size()); i++) { CertValue cval = (CertValue) m_certValues.get(i); if (cval.getType() == CertValue.CERTVAL_TYPE_TSA) vec.add(cval.getCert()); } return vec; } /** * return the count of TimestampInfo objects * * @return count of TimestampInfo objects */ public int countTimestampInfos() { return ((m_timestamps == null) ? 0 : m_timestamps.size()); } /** * Adds a new TimestampInfo object * * @param ts * new object to be added */ public void addTimestampInfo(TimestampInfo ts) { if (m_timestamps == null) m_timestamps = new ArrayList(); ts.setSignature(this); m_timestamps.add(ts); } /** * Retrieves TimestampInfo element with the desired index * * @param idx * TimestampInfo index * @return TimestampInfo element or null if not found */ public TimestampInfo getTimestampInfo(int idx) { if (m_timestamps != null && idx < m_timestamps.size()) { return (TimestampInfo) m_timestamps.get(idx); } else return null; // not found } /** * Retrieves the last TimestampInfo element * * @return TimestampInfo element or null if not found */ public TimestampInfo getLastTimestampInfo() { if (m_timestamps != null && m_timestamps.size() > 0) { return (TimestampInfo) m_timestamps.get(m_timestamps.size() - 1); } else return null; // not found } /** * Retrieves TimestampInfo element with the desired type * * @param type * TimestampInfo type * @return TimestampInfo element or null if not found */ public TimestampInfo getTimestampInfoOfType(int type) { for (int i = 0; (m_timestamps != null) && (i < m_timestamps.size()); i++) { TimestampInfo ts = (TimestampInfo) m_timestamps.get(i); if (ts.getType() == type) return ts; } return null; // not found } /** * Retrieves TimestampInfo element with the desired type. If not found creates a new one with * this type. * * @param type * TimestampInfo type * @return TimestampInfo element * @throws DigiDocException * for validation errors */ public TimestampInfo getOrCreateTimestampInfoOfType(int type) throws DigiDocException { TimestampInfo ts = getTimestampInfoOfType(type); if (ts == null) { ts = new TimestampInfo(); ts.setType(type); addTimestampInfo(ts); } return ts; // not found } /** * Gets confirmation and adds the corresponding members that carry the returned info to this * signature * * @throws DigiDocException * for all errors */ public void getConfirmation() throws DigiDocException { NotaryFactory notFac = FactoryManager.getNotaryFactory(); X509Certificate cert = m_keyInfo.getSignersCertificate(); X509Certificate caCert = null; DigiDocFactory ddocFac = null; try { ddocFac = FactoryManager.getDigiDocFactory(); caCert = ddocFac.findCAforCertificate(cert); } catch (DigiDocException dex) { m_logger .info("ERROR: Cannot find the CAs certificate for signer's certificate with DN: " + cert.getSubjectDN()); m_logger.info(" and issuer DN: " + cert.getIssuerDN()); throw dex; } Notary not = notFac.getConfirmation(this, cert, caCert); CompleteRevocationRefs rrefs = new CompleteRevocationRefs(not); // modified in ver 2.1.0 - find responder certs that succeded in verification X509Certificate rcert = notFac.getNotaryCert(rrefs.getResponderCommonName(), not .getCertNr()); // if the request was successful then // create new data memebers CertValue cval = new CertValue(); cval.setType(CertValue.CERTVAL_TYPE_RESPONDER); cval.setCert(rcert); cval.setId(m_id + "-RESPONDER_CERT"); addCertValue(cval); CertID cid = new CertID(this, rcert, CertID.CERTID_TYPE_RESPONDER); addCertID(cid); CompleteCertificateRefs crefs = new CompleteCertificateRefs(); UnsignedProperties usp = new UnsignedProperties(this, crefs, rrefs, rcert, not); rrefs.setUnsignedProperties(usp); crefs.setUnsignedProperties(usp); setUnsignedProperties(usp); // reset original content since we just added to confirmation if (m_origContent != null) { String str = new String(m_origContent); int idx1 = str.indexOf("</SignedProperties>"); if (idx1 != -1) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bos.write(m_origContent, 0, idx1); bos.write("</SignedProperties>".getBytes()); bos.write(usp.toXML()); bos.write("</QualifyingProperties></Object></Signature>".getBytes()); m_origContent = bos.toByteArray(); } catch (java.io.IOException ex) { DigiDocException.handleException(ex, DigiDocException.ERR_OCSP_GET_CONF); } } } } /** * Verifies this signature * * @param sdoc * parent doc object * @param checkDate * Date on which to check the signature validity * @param demandConfirmation * true if you demand OCSP confirmation from every signature * @return a possibly empty list of DigiDocException objects */ public ArrayList verify(SignedDoc sdoc, boolean checkDate, boolean demandConfirmation) { Date do1 = null, dt1 = null, dt2 = null; ArrayList errs = new ArrayList(); // check the DataFile digests for (int i = 0; i < sdoc.countDataFiles(); i++) { DataFile df = sdoc.getDataFile(i); // System.out.println("Check digest for DF: " + df.getId()); Reference ref = m_signedInfo.getReferenceForDataFile(df); byte[] dfDig = null; try { dfDig = df.getDigest(); } catch (DigiDocException ex) { errs.add(ex); } if (ref != null) { // System.out.println("Compare it to: " + Base64Util.encode(ref.getDigestValue(), // 0)); if (!SignedDoc.compareDigests(ref.getDigestValue(), dfDig)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for DataFile: " + df.getId(), null)); // System.out.println("BAD DIGEST"); } // else System.out.println("GOOD DIGEST"); } else { // System.out.println("No Reference"); errs.add(new DigiDocException(DigiDocException.ERR_DATA_FILE_NOT_SIGNED, "No Reference element for DataFile: " + df.getId(), null)); } // if this is a detatched file and the file // referred by this entry actually exists, // then go and check it's digest // If the datafile doesn't exist the // just trust whatever is in the XML if (df.getContentType().equals(DataFile.CONTENT_DETATCHED)) { File fTest = new File(df.getFileName()); if (fTest.canRead()) { // System.out.println("Check detatched file: " + fTest.getAbsolutePath()); byte[] realDigest = null; byte[] detDigest = null; try { realDigest = df.calculateDetatchedFileDigest(); detDigest = df.getDigestValue(); } catch (DigiDocException ex) { errs.add(ex); } if (!SignedDoc.compareDigests(detDigest, realDigest)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for detatched file: " + df.getFileName(), null)); } } // else System.out.println("Cannot read detatched file: " + // fTest.getAbsolutePath()); } } // check signed properties digest Reference ref2 = m_signedInfo.getReferenceForSignedProperties(m_sigProp); if (ref2 != null) { byte[] spDig = null; try { spDig = m_sigProp.calculateDigest(); // System.out.println("SignedProp real digest: " + Base64Util.encode(spDig, 0)); } catch (DigiDocException ex) { errs.add(ex); } // System.out.println("Compare it to: " + Base64Util.encode(ref2.getDigestValue(), 0)); if (!SignedDoc.compareDigests(ref2.getDigestValue(), spDig)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for SignedProperties: " + m_sigProp.getId(), null)); // System.out.println("BAD DIGEST"); } // else System.out.println("GOOD DIGEST"); } else { errs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED, "No Reference element for SignedProperties: " + m_sigProp.getId(), null)); } // verify signature value try { byte[] dig = m_signedInfo.calculateDigest(); // System.out.println("SignedInfo real digest: " + Base64Util.encode(dig, 0) + " hex: " // + SignedDoc.bin2hex(dig)); SignedDoc.verify(dig, m_signatureValue.getValue(), m_keyInfo.getSignersCertificate()); // System.out.println("GOOD DIGEST"); } catch (DigiDocException ex) { errs.add(ex); System.out.println("BAD DIGEST"); } // verify signers cert... // check the certs validity dates try { if (checkDate) m_keyInfo.getSignersCertificate().checkValidity(m_sigProp.getSigningTime()); } catch (Exception ex) { errs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED, "Signers certificate has expired!", null)); } // check certificates CA try { DigiDocFactory digFac = FactoryManager.getDigiDocFactory(); digFac.verifyCertificate(m_keyInfo.getSignersCertificate()); } catch (DigiDocException ex) { errs.add(ex); } // if we check signatures using CRL String verifier = conf.getStringProperty("DIGIDOC_SIGNATURE_VERIFIER", "OCSP"); if (verifier != null && verifier.equals("CRL")) { try { CRLFactory crlFac = FactoryManager.getCRLFactory(); crlFac.checkCertificate(m_keyInfo.getSignersCertificate(), new Date()); } catch (DigiDocException ex) { errs.add(ex); } } // check confirmation if (m_unsigProp != null) { ArrayList e = m_unsigProp.verify(sdoc); if (!e.isEmpty()) errs.addAll(e); if (m_unsigProp.getNotary() != null) do1 = m_unsigProp.getNotary().getProducedAt(); } else { // not OCSP confirmation if (demandConfirmation) errs.add(new DigiDocException(DigiDocException.ERR_NO_CONFIRMATION, "Signature has no OCSP confirmation!", null)); } // verify timestamps ArrayList tsaCerts = findTSACerts(); if (m_timestamps != null && m_timestamps.size() > 0) { TimestampFactory tsFac = null; try { tsFac = FactoryManager.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++) { // System.out.println("Paso por for m_timestamps.size()= " + m_timestamps.size()); TimestampInfo ts = (TimestampInfo) m_timestamps.get(i); if (ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIGNATURE) { dt1 = ts.getTime(); // System.out.println("Tipo del timestamp TimestampInfo.TIMESTAMP_TYPE_SIGNATURE "); } if (ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS) { dt2 = ts.getTime(); // System.out.println("Tipo del timestamp TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS "); } } // System.out.println("OCSP time: " + do1); // System.out.println("SignatureTimeStamp time: " + dt1); // System.out.println("SigAndRefsTimeStamp time: " + dt2); int nMaxTsTimeErrSecs = conf.getIntProperty( "DIGIDOC_MAX_TSA_TIME_ERR_SECS", 0); if (dt1 == null) { errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "Cannot retrieve SignatureTimeStamp", null)); } else { dt1 = new Date(dt1.getTime() - (nMaxTsTimeErrSecs * 1000)); if (dt2 != null) { dt2 = new Date(dt2.getTime() + (nMaxTsTimeErrSecs * 1000)); // System.out.println("SignatureTimeStamp adj time: " + dt1); // System.out.println("SigAndRefsTimeStamp adj time: " + dt2); 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)); } else { if (do1.before(dt1)) errs .add(new DigiDocException( DigiDocException.ERR_TIMESTAMP_VERIFY, "OCSP time is not between SignAndRefsTimeStamp and SignatureTimeStamp", null)); } } } return errs; } /** * Verifies this signature. Demands either OCSP confirmation or uses CRL to check signature * validity. * * @param sdoc * parent doc object * @param checkDate * Date on which to check the signature validity * @param bUseOcsp * true if you demand OCSP confirmation from every signature. False if you want to * check against CRL. * @return a possibly empty list of DigiDocException objects */ public ArrayList verifyOcspOrCrl(SignedDoc sdoc, boolean checkDate, boolean bUseOcsp) { Date do1 = null, dt1 = null, dt2 = null; ArrayList errs = new ArrayList(); // check the DataFile digests for (int i = 0; i < sdoc.countDataFiles(); i++) { DataFile df = sdoc.getDataFile(i); // System.out.println("Check digest for DF: " + df.getId()); Reference ref = m_signedInfo.getReferenceForDataFile(df); byte[] dfDig = null; try { dfDig = df.getDigest(); } catch (DigiDocException ex) { errs.add(ex); } if (ref != null) { // System.out.println("Compare it to: " + Base64Util.encode(ref.getDigestValue(), // 0)); if (!SignedDoc.compareDigests(ref.getDigestValue(), dfDig)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for DataFile: " + df.getId(), null)); // System.out.println("BAD DIGEST"); } // else System.out.println("GOOD DIGEST"); } else { // System.out.println("No Reference"); errs.add(new DigiDocException(DigiDocException.ERR_DATA_FILE_NOT_SIGNED, "No Reference element for DataFile: " + df.getId(), null)); } // if this is a detatched file and the file // referred by this entry actually exists, // then go and check it's digest // If the datafile doesn't exist the // just trust whatever is in the XML if (df.getContentType().equals(DataFile.CONTENT_DETATCHED)) { File fTest = new File(df.getFileName()); if (fTest.canRead()) { // System.out.println("Check detatched file: " + fTest.getAbsolutePath()); byte[] realDigest = null; byte[] detDigest = null; try { realDigest = df.calculateDetatchedFileDigest(); detDigest = df.getDigestValue(); } catch (DigiDocException ex) { errs.add(ex); } if (!SignedDoc.compareDigests(detDigest, realDigest)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for detatched file: " + df.getFileName(), null)); } } // else System.out.println("Cannot read detatched file: " + // fTest.getAbsolutePath()); } } // check signed properties digest Reference ref2 = m_signedInfo.getReferenceForSignedProperties(m_sigProp); if (ref2 != null) { byte[] spDig = null; try { spDig = m_sigProp.calculateDigest(); // System.out.println("SignedProp real digest: " + Base64Util.encode(spDig, 0)); } catch (DigiDocException ex) { errs.add(ex); } // System.out.println("Compare it to: " + Base64Util.encode(ref2.getDigestValue(), 0)); if (!SignedDoc.compareDigests(ref2.getDigestValue(), spDig)) { errs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE, "Bad digest for SignedProperties: " + m_sigProp.getId(), null)); // System.out.println("BAD DIGEST"); } // else System.out.println("GOOD DIGEST"); } else { errs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED, "No Reference element for SignedProperties: " + m_sigProp.getId(), null)); } // verify signature value try { byte[] dig = m_signedInfo.calculateDigest(); // System.out.println("SignedInfo real digest: " + Base64Util.encode(dig, 0) + " hex: " // + SignedDoc.bin2hex(dig)); SignedDoc.verify(dig, m_signatureValue.getValue(), m_keyInfo.getSignersCertificate()); // System.out.println("GOOD DIGEST"); } catch (DigiDocException ex) { errs.add(ex); System.out.println("BAD DIGEST"); } // verify signers cert... // check the certs validity dates try { if (checkDate) m_keyInfo.getSignersCertificate().checkValidity(m_sigProp.getSigningTime()); } catch (Exception ex) { errs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED, "Signers certificate has expired!", null)); } // check certificates CA try { DigiDocFactory digFac = FactoryManager.getDigiDocFactory(); digFac.verifyCertificate(m_keyInfo.getSignersCertificate()); } catch (DigiDocException ex) { errs.add(ex); } // switch OCSP or CRL verification if (bUseOcsp) { // use OCSP // check confirmation if (m_unsigProp != null) { ArrayList e = m_unsigProp.verify(sdoc); if (!e.isEmpty()) errs.addAll(e); } else { // not OCSP confirmation errs.add(new DigiDocException(DigiDocException.ERR_NO_CONFIRMATION, "Signature has no OCSP confirmation!", null)); } // verify timestamps ArrayList tsaCerts = findTSACerts(); if (m_timestamps.size() > 0) { TimestampFactory tsFac = null; try { tsFac = FactoryManager.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 (ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIGNATURE) dt1 = ts.getTime(); if (ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS) dt2 = ts.getTime(); } // System.out.println("OCSP time: " + do1); // System.out.println("SignatureTimeStamp time: " + dt1); // System.out.println("SigAndRefsTimeStamp time: " + dt2); int nMaxTsTimeErrSecs = conf.getIntProperty( "DIGIDOC_MAX_TSA_TIME_ERR_SECS", 0); if (dt1 == null) { errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "Cannot retrieve SignatureTimeStamp", null)); } else { dt1 = new Date(dt1.getTime() - (nMaxTsTimeErrSecs * 1000)); if (dt2 != null) { dt2 = new Date(dt2.getTime() + (nMaxTsTimeErrSecs * 1000)); // System.out.println("SignatureTimeStamp adj time: " + dt1); // System.out.println("SigAndRefsTimeStamp adj time: " + dt2); 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)); } else { if (do1.before(dt1)) errs .add(new DigiDocException( DigiDocException.ERR_TIMESTAMP_VERIFY, "OCSP time is not between SignAndRefsTimeStamp and SignatureTimeStamp", null)); } } } } else { try { CRLFactory crlFac = FactoryManager.getCRLFactory(); crlFac.checkCertificate(m_keyInfo.getSignersCertificate(), new Date()); } catch (DigiDocException ex) { errs.add(ex); } } return errs; } /** * Helper method to validate the whole Signature object * * @return a possibly empty list of DigiDocException objects */ public ArrayList validate() { ArrayList errs = new ArrayList(); DigiDocException ex = validateId(m_id); if (ex != null) errs.add(ex); ArrayList e = m_signedInfo.validate(); if (!e.isEmpty()) errs.addAll(e); e = m_signatureValue.validate(); if (!e.isEmpty()) errs.addAll(e); e = m_keyInfo.validate(); if (!e.isEmpty()) errs.addAll(e); e = m_sigProp.validate(); if (!e.isEmpty()) errs.addAll(e); if (m_unsigProp != null) { e = m_unsigProp.validate(); if (!e.isEmpty()) errs.addAll(e); } return errs; } /** * Converts the Signature to XML form * * @return XML representation of Signature */ public byte[] toXML() throws DigiDocException { if (m_origContent == null) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { bos.write(ConvertUtils.str2data("<Signature Id=\"")); bos.write(ConvertUtils.str2data(m_id)); bos.write(ConvertUtils.str2data("\" xmlns=\"" + SignedDoc.xmlns_xmldsig + "\">\n")); bos.write(m_signedInfo.toXML()); bos.write(ConvertUtils.str2data("\n")); bos.write(m_signatureValue.toXML()); bos.write(ConvertUtils.str2data("\n")); bos.write(m_keyInfo.toXML()); // In version 1.3 we use xmlns atributes like specified in XAdES if (m_sigDoc.getVersion().equals(SignedDoc.VERSION_1_3)) { bos.write(ConvertUtils.str2data("\n<Object><QualifyingProperties xmlns=\"")); bos.write(ConvertUtils.str2data(SignedDoc.xmlns_etsi)); bos.write(ConvertUtils.str2data("\" Target=\"#")); bos.write(ConvertUtils.str2data(m_id)); bos.write(ConvertUtils.str2data("\">")); } else // in versions prior to 1.3 we used atributes in wrong places bos.write(ConvertUtils.str2data("\n<Object><QualifyingProperties>")); if (m_sigProp != null) bos.write(m_sigProp.toXML()); if (m_unsigProp != null) bos.write(m_unsigProp.toXML()); bos.write(ConvertUtils.str2data("</QualifyingProperties></Object>\n")); bos.write(ConvertUtils.str2data("</Signature>")); } catch (IOException ex) { DigiDocException.handleException(ex, DigiDocException.ERR_XML_CONVERT); } return bos.toByteArray(); } else { return m_origContent; } } /** * Returns the stringified form of Signature * * @return Signature string representation */ public String toString() { String str = null; try { str = new String(toXML(), "UTF-8"); } catch (Exception ex) { ex.printStackTrace(); } return str; } }