/* * XAdES4j - A Java library for generation and verification of XAdES signatures. * Copyright (C) 2010 Luis Goncalves. * * XAdES4j 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 3 of the License, or any later version. * * XAdES4j 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. * * You should have received a copy of the GNU Lesser General Public License along * with XAdES4j. If not, see <http://www.gnu.org/licenses/>. */ package xades4j.verification; import java.util.Collection; import java.util.HashSet; import java.util.Set; import xades4j.properties.ArchiveTimeStampProperty; import xades4j.properties.CertificateValuesProperty; import xades4j.properties.CompleteCertificateRefsProperty; import xades4j.properties.CompleteRevocationRefsProperty; import xades4j.properties.RevocationValuesProperty; import xades4j.properties.SigAndRefsTimeStampProperty; import xades4j.properties.SignaturePolicyBase; import xades4j.properties.SignatureTimeStampProperty; import xades4j.properties.SigningCertificateProperty; /** * * @author Luís */ class XAdESFormChecker { private XAdESFormChecker() { } static XAdESForm checkForm(Collection<PropertyInfo> props) throws InvalidXAdESFormException { Set<String> availablePropsNames = new HashSet<String>(); for (PropertyInfo propInfo : props) { availablePropsNames.add(propInfo.getProperty().getName()); } XAdESFormDesc formDesc = XADES_C_DESC; do { if (formDesc.check(availablePropsNames)) return formDesc.getForm(); } while ((formDesc = formDesc.getPrevious()) != null); // XAdES G.2.2.1: "The verification process should assess whether the signature // is a XAdES signature and if so, identify the specific form, by inspecting // the different qualifying properties present. The verification process // should not accept as XAdES signature any combination not aligned with // those established in the normative part of the present document or the // extended forms defined in the informative annex B." throw new InvalidXAdESFormException("Signature doesn't follow any of the XAdES forms"); } static final XAdESFormDesc XADES_BES_DESC = new XAdES_BES_Desc(), XADES_EPES_DESC = new XAdES_EPES_Desc(), XADES_T_DESC = new XAdES_T_Desc(), XADES_C_DESC = new XAdES_C_Desc(), XADES_X_DESC = new XAdES_X_Desc(), XADES_X_L_DESC = new XAdES_X_L_Desc(); /**************************************************************************/ /**/ private static abstract class XAdESFormDesc { private XAdESFormDesc[] baseForms; // Ordered from the top format to the lower format. public XAdESFormDesc(XAdESFormDesc... baseForms) { this.baseForms = baseForms; } boolean check(Set<String> availablePropsNames) throws InvalidXAdESFormException { // Check the properties for the current form. if (!checkProps(availablePropsNames)) return false; // If the properties of the current form are available, at least one // of the base forms has to be well-formed. if (baseForms.length == 0) return true; for (int i = 0; i < baseForms.length; i++) { if (baseForms[i].check(availablePropsNames)) return true; } throw new InvalidXAdESFormException(String.format("Required base forms for %s are not present", this.getForm().toString())); } XAdESFormDesc getPrevious() { return baseForms[0]; } /** * Checks the properties for the current form. Should throw an exception * when the form is malformed. * @return true if the format specific properties are available; false otherwise */ protected abstract boolean checkProps(Set<String> availablePropsNames) throws InvalidXAdESFormException; abstract XAdESForm getForm(); } /**/ static class XAdES_BES_Desc extends XAdESFormDesc { @Override protected boolean checkProps(Set<String> availablePropsNames) { return availablePropsNames.contains(SigningCertificateProperty.PROP_NAME); } @Override XAdESForm getForm() { return XAdESForm.BES; } } /**/ static class XAdES_EPES_Desc extends XAdESFormDesc { public XAdES_EPES_Desc() { super(XADES_BES_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) { return availablePropsNames.contains(SignaturePolicyBase.PROP_NAME); } @Override XAdESForm getForm() { return XAdESForm.EPES; } } /**/ static class XAdES_T_Desc extends XAdESFormDesc { public XAdES_T_Desc() { super(XADES_EPES_DESC, XADES_BES_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) { return availablePropsNames.contains(SignatureTimeStampProperty.PROP_NAME); } @Override XAdESForm getForm() { return XAdESForm.T; } } /**/ static class XAdES_C_Desc extends XAdESFormDesc { public XAdES_C_Desc() { super(XADES_T_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) throws InvalidXAdESFormException { boolean hasCompCertRefs = availablePropsNames.contains(CompleteCertificateRefsProperty.PROP_NAME); boolean hasCompRevocRefs = availablePropsNames.contains(CompleteRevocationRefsProperty.PROP_NAME); boolean xor = hasCompCertRefs ^ hasCompRevocRefs; if (xor) throw new InvalidXAdESFormException(String.format( "Both %s and %s have to be present in %s form", CompleteCertificateRefsProperty.PROP_NAME, CompleteRevocationRefsProperty.PROP_NAME, this.getForm().toString())); // If has both props, it's C form. if (hasCompCertRefs) return true; // Can't have attr properties if the other 2 are not present. if (availablePropsNames.contains("AttributeCertificateRefs") || availablePropsNames.contains("AttributeRevocationRefs")) throw new InvalidXAdESFormException("Attr properties cannot be present without the base C form properties"); return false; } @Override XAdESForm getForm() { return XAdESForm.C; } } /**/ static class XAdES_X_Desc extends XAdESFormDesc { public XAdES_X_Desc() { super(XADES_C_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) throws InvalidXAdESFormException { return availablePropsNames.contains(SigAndRefsTimeStampProperty.PROP_NAME) || availablePropsNames.contains("RefsOnlyTimeStamp"); } @Override XAdESForm getForm() { return XAdESForm.X; } } /**/ static class XAdES_X_L_Desc extends XAdESFormDesc { public XAdES_X_L_Desc() { super(XADES_X_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) throws InvalidXAdESFormException { boolean hasCompCert = availablePropsNames.contains(CertificateValuesProperty.PROP_NAME); boolean hasCompRevoc = availablePropsNames.contains(RevocationValuesProperty.PROP_NAME); boolean xor = hasCompCert ^ hasCompRevoc; if (xor) throw new InvalidXAdESFormException(String.format( "Both %s and %s have to be present in %s form", "CertificateValues", "RevocationValues", this.getForm().toString())); return hasCompCert; } @Override XAdESForm getForm() { return XAdESForm.X_L; } } /**/ static class XAdES_A_Desc extends XAdESFormDesc { public XAdES_A_Desc() { super(XADES_X_L_DESC); } @Override protected boolean checkProps(Set<String> availablePropsNames) throws InvalidXAdESFormException { return availablePropsNames.contains(ArchiveTimeStampProperty.PROP_NAME); } @Override XAdESForm getForm() { return XAdESForm.A; } } }