/* * SignedProperties.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.Date; import java.math.BigInteger; import java.util.ArrayList; //import java.util.TimeZone; //import java.util.Calendar; import ee.sk.utils.ConvertUtils; import java.security.cert.X509Certificate; import ee.sk.utils.ConfigManager; import ee.sk.digidoc.factory.CanonicalizationFactory; import ee.sk.digidoc.factory.DigiDocXmlGenFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * Models the SignedProperties element of * an XML-DSIG/ETSI Signature. * @author Veiko Sinivee * @version 1.0 */ public class SignedProperties implements Serializable { private static final long serialVersionUID = 1L; /** signature object to which this belongs */ private Signature m_sig; /** id attribute */ private String m_id; /** target attribute */ private String m_target; /** signing time measured by signers own computer */ private Date m_signingTime; /** signers certs digest algorithm */ private String m_certDigestAlgorithm; /** signers cert id */ private String m_certId; /** signers certs digest data */ private byte[] m_certDigestValue; /** signers certs issuer serial number */ private BigInteger m_certSerial; /** signature production place */ private SignatureProductionPlace m_address; /** claimed roles */ private ArrayList m_claimedRoles; /** digest over the original bytes read from XML file */ private byte[] m_origDigest; /** SignaturePolicyIdentifier */ private SignaturePolicyIdentifier m_signaturePolicyIdentifier; /** DataObjectFormat list */ private ArrayList m_dataObjectFormats; /** SignedDataObjectProperties */ private SignedDataObjectProperties m_sigDataObjectProp; /** * Creates new SignedProperties. Initializes * everything to null * @param sig parent signature */ public SignedProperties(Signature sig) { m_sig = sig; m_id = null; m_target = null; m_signingTime = null; m_certDigestAlgorithm = null; m_certDigestValue = null; m_certSerial = null; m_claimedRoles = null; m_address = null; m_certId = null; m_origDigest = null; m_signaturePolicyIdentifier = null; m_sigDataObjectProp = null; } /** * Creates new SignedProperties. * @param sig parent signature * @param id id attribute value * @param target target attribute value * @param signingTime signing timestamp * @param certId signers cert id (in XML) * @param certDigAlg signers cert digest algorithm id/uri * @param digest signers cert digest value * @param serial signers cert serial number * @throws DigiDocException for validation errors */ public SignedProperties(Signature sig, String id, String target, Date signingTime, String certId, String certDigAlg, byte[] digest, BigInteger serial) throws DigiDocException { m_sig = sig; setId(id); setTarget(target); setSigningTime(signingTime); setCertId(certId); setCertDigestAlgorithm(certDigAlg); setCertDigestValue(digest); setCertSerial(serial); m_claimedRoles = null; m_address = null; m_origDigest = null; m_signaturePolicyIdentifier = null; m_sigDataObjectProp = null; } /** * Creates new SignedProperties with default * values taken from signers certificate and signature * @param sig Signature reference * @param cert signers certificate * @param claimedRoles signers claimed roles * @param adr signers address * @throws DigiDocException for validation errors */ public SignedProperties(Signature sig, X509Certificate cert, String[] claimedRoles, SignatureProductionPlace adr) throws DigiDocException { m_sig = sig; setId(sig.getId() + "-SignedProperties"); setTarget("#" + sig.getId()); setSigningTime(new Date()); setCertId(sig.getId() + "-CERTINFO"); try { String sDigType = ConfigManager.instance().getDefaultDigestType(sig.getSignedDoc()); String sDigAlg = ConfigManager.digType2Alg(sDigType); setCertDigestAlgorithm(sDigAlg); setCertDigestValue(SignedDoc.digestOfType(cert.getEncoded(), sDigType)); } catch(Exception ex) { DigiDocException.handleException(ex, DigiDocException.ERR_CALCULATE_DIGEST); } setCertSerial(cert.getSerialNumber()); if((claimedRoles != null) && (claimedRoles.length > 0)) { for(int i = 0; i < claimedRoles.length; i++) addClaimedRole(claimedRoles[i]); } if(adr != null) setSignatureProductionPlace(adr); m_origDigest = null; } /** * 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 origDigest attribute * @return value of origDigest attribute */ public byte[] getOrigDigest() { return m_origDigest; } /** * Mutator for origDigest attribute * @param str new value for origDigest attribute */ public void setOrigDigest(byte[] data) { m_origDigest = 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_SIGPROP_ID, "Id must not be empty", null); return ex; } /** * Accessor for target attribute * @return value of target attribute */ public String getTarget() { return m_target; } /** * Mutator for target attribute * @param str new value for target attribute * @throws DigiDocException for validation errors */ public void setTarget(String str) throws DigiDocException { DigiDocException ex = validateTarget(str); if(ex != null) throw ex; m_target = str; } /** * Helper method to validate a target * @param str input data * @return exception or null for ok */ private DigiDocException validateTarget(String str) { DigiDocException ex = null; if(str == null && m_sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) && !m_sig.getSignedDoc().getVersion().equals(SignedDoc.VERSION_1_3)) ex = new DigiDocException(DigiDocException.ERR_SIGPROP_TARGET, "Target must be in form: #<signature-id>", null); return ex; } /** * Accessor for certId attribute * @return value of certId attribute */ public String getCertId() { return m_certId; } /** * Mutator for certId attribute * @param str new value for certId attribute * @throws DigiDocException for validation errors */ public void setCertId(String str) throws DigiDocException { if(m_sig.getSignedDoc() != null && !m_sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && !m_sig.getSignedDoc().getVersion().equals(SignedDoc.VERSION_1_3)) { DigiDocException ex = validateCertId(str); if(ex != null) throw ex; } m_certId = str; } /** * Helper method to validate an certificate id * @param str input data * @return exception or null for ok */ private DigiDocException validateCertId(String str) { DigiDocException ex = null; if(str == null && !m_sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) ex = new DigiDocException(DigiDocException.ERR_SIGPROP_CERT_ID, "Cert Id must be in form: <signature-id>-CERTINFO", null); return ex; } /** * Accessor for signatureProductionPlace element * @return value of signatureProductionPlace element */ public SignatureProductionPlace getSignatureProductionPlace() { return m_address; } /** * Mutator for signatureProductionPlace element * @param str new value for signatureProductionPlace element */ public void setSignatureProductionPlace(SignatureProductionPlace adr) throws DigiDocException { m_address = adr; } /** * Accessor for SignaturePolicyIdentifier element * @return value of SignaturePolicyIdentifier element */ public SignaturePolicyIdentifier getSignaturePolicyIdentifier() { return m_signaturePolicyIdentifier; } /** * Mutator for SignaturePolicyIdentifier element * @param str new value for SignaturePolicyIdentifier element */ public void setSignaturePolicyIdentifier(SignaturePolicyIdentifier spi) throws DigiDocException { m_signaturePolicyIdentifier = spi; } /** * Accessor for signingTime attribute * @return value of signingTime attribute */ public Date getSigningTime() { return m_signingTime; } /** * Mutator for signingTime attribute * @param str new value for signingTime attribute * @throws DigiDocException for validation errors */ public void setSigningTime(Date d) throws DigiDocException { DigiDocException ex = validateSigningTime(d); if(ex != null) throw ex; m_signingTime = d; } /** * Helper method to validate a signingTime * @param str input data * @return exception or null for ok */ private DigiDocException validateSigningTime(Date d) { DigiDocException ex = null; if(d == null) // check the uri somehow ??? ex = new DigiDocException(DigiDocException.ERR_SIGNING_TIME, "Singing time cannot be empty!", null); return ex; } /** * Accessor for certDigestAlgorithm attribute * @return value of certDigestAlgorithm attribute */ public String getCertDigestAlgorithm() { return m_certDigestAlgorithm; } /** * Mutator for certDigestAlgorithm attribute * @param str new value for certDigestAlgorithm attribute * @throws DigiDocException for validation errors */ public void setCertDigestAlgorithm(String str) throws DigiDocException { DigiDocException ex = validateCertDigestAlgorithm(str); if(ex != null) throw ex; m_certDigestAlgorithm = str; } /** * Helper method to validate a digest algorithm * @param str input data * @return exception or null for ok */ private DigiDocException validateCertDigestAlgorithm(String str) { DigiDocException ex = null; if(str == null || (!str.equals(SignedDoc.SHA1_DIGEST_ALGORITHM) && !str.equals(SignedDoc.SHA256_DIGEST_ALGORITHM_1) && !str.equals(SignedDoc.SHA256_DIGEST_ALGORITHM_2) && !str.equals(SignedDoc.SHA512_DIGEST_ALGORITHM))) ex = new DigiDocException(DigiDocException.ERR_DIGEST_ALGORITHM, "Currently supports only SHA1, SHA256 or SHA256 digest algorithm", null); return ex; } /** * Accessor for certDigestValue attribute * @return value of certDigestValue attribute */ public byte[] getCertDigestValue() { return m_certDigestValue; } /** * Mutator for certDigestValue attribute * @param data new value for certDigestValue attribute * @throws DigiDocException for validation errors */ public void setCertDigestValue(byte[] data) throws DigiDocException { DigiDocException ex = validateCertDigestValue(data); if(ex != null) throw ex; m_certDigestValue = data; } /** * Helper method to validate a digest value * @param data input data * @return exception or null for ok */ private DigiDocException validateCertDigestValue(byte[] data) { DigiDocException ex = null; if(data == null || (data.length != SignedDoc.SHA1_DIGEST_LENGTH && data.length != SignedDoc.SHA256_DIGEST_LENGTH && data.length != SignedDoc.SHA512_DIGEST_LENGTH)) ex = new DigiDocException(DigiDocException.ERR_DIGEST_LENGTH, "Invalid digest length", null); return ex; } /** * Accessor for certSerial attribute * @return value of certSerial attribute */ public BigInteger getCertSerial() { return m_certSerial; } /** * Mutator for certSerial attribute * @param str new value for certSerial attribute * @throws DigiDocException for validation errors */ public void setCertSerial(BigInteger i) throws DigiDocException { DigiDocException ex = validateCertSerial(i); if(ex != null) throw ex; m_certSerial = i; } /** * Helper method to validate a certSerial * @param str input data * @return exception or null for ok */ private DigiDocException validateCertSerial(BigInteger i) { DigiDocException ex = null; if(i == null) // check the uri somehow ??? ex = new DigiDocException(DigiDocException.ERR_CERT_SERIAL, "Certificates serial number cannot be empty!", null); return ex; } /** * Returns the count of claimedRole objects * @return count of Reference objects */ public int countClaimedRoles() { return ((m_claimedRoles == null) ? 0 : m_claimedRoles.size()); } /** * Adds a new reference object * @param ref Reference object to add */ public void addClaimedRole(String role) { if(m_claimedRoles == null) m_claimedRoles = new ArrayList(); m_claimedRoles.add(role); } /** * Returns the desired claimedRole object * @param idx index of the claimedRole object * @return desired claimedRole object */ public String getClaimedRole(int idx) { return (String)m_claimedRoles.get(idx); } /** * Accessor for SignedDataObjectProperties element * @return value of SignedDataObjectProperties element */ public SignedDataObjectProperties getSignedDataObjectProperties() { return m_sigDataObjectProp; } /** * Mutator for SignedDataObjectProperties element * @param sigDataObjProp new value for SignedDataObjectProperties element */ public void setSignedDataObjectProperties(SignedDataObjectProperties sigDataObjProp) { m_sigDataObjectProp = sigDataObjProp; } /** * Helper method to validate the whole * SignedProperties 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); ex = validateTarget(m_target); if(ex != null) errs.add(ex); if(!m_sig.getSignedDoc().getVersion().equals(SignedDoc.VERSION_1_3)) { ex = validateCertId(m_certId); if(ex != null) errs.add(ex); } if(m_claimedRoles != null && m_claimedRoles.size() > 2) { ex = new DigiDocException(DigiDocException.ERR_INPUT_VALUE, "Currently supports no more than 2 ClaimedRoles", null); errs.add(ex); } ex = validateSigningTime(m_signingTime); if(ex != null) errs.add(ex); ex = validateCertDigestAlgorithm(m_certDigestAlgorithm); if(ex != null) errs.add(ex); ex = validateCertDigestValue(m_certDigestValue); if(ex != null) errs.add(ex); ex = validateCertSerial(m_certSerial); if(ex != null) errs.add(ex); if(m_sigDataObjectProp != null) { ArrayList errs2 = m_sigDataObjectProp.validate(); if(errs2 != null && errs2.size() > 0) errs.addAll(errs2); } // claimed roles // and signature production place are optional return errs; } /** * Calculates the digest of SignedProperties block * @return SignedProperties block digest */ public byte[] calculateDigest() throws DigiDocException { if(m_origDigest == null) { CanonicalizationFactory canFac = ConfigManager. instance().getCanonicalizationFactory(); DigiDocXmlGenFactory genFac = new DigiDocXmlGenFactory(m_sig.getSignedDoc()); byte[] xml = genFac.signedPropertiesToXML(m_sig, this); byte[] tmp = canFac.canonicalize(xml, SignedDoc.CANONICALIZATION_METHOD_20010315); String sDigType = ConfigManager.instance().getDefaultDigestType(m_sig.getSignedDoc()); byte[] hash = SignedDoc.digestOfType(tmp, sDigType); return hash; } else return m_origDigest; } }