/* * 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 es.uji.security.crypto.openxades.digidoc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.security.cert.X509Certificate; import java.util.ArrayList; 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.utils.ConvertUtils; /** * Models the unsigned properties of a signature. * * @author Veiko Sinivee * @version 1.0 */ public class UnsignedProperties implements Serializable { /** signature reference */ private Signature m_signature; /** CompleteCertificateRefs object */ private CompleteCertificateRefs m_certRefs; /** CompleteRevocationRefs object */ private CompleteRevocationRefs m_revRefs; /** Notary object */ private Notary m_notary; /** * 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_notary = null; } /** * Creates new UsignedProperties * * @param sig * signature reference * @param crefs * responders cert digest & info * @param rrefs * OCSP response digest & info * @param rcert * responders cert * @param not * OCSP response */ public UnsignedProperties(Signature sig, CompleteCertificateRefs crefs, CompleteRevocationRefs rrefs, X509Certificate rcert, Notary not) throws DigiDocException { m_signature = sig; setCompleteCertificateRefs(crefs); setCompleteRevocationRefs(rrefs); setRespondersCertificate(rcert); setNotary(not); } /** * 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; // System.out.println("UnsignedProp sig: " + ((m_signature == null) ? "NULL" : "OK")); if (m_signature != null) { CertValue cval = m_signature.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER); // System.out.println("UnsignedProp cval: " + ((cval == null) ? "NULL" : "OK")); 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 { DigiDocException ex = validateRespondersCertificate(cert); if (ex != null) throw ex; if (m_signature != null) { CertValue cval = m_signature .getOrCreateCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER); 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; if (cert == null) ex = new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Notarys certificate is required", null); return ex; } /** * Accessor for notary attribute * * @return value of notary attribute */ public Notary getNotary() { return m_notary; } /** * Mutator for notary attribute * * @param str * new value for notary attribute * @throws DigiDocException * for validation errors */ public void setNotary(Notary not) throws DigiDocException { // ArrayList errs = not.validate(); // if(!errs.isEmpty()) // throw (DigiDocException)errs.get(0); m_notary = 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(); // System.out.println("Responders cert: " + getRespondersCertificate().getSerialNumber() + // " complete cert refs nr: " + m_certRefs.getCertSerial()); 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())) { errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Wrong notarys certificate!", null)); } // verify notary certs digest using CompleteCertificateRefs try { byte[] digest = SignedDoc.digest(cert.getEncoded()); if (!SignedDoc.compareDigests(digest, m_certRefs.getCertDigestValue())) errs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT, "Notary certificates digest doesn't match!", null)); } 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 { byte[] ocspData = m_notary.getOcspResponseData(); // System.out.println("OCSP data len: " + ocspData.length); byte[] digest1 = SignedDoc.digest(ocspData); // System.out.println("Calculated digest: " + Base64Util.encode(digest1, 0)); byte[] digest2 = m_revRefs.getDigestValue(); // System.out.println("Real digest: " + Base64Util.encode(digest2, 0)); if (!SignedDoc.compareDigests(digest1, digest2)) errs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "Notarys digest doesn't match!", null)); } catch (DigiDocException ex) { errs.add(ex); } // verify notary status try { NotaryFactory notFac = FactoryManager.getNotaryFactory(); notFac.parseAndVerifyResponse(m_signature, m_notary); } 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; } /** * Converts the UnsignedProperties to XML form * * @return XML representation of UnsignedProperties */ public byte[] toXML() throws DigiDocException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { if (m_signature.getSignedDoc().getVersion().equals(SignedDoc.VERSION_1_3)) { bos.write(ConvertUtils.str2data("<UnsignedProperties>")); } else { bos.write(ConvertUtils.str2data("<UnsignedProperties Target=\"#")); bos.write(ConvertUtils.str2data(m_signature.getId())); bos.write(ConvertUtils.str2data("\">")); } bos.write(ConvertUtils.str2data("\n<UnsignedSignatureProperties>")); if (m_certRefs != null) bos.write(m_certRefs.toXML()); if (m_revRefs != null) { bos.write(m_revRefs.toXML()); bos.write(ConvertUtils.str2data("\n")); } bos.write(ConvertUtils.str2data("<CertificateValues>\n")); for (int i = 0; i < m_signature.countCertValues(); i++) { CertValue cval = m_signature.getCertValue(i); if (cval.getType() != CertValue.CERTVAL_TYPE_SIGNER) bos.write(cval.toXML()); } bos.write(ConvertUtils.str2data("</CertificateValues>")); if (m_notary != null) { bos.write(ConvertUtils.str2data("\n")); bos.write(m_notary.toXML(m_signature.getSignedDoc().getVersion())); } for (int i = 0; i < m_signature.countTimestampInfos(); i++) { TimestampInfo ts = (TimestampInfo) m_signature.getTimestampInfo(i); bos.write(ConvertUtils.str2data(ts.toString())); } bos.write(ConvertUtils .str2data("</UnsignedSignatureProperties>\n</UnsignedProperties>")); } catch (IOException ex) { DigiDocException.handleException(ex, DigiDocException.ERR_XML_CONVERT); } return bos.toByteArray(); } /** * Returns the stringified form of UnsignedProperties * * @return UnsignedProperties string representation */ public String toString() { String str = null; try { str = new String(toXML()); } catch (Exception ex) { } return str; } }