/* * UnsignedProperties.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 ee.sk.digidoc; import java.io.Serializable; import java.util.ArrayList; import java.util.Vector; import java.security.cert.X509Certificate; import ee.sk.utils.ConvertUtils; import ee.sk.utils.ConfigManager; import ee.sk.digidoc.factory.BouncyCastleNotaryFactory; import ee.sk.digidoc.factory.NotaryFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.log4j.Logger; /** * Models the unsigned properties of * a signature. * @author Veiko Sinivee * @version 1.0 */ public class UnsignedProperties implements Serializable { private static final long serialVersionUID = 1L; /** signature reference */ private Signature m_signature; /** CompleteCertificateRefs object */ private CompleteCertificateRefs m_certRefs; /** CompleteRevocationRefs object */ private CompleteRevocationRefs m_revRefs; /** Notary object */ private Vector m_notaries; private static Logger m_logger = Logger.getLogger(UnsignedProperties.class); /** * Creates new UsignedProperties * Initializes everything to null * @param sig signature reference */ public UnsignedProperties(Signature sig) { m_signature = sig; m_certRefs = null; m_revRefs = null; m_notaries = null; } /** * Creates new UsignedProperties * @param sig signature reference * @param crefs responders cert digest & info * @param rrefs OCSP response digest & info */ public UnsignedProperties(Signature sig, CompleteCertificateRefs crefs, CompleteRevocationRefs rrefs) throws DigiDocException { m_signature = sig; setCompleteCertificateRefs(crefs); setCompleteRevocationRefs(rrefs); } /** * Accessor for completeCertificateRefs attribute * @return value of completeCertificateRefs attribute */ public CompleteCertificateRefs getCompleteCertificateRefs() { return m_certRefs; } /** * Accessor for signature attribute * @return value of signature attribute */ public Signature getSignature() { return m_signature; } /** * Mutator for completeCertificateRefs attribute * @param str new value for completeCertificateRefs attribute * @throws DigiDocException for validation errors */ public void setCompleteCertificateRefs(CompleteCertificateRefs crefs) throws DigiDocException { //ArrayList errs = crefs.validate(); //if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_certRefs = crefs; } /** * Accessor for completeRevocationRefs attribute * @return value of completeRevocationRefs attribute */ public CompleteRevocationRefs getCompleteRevocationRefs() { return m_revRefs; } /** * Mutator for completeRevocationRefs attribute * @param str new value for completeRevocationRefs attribute * @throws DigiDocException for validation errors */ public void setCompleteRevocationRefs(CompleteRevocationRefs refs) throws DigiDocException { //ArrayList errs = refs.validate(); //if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_revRefs = refs; } /** * Accessor for respondersCertificate attribute * @return value of respondersCertificate attribute */ public X509Certificate getRespondersCertificate() { X509Certificate cert = null; if(m_signature != null) { CertValue cval = m_signature.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER); if(cval != null) cert = cval.getCert(); } return cert; } /** * Mutator for respondersCertificate attribute * @param cert new value for respondersCertificate attribute * @throws DigiDocException for validation errors */ public void setRespondersCertificate(X509Certificate cert) throws DigiDocException { if(m_signature != null && cert != null) { CertValue cval = m_signature.getOrCreateCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER); cval.setId(m_signature.getId() + "-RESPONDER_CERT"); cval.setCert(cert); } } /** * Helper method to validate a responders cert * @param cert input data * @return exception or null for ok */ private DigiDocException validateRespondersCertificate(X509Certificate cert) { DigiDocException ex = null; return ex; } /** * Get the n-th Notary object * @param nIdx Notary index * @return Notary object */ public Notary getNotaryById(int nIdx) { if(m_notaries != null && nIdx < m_notaries.size()) return (Notary)m_notaries.elementAt(nIdx); else return null; } /** * Add a new Notary * @param not Notary object */ public void addNotary(Notary not) { if(m_notaries == null) m_notaries = new Vector(); m_notaries.add(not); } /** * Count the number of Notary objects * @return number of Notary objects */ public int countNotaries() { return (m_notaries != null) ? m_notaries.size() : 0; } /** * Accessor for notary attribute * @return value of notary attribute */ public Notary getNotary() { return getNotaryById(0); } /** * Accessor for notary attribute * @return value of notary attribute */ public Notary getLastNotary() { return getNotaryById(countNotaries()-1); } /** * Mutator for notary attribute * @param str new value for notary attribute * @throws DigiDocException for validation errors */ public void setNotary(Notary not) throws DigiDocException { addNotary(not); } /** * Verifies this confirmation * @param sdoc parent doc object * @return a possibly empty list of DigiDocException objects */ public ArrayList verify(SignedDoc sdoc) { ArrayList errs = new ArrayList(); // verify notary certs serial number using CompleteCertificateRefs X509Certificate cert = getRespondersCertificate(); if(m_logger.isDebugEnabled()) m_logger.debug("Responders cert: " + cert.getSerialNumber() + " - " + cert.getSubjectDN().getName() + " complete cert refs nr: " + m_certRefs.getCertSerial() + " - " + m_certRefs.getCertIssuer()); if(cert == null) { errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "No notarys certificate!", null)); return errs; } if(cert != null && !cert.getSerialNumber().equals(m_certRefs.getCertSerial()) && !m_signature.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) { errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Wrong notarys certificate: " + cert.getSerialNumber() + " ref: " + m_certRefs.getCertSerial(), null)); } // verify notary certs digest using CompleteCertificateRefs try { if(!m_signature.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) { byte[] digest = SignedDoc.digestOfType(cert.getEncoded(), (m_signature.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(m_certRefs.getCertDigestValue(), 0)); if(!SignedDoc.compareDigests(digest, m_certRefs.getCertDigestValue())) { errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Notary certificates digest doesn't match!", null)); m_logger.error("Notary certificates digest doesn't match!"); } } } catch(DigiDocException ex) { errs.add(ex); } catch(Exception ex) { errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Error calculating notary certificate digest!", null)); } // verify notarys digest using CompleteRevocationRefs try { for(int i = 0; i < countNotaries(); i++) { Notary not = 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) { errs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "OCSP value is empty!", null)); continue; } OcspRef orf = m_revRefs.getOcspRefByUri("#" + not.getId()); if(m_logger.isDebugEnabled()) m_logger.debug("OCSP ref: " + ((orf != null) ? orf.getUri() : "NULL")); if(orf == null) { errs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "No OCSP ref for uri: #" + not.getId(), null)); continue; } byte[] digest1 = SignedDoc.digestOfType(ocspData, (m_signature.getSignedDoc().getFormat(). equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE)); byte[] digest2 = orf.getDigestValue(); if(m_logger.isDebugEnabled()) m_logger.debug("Check ocsp: " + not.getId() + " calc hash: " + Base64Util.encode(digest1, 0) + " refs-hash: " + Base64Util.encode(digest2, 0)); if(!SignedDoc.compareDigests(digest1, digest2)) { errs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "Notarys digest doesn't match!", null)); m_logger.error("Notarys digest doesn't match!"); } } } catch(DigiDocException ex) { errs.add(ex); } // verify notary status try { NotaryFactory notFac = ConfigManager.instance().getNotaryFactory(); for(int i = 0; i < countNotaries(); i++) { Notary not = getNotaryById(i); if(m_logger.isDebugEnabled()) m_logger.debug("Verify notray: " + not.getId() + " ocsp: " + ((not.getOcspResponseData() != null) ? not.getOcspResponseData().length : 0) + " responder: " + not.getResponderId()); notFac.parseAndVerifyResponse(m_signature, not); } } catch(DigiDocException ex) { errs.add(ex); } return errs; } /** * Helper method to validate the whole * UnsignedProperties object * @return a possibly empty list of DigiDocException objects */ public ArrayList validate() { ArrayList errs = new ArrayList(); DigiDocException ex = null; X509Certificate cert = getRespondersCertificate(); if(cert == null) ex = validateRespondersCertificate(cert); if(ex != null) errs.add(ex); ArrayList e = null; if(m_certRefs != null) { e = m_certRefs.validate(); if(!e.isEmpty()) errs.addAll(e); } if(m_revRefs != null) { e = m_revRefs.validate(); if(!e.isEmpty()) errs.addAll(e); } // notary ??? return errs; } }