diff --git a/pom.xml b/pom.xml index d4cc98ae..44d20eb4 100644 --- a/pom.xml +++ b/pom.xml @@ -72,8 +72,7 @@ 1.56 - - + ossrh https://oss.sonatype.org/content/repositories/snapshots @@ -97,6 +96,29 @@ + + test-with-proxy + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + http.proxyHost + ${http.proxyHost} + + + http.proxyPort + ${http.proxyPort} + + + + + + + release @@ -170,4 +192,4 @@ - \ No newline at end of file + diff --git a/src/main/java/xades4j/production/DataGenArchiveTimeStamp.java b/src/main/java/xades4j/production/DataGenArchiveTimeStamp.java index 61a37923..23102e36 100644 --- a/src/main/java/xades4j/production/DataGenArchiveTimeStamp.java +++ b/src/main/java/xades4j/production/DataGenArchiveTimeStamp.java @@ -28,11 +28,8 @@ import org.w3c.dom.Element; import xades4j.properties.ArchiveTimeStampProperty; import xades4j.properties.CertificateValuesProperty; -import xades4j.properties.CompleteCertificateRefsProperty; -import xades4j.properties.CompleteRevocationRefsProperty; import xades4j.properties.QualifyingProperty; import xades4j.properties.RevocationValuesProperty; -import xades4j.properties.SignatureTimeStampProperty; import xades4j.properties.data.ArchiveTimeStampData; import xades4j.properties.data.BaseXAdESTimeStampData; import xades4j.providers.AlgorithmsProviderEx; @@ -94,13 +91,10 @@ protected void addPropSpecificTimeStampInput( if (ki != null) digestInput.addNode(ki.getElement()); - // Unsigned properties, in order of appearance. - Map propsCnt = new HashMap(5); + // Required properties, in order of appearance. + Map propsCnt = new HashMap(2); propsCnt.put(CertificateValuesProperty.PROP_NAME, 0); propsCnt.put(RevocationValuesProperty.PROP_NAME, 0); - propsCnt.put(CompleteCertificateRefsProperty.PROP_NAME, 0); - propsCnt.put(CompleteRevocationRefsProperty.PROP_NAME, 0); - propsCnt.put(SignatureTimeStampProperty.PROP_NAME, 0); e = DOMHelper.getFirstChildElement(unsignedSigPropsElem); // UnsignedProperties shouldn't be empty! diff --git a/src/main/java/xades4j/production/DataGenAttrAuthoritiesCertValues.java b/src/main/java/xades4j/production/DataGenAttrAuthoritiesCertValues.java new file mode 100644 index 00000000..3da272bb --- /dev/null +++ b/src/main/java/xades4j/production/DataGenAttrAuthoritiesCertValues.java @@ -0,0 +1,54 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.production; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.CertificateValuesData; +import xades4j.properties.data.PropertyDataObject; + +/** + * @author Hubert Kario + */ +public class DataGenAttrAuthoritiesCertValues + implements PropertyDataObjectGenerator +{ + @Override + public PropertyDataObject generatePropertyData( + AttrAuthoritiesCertValuesProperty prop, + PropertiesDataGenerationContext ctx) + throws PropertyDataGenerationException + { + AttrAuthoritiesCertValuesData attrAuthCertValuesData = + new AttrAuthoritiesCertValuesData(); + try + { + for (X509Certificate cer : prop.getCertificates()) + { + attrAuthCertValuesData.addData(cer.getEncoded()); + } + } catch (CertificateEncodingException ex) + { + throw new PropertyDataGenerationException(prop, "cannot get encoded certificate", ex); + } + + return attrAuthCertValuesData; + } +} diff --git a/src/main/java/xades4j/production/DataGenAttributeRevocationValues.java b/src/main/java/xades4j/production/DataGenAttributeRevocationValues.java new file mode 100644 index 00000000..d17cb6a9 --- /dev/null +++ b/src/main/java/xades4j/production/DataGenAttributeRevocationValues.java @@ -0,0 +1,53 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.production; + +import java.security.cert.CRLException; +import java.security.cert.X509CRL; + +import xades4j.properties.AttributeRevocationValuesProperty; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.PropertyDataObject; + +/** + * + * @author Hubert Kario + * + */ +public class DataGenAttributeRevocationValues implements + PropertyDataObjectGenerator +{ + @Override + public PropertyDataObject generatePropertyData( + AttributeRevocationValuesProperty prop, + PropertiesDataGenerationContext ctx) + throws PropertyDataGenerationException + { + AttributeRevocationValuesData attrRevocValuesData = new AttributeRevocationValuesData(); + try + { + for (X509CRL crl : prop.getCrls()) + { + attrRevocValuesData.addData(crl.getEncoded()); + } + } catch (CRLException ex) + { + throw new PropertyDataGenerationException(prop, "cannot get encoded CRL", ex); + } + return attrRevocValuesData; + } +} diff --git a/src/main/java/xades4j/production/DataGenTimeStampValidationData.java b/src/main/java/xades4j/production/DataGenTimeStampValidationData.java new file mode 100644 index 00000000..dbca80c9 --- /dev/null +++ b/src/main/java/xades4j/production/DataGenTimeStampValidationData.java @@ -0,0 +1,71 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.production; + +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; + +import xades4j.properties.TimeStampValidationDataProperty; +import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.TimeStampValidationDataData; + +/** + * + * @author Hubert Kario + * + */ +public class DataGenTimeStampValidationData implements + PropertyDataObjectGenerator +{ + + @Override + public PropertyDataObject generatePropertyData( + TimeStampValidationDataProperty prop, + PropertiesDataGenerationContext ctx) + throws PropertyDataGenerationException + { + TimeStampValidationDataData tsValidationDataData = + new TimeStampValidationDataData(); + + try + { + for (X509Certificate cer : prop.getCertificates()) + { + tsValidationDataData.addCertificateData(cer.getEncoded()); + } + } catch (CertificateEncodingException e) + { + throw new PropertyDataGenerationException(prop, "cannot get encoded certificate", e); + } + + try + { + for (X509CRL crl : prop.getCrls()) + { + tsValidationDataData.addCRLData(crl.getEncoded()); + } + } catch (CRLException e) + { + throw new PropertyDataGenerationException(prop, "cannot get encoded CRL", e); + } + + return tsValidationDataData; + } + +} diff --git a/src/main/java/xades4j/production/DefaultProductionBindingsModule.java b/src/main/java/xades4j/production/DefaultProductionBindingsModule.java index a0797773..c75f8ed6 100644 --- a/src/main/java/xades4j/production/DefaultProductionBindingsModule.java +++ b/src/main/java/xades4j/production/DefaultProductionBindingsModule.java @@ -22,6 +22,8 @@ import xades4j.properties.AllDataObjsCommitmentTypeProperty; import xades4j.properties.AllDataObjsTimeStampProperty; import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; import xades4j.properties.CertificateValuesProperty; import xades4j.properties.CommitmentTypeProperty; import xades4j.properties.CompleteCertificateRefsProperty; @@ -39,6 +41,7 @@ import xades4j.properties.SignerRoleProperty; import xades4j.properties.SigningCertificateProperty; import xades4j.properties.SigningTimeProperty; +import xades4j.properties.TimeStampValidationDataProperty; import xades4j.properties.data.CustomPropertiesDataObjsStructureVerifier; import xades4j.providers.AlgorithmsProvider; import xades4j.providers.AlgorithmsProviderEx; @@ -163,12 +166,24 @@ public void provideProperties(DataObjectDesc dataObj) { }).to(DataGenCertificateValues.class); + bind(new TypeLiteral>() + { + }).to(DataGenAttrAuthoritiesCertValues.class); + bind(new TypeLiteral>() { }).to(DataGenRevocationValues.class); + bind(new TypeLiteral>() + { + }).to(DataGenAttributeRevocationValues.class); + bind(new TypeLiteral>() { }).to(DataGenArchiveTimeStamp.class); + + bind(new TypeLiteral>() + { + }).to(DataGenTimeStampValidationData.class); } } diff --git a/src/main/java/xades4j/production/SignaturePropertiesCollectorImpl.java b/src/main/java/xades4j/production/SignaturePropertiesCollectorImpl.java index bb03c5b6..31124631 100644 --- a/src/main/java/xades4j/production/SignaturePropertiesCollectorImpl.java +++ b/src/main/java/xades4j/production/SignaturePropertiesCollectorImpl.java @@ -26,6 +26,7 @@ import xades4j.properties.SigningTimeProperty; import xades4j.properties.UnsignedSignatureProperty; import xades4j.providers.SignaturePropertiesCollector; +import xades4j.utils.PropertiesList; import xades4j.utils.PropertiesSet; /** @@ -34,13 +35,13 @@ class SignaturePropertiesCollectorImpl implements SignaturePropertiesCollector { private final PropertiesSet signedSigProps; - private final PropertiesSet unsignedSigProps; + private final PropertiesList unsignedSigProps; public SignaturePropertiesCollectorImpl() { this.signedSigProps = new PropertiesSet(2); - this.unsignedSigProps = new PropertiesSet(0); + this.unsignedSigProps = new PropertiesList(0); } /***** Signed signature properties *****/ diff --git a/src/main/java/xades4j/production/XadesCSigningProfile.java b/src/main/java/xades4j/production/XadesCSigningProfile.java index d522a024..5db0a88f 100644 --- a/src/main/java/xades4j/production/XadesCSigningProfile.java +++ b/src/main/java/xades4j/production/XadesCSigningProfile.java @@ -28,10 +28,19 @@ *

* The {@code AttributeCertificateRefs} and {@code AttributeRevocationRefs} properties * are not supported. + *

+ * If the (implicit or explicit) policy you're following requires grace periods for + * signature creation, it is highly recommended not to use this signing profile + * but to use {@link XadesTSigningProfile} for initial signature creation and then extend + * the signature to XAdES-C form after the grace period has elapsed and new revocation + * information is available. * @author Luís */ public class XadesCSigningProfile extends XadesTSigningProfile { + /** + * @see XadesCSigningProfile + */ public XadesCSigningProfile( KeyingDataProvider keyingProvider, ValidationDataProvider validationDataProv) @@ -40,6 +49,9 @@ public XadesCSigningProfile( withBinding(ValidationDataProvider.class, validationDataProv); } + /** + * @see XadesCSigningProfile + */ public XadesCSigningProfile( KeyingDataProvider keyingProvider, Class validationDataProvClass) @@ -48,6 +60,9 @@ public XadesCSigningProfile( withBinding(ValidationDataProvider.class, validationDataProvClass); } + /** + * @see XadesCSigningProfile + */ public XadesCSigningProfile( Class keyingProviderClass, ValidationDataProvider validationDataProv) @@ -56,6 +71,9 @@ public XadesCSigningProfile( withBinding(ValidationDataProvider.class, validationDataProv); } + /** + * @see XadesCSigningProfile + */ public XadesCSigningProfile( Class keyingProviderClass, Class validationDataProvClass) diff --git a/src/main/java/xades4j/production/XadesFormatExtenderProfile.java b/src/main/java/xades4j/production/XadesFormatExtenderProfile.java index 52a46c54..8c05a372 100644 --- a/src/main/java/xades4j/production/XadesFormatExtenderProfile.java +++ b/src/main/java/xades4j/production/XadesFormatExtenderProfile.java @@ -34,7 +34,7 @@ * is used to add unsigned signature properties to an existing signature in order * augment its format. This can be done as part of the {@link xades4j.verification.XadesVerifier#verify(org.w3c.dom.Element, xades4j.verification.SignatureSpecificVerificationOptions, xades4j.production.XadesSignatureFormatExtender, xades4j.verification.XAdESForm) verification process}. * The {@code XadesSignatureFormatExtender} can also be used separately, but no - * checks are made on the correctness of the signature. + * checks are made on the correctness of the signature or added properties. *

* This profile follows the same principles of {@link XadesSigningProfile}. * @author Luís diff --git a/src/main/java/xades4j/production/XadesSigningProfile.java b/src/main/java/xades4j/production/XadesSigningProfile.java index 652eb7d0..6e10b13d 100644 --- a/src/main/java/xades4j/production/XadesSigningProfile.java +++ b/src/main/java/xades4j/production/XadesSigningProfile.java @@ -44,7 +44,9 @@ * The purpose of this class is to configure a {@link XadesSigner} that will actually * produce signatures with those characteristics. *

- * Only a {@link KeyingDataProvider} has to externally be supplied. All the other components + * Only a {@link KeyingDataProvider} has to externally be supplied. If you're creating + * a XAdES-T or XAdES-C form, it's highly recommended to change the default + * {@link TimeStampTokenProvider}. All the other components * have default implementations that are used if no other actions are taken. However, * all of them can be replaced through the corresponding methods, either by an instance * or a class. When a class is used it may have dependencies on other components, @@ -58,12 +60,14 @@ *

* The XAdES form is also part of the profile. Each form has additional requirements, * hence being defined by a specific subclass. There are profiles up to XAdES-C. - * The extended formats are also supported (with a few limitations) but can only - * be added after verfication ({@link xades4j.verification.XadesVerifier}). + * If the policy you're implementing requires grace periods for signatures, it's highly + * recommended to not create XAdES-C form directly. + * The extended formats are also supported but can only + * be added after verification ({@link xades4j.verification.XadesVerifier}). *

* Repeated dependency bindings will not cause an immediate error. An exception * will be thrown when an instance of {@code XadesSigner} is requested. - * + * * @see XadesBesSigningProfile * @see XadesEpesSigningProfile * @see XadesTSigningProfile @@ -103,7 +107,7 @@ protected XadesSigningProfile( /** * Creates a new {@code XadesSigner} based on the current state of the profile. * If any changes are made after this call, the previously returned signer will - * not be afected. Other signers can be created, accumulating the profile changes. + * not be affected. Other signers can be created, accumulating the profile changes. * @return a {@code XadesSigner} accordingly to this profile * @throws XadesProfileResolutionException if the dependencies of the signer (direct and indirect) cannot be resolved */ @@ -116,7 +120,7 @@ public final XadesSigner newSigner() throws XadesProfileResolutionException /***/ /** - * Adds a type dependency mapping to the profile. This is tipically done from an + * Adds a type dependency mapping to the profile. This is typically done from an * interface to a type that implements that interface. When a dependency to * {@code from} is found, the {@code to} class is used. The {@code to} class * may in turn have its own dependencies. diff --git a/src/main/java/xades4j/production/XadesTSigningProfile.java b/src/main/java/xades4j/production/XadesTSigningProfile.java index 295b8442..dd8d31d0 100644 --- a/src/main/java/xades4j/production/XadesTSigningProfile.java +++ b/src/main/java/xades4j/production/XadesTSigningProfile.java @@ -22,22 +22,29 @@ /** * A profile for producing XAdES-T signatures. A {@link KeyingDataProvider} has * to be supplied. The library has a default {@link xades4j.providers.TimeStampTokenProvider} - * that will be used to configure the {@code XadesSigner}. As all teh other components + * that will be used to configure the {@code XadesSigner}. As all the other components * it can be exchanged. *

* A {@link SignaturePolicyInfoProvider} should be added to produce a XAdES-T based * on XAdES-EPES. * @see XadesSigningProfile + * @see XadesCSigningProfile * @author Luís */ public class XadesTSigningProfile extends XadesSigningProfile { + /** + * @see XadesTSigningProfile + */ public XadesTSigningProfile( Class keyingProviderClass) { super(keyingProviderClass); } + /** + * @see XadesTSigningProfile + */ public XadesTSigningProfile(KeyingDataProvider keyingProvider) { super(keyingProvider); diff --git a/src/main/java/xades4j/properties/AllDataObjsTimeStampProperty.java b/src/main/java/xades4j/properties/AllDataObjsTimeStampProperty.java index b9af316a..efaa69d2 100644 --- a/src/main/java/xades4j/properties/AllDataObjsTimeStampProperty.java +++ b/src/main/java/xades4j/properties/AllDataObjsTimeStampProperty.java @@ -18,6 +18,8 @@ import java.util.Date; +import xades4j.providers.ValidationData; + /** * Represents a time-stamp computed before the signature production, over the sequence * formed by ALL the {@code ds:Reference} elements within the {@code ds:SignedInfo} @@ -31,9 +33,11 @@ * @author Luís */ public final class AllDataObjsTimeStampProperty extends SignedDataObjectProperty + implements BaseXAdESTimeStampProperty { public static final String PROP_NAME = "AllDataObjectsTimeStamp"; private Date time; + private ValidationData validationData; public AllDataObjsTimeStampProperty() { @@ -64,4 +68,16 @@ public void setTime(Date time) { this.time = time; } + + @Override + public ValidationData getValidationData() + { + return validationData; + } + + @Override + public void setValidationData(ValidationData validationData) + { + this.validationData = validationData; + } } diff --git a/src/main/java/xades4j/properties/ArchiveTimeStampProperty.java b/src/main/java/xades4j/properties/ArchiveTimeStampProperty.java index 6af6f7f3..cfb0835b 100644 --- a/src/main/java/xades4j/properties/ArchiveTimeStampProperty.java +++ b/src/main/java/xades4j/properties/ArchiveTimeStampProperty.java @@ -18,21 +18,27 @@ import java.util.Date; +import xades4j.providers.ValidationData; + /** * The {@code xades141:ArchiveTimeStamp} unsigned signature property. Used for the * XAdES-A form. * @author Luís */ public final class ArchiveTimeStampProperty extends UnsignedSignatureProperty + implements BaseXAdESTimeStampProperty { - public static final String PROP_NAME = "xadesv141:ArchiveTimeStamp"; + // it's in "xades141" namespace though! + public static final String PROP_NAME = "ArchiveTimeStamp"; /**/ private Date time; + private ValidationData validationData; /** * Gets the time-stamp time after signature production or verification. * @return the time or {@code null} if the property wasn't part of a signature production */ + @Override public Date getTime() { return time; @@ -43,6 +49,7 @@ public Date getTime() * the time-stamp can be accessed afterwards. * @param time the time */ + @Override public void setTime(Date time) { this.time = time; @@ -51,6 +58,18 @@ public void setTime(Date time) @Override public String getName() { - throw new UnsupportedOperationException("Not supported yet."); + return PROP_NAME; } + + @Override + public ValidationData getValidationData() + { + return validationData; + } + + @Override + public void setValidationData(ValidationData validationData) + { + this.validationData = validationData; + } } diff --git a/src/main/java/xades4j/properties/AttrAuthoritiesCertValuesProperty.java b/src/main/java/xades4j/properties/AttrAuthoritiesCertValuesProperty.java new file mode 100644 index 00000000..39b29737 --- /dev/null +++ b/src/main/java/xades4j/properties/AttrAuthoritiesCertValuesProperty.java @@ -0,0 +1,56 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.properties; + +import java.security.cert.X509Certificate; +import java.util.Collection; + +/** + * The {@code AttrAuthoritiesCertValues} is an optional unsigned property and qualifies + * the XML signatures. There can be at most one occurrence of this property in the + * signature. + *

+ * This element should have the full set of certificates that are needed to verify + * time stamps in {@code SignatureTimeStamp}, {@code SigAndRefsTimeStamp} and + * {@code RefsOnlyTimeStamp}. + * This property is optional part of XAdES-X-L form. + * @author Hubert Kario + */ +public final class AttrAuthoritiesCertValuesProperty extends UnsignedSignatureProperty +{ + + public static final String PROP_NAME = "AttrAuthoritiesCertValues"; + private final Collection certificates; + + public AttrAuthoritiesCertValuesProperty(Collection certificates) + { + if (null == certificates) + throw new NullPointerException(); + this.certificates = certificates; + } + + public Collection getCertificates() + { + return certificates; + } + + @Override + public String getName() + { + return PROP_NAME; + } +} diff --git a/src/main/java/xades4j/properties/AttributeRevocationValuesProperty.java b/src/main/java/xades4j/properties/AttributeRevocationValuesProperty.java new file mode 100644 index 00000000..ed7a9179 --- /dev/null +++ b/src/main/java/xades4j/properties/AttributeRevocationValuesProperty.java @@ -0,0 +1,55 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.properties; + +import java.security.cert.X509CRL; +import java.util.Collection; + +/** + * The {@code AttributeRevocationValues} property is an optional unsigned property and + * qualifies the XML signature. There is at most one occurrence of this property in the + * signature. + *

+ * The {@code AttributeRevocationValues} property is used to hold the values of revocation + * information which are needed to check validity of TSA certificates in TimeStamps. + * + * @author Hubert Kario + * + */ +public class AttributeRevocationValuesProperty extends + UnsignedSignatureProperty +{ + public static final String PROP_NAME = "AttributeRevocationValues"; + private final Collection crls; + + public AttributeRevocationValuesProperty(Collection crls) + { + if (crls == null) + throw new NullPointerException(); + this.crls = crls; + } + public Collection getCrls() + { + return crls; + } + + @Override + public String getName() + { + return PROP_NAME; + } +} diff --git a/src/main/java/xades4j/properties/BaseXAdESTimeStampProperty.java b/src/main/java/xades4j/properties/BaseXAdESTimeStampProperty.java new file mode 100644 index 00000000..d5ad262a --- /dev/null +++ b/src/main/java/xades4j/properties/BaseXAdESTimeStampProperty.java @@ -0,0 +1,55 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.properties; + +import java.util.Date; + +import xades4j.providers.ValidationData; + +/** + * Interface applicable to all TimeStampProperties used in XAdES signatures. + * Provides the ability to query for time from time stamp token and validation data + * used to verify the token. + * + * @author Hubert Kario + * + */ +public interface BaseXAdESTimeStampProperty extends QualifyingProperty +{ + /** + * Return the time at which the token claims to be generated. + *

+ * Note: if there are multiple time stamp tokens in single property, only the time + * from last one will be returned. + * @return time from time stamp token in the property + */ + public Date getTime(); + + public void setTime(Date time); + + /** + * Validation data (certificates and CRLs) used to check validity of the time stamp + * token + *

+ * Note: if there are multiple time stamp tokens in property, only validation data + * for the last one will be returned. + * @return validation data + */ + public ValidationData getValidationData(); + + public void setValidationData(ValidationData validationData); +} diff --git a/src/main/java/xades4j/properties/IndividualDataObjsTimeStampProperty.java b/src/main/java/xades4j/properties/IndividualDataObjsTimeStampProperty.java index bfb77522..6c39ce7b 100644 --- a/src/main/java/xades4j/properties/IndividualDataObjsTimeStampProperty.java +++ b/src/main/java/xades4j/properties/IndividualDataObjsTimeStampProperty.java @@ -18,6 +18,8 @@ import java.util.Date; +import xades4j.providers.ValidationData; + /** * Represents a time-stamp computed before the signature production, over a sequence * formed by some of the {@code ds:Reference} elements within the {@code ds:SignedInfo} @@ -31,10 +33,12 @@ * @author Luís */ public final class IndividualDataObjsTimeStampProperty extends SignedDataObjectProperty + implements BaseXAdESTimeStampProperty { public static final String PROP_NAME = "IndividualDataObjectsTimeStamp"; /**/ private Date time; + private ValidationData validationData; public IndividualDataObjsTimeStampProperty() { @@ -65,4 +69,16 @@ public void setTime(Date time) { this.time = time; } + + @Override + public ValidationData getValidationData() + { + return validationData; + } + + @Override + public void setValidationData(ValidationData validationData) + { + this.validationData = validationData; + } } diff --git a/src/main/java/xades4j/properties/SigAndRefsTimeStampProperty.java b/src/main/java/xades4j/properties/SigAndRefsTimeStampProperty.java index df397275..488bab0d 100644 --- a/src/main/java/xades4j/properties/SigAndRefsTimeStampProperty.java +++ b/src/main/java/xades4j/properties/SigAndRefsTimeStampProperty.java @@ -18,6 +18,8 @@ import java.util.Date; +import xades4j.providers.ValidationData; + /** * The {@code SigAndRefsTimeStamp} element is an unsigned property qualifying * the signature. @@ -29,10 +31,12 @@ * @author Luís */ public final class SigAndRefsTimeStampProperty extends UnsignedSignatureProperty + implements BaseXAdESTimeStampProperty { public static final String PROP_NAME = "SigAndRefsTimeStamp"; /**/ private Date time; + private ValidationData validationData; /** * Gets the time-stamp time. @@ -53,4 +57,16 @@ public String getName() { return PROP_NAME; } + + @Override + public ValidationData getValidationData() + { + return validationData; + } + + @Override + public void setValidationData(ValidationData validationData) + { + this.validationData = validationData; + } } diff --git a/src/main/java/xades4j/properties/SignatureTimeStampProperty.java b/src/main/java/xades4j/properties/SignatureTimeStampProperty.java index ca01d980..d8719e0a 100644 --- a/src/main/java/xades4j/properties/SignatureTimeStampProperty.java +++ b/src/main/java/xades4j/properties/SignatureTimeStampProperty.java @@ -18,16 +18,19 @@ import java.util.Date; +import xades4j.providers.ValidationData; + /** * The {@code SignatureTimeStamp} property encapsulates the time-stamp over the * {@code ds:SignatureValue} element. * @author Luís */ public final class SignatureTimeStampProperty extends UnsignedSignatureProperty + implements BaseXAdESTimeStampProperty { public static final String PROP_NAME = "SignatureTimeStamp"; - /**/ private Date time; + private ValidationData validationData; public SignatureTimeStampProperty() { @@ -39,17 +42,27 @@ public String getName() return PROP_NAME; } - /** - * Gets the time-stamp time. - * @return the time or {@code null} if the property hasn't been processed in signature production or verification. - */ + @Override public Date getTime() { return time; } + @Override public void setTime(Date time) { this.time = time; } + + @Override + public ValidationData getValidationData() + { + return validationData; + } + + @Override + public void setValidationData(ValidationData validationData) + { + this.validationData = validationData; + } } diff --git a/src/main/java/xades4j/properties/TimeStampValidationDataProperty.java b/src/main/java/xades4j/properties/TimeStampValidationDataProperty.java new file mode 100644 index 00000000..3caba07a --- /dev/null +++ b/src/main/java/xades4j/properties/TimeStampValidationDataProperty.java @@ -0,0 +1,75 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.properties; + +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Collection; + +/** + * The {@code TimeStampValidationData} is an optional unsigned property that extends + * the XAdES-A format with information needed to verify previously signed time stamps. + * There can be multiple occurrences of this property in signature. + *

+ * In principle, the {@code TimeStampValidationData} element contains the full set of + * certificates and revocation information (CRLs or OCSP responses) that have been used + * to validate previous time stamp (XAdES-X time stamp in case of first + * {@code TimeStampValidationData} added after creation of first + * {@code ArchiveTimeStamp}). + * @author Hubert Kario + */ +public class TimeStampValidationDataProperty extends UnsignedSignatureProperty +{ + // it is in xadesv141 namespace though! + public static final String PROP_NAME = "TimeStampValidationData"; + private final Collection certificateValues; + private final Collection crls; + + /** + * Both parameters can be {@code null} or empty, but not at the same time + * + * @param certificates can be null + * @param crls can be null + */ + public TimeStampValidationDataProperty(Collection certificates, + Collection crls) + { + if ((certificates == null || certificates.isEmpty()) && + ((crls == null) || crls.isEmpty())) + throw new NullPointerException("Both parameters can't be null/empty"); + + this.certificateValues = certificates; + this.crls = crls; + } + + public Collection getCrls() + { + return crls; + } + + public Collection getCertificates() + { + return certificateValues; + } + + @Override + public String getName() + { + return PROP_NAME; + } + +} diff --git a/src/main/java/xades4j/properties/data/AttrAuthoritiesCertValuesData.java b/src/main/java/xades4j/properties/data/AttrAuthoritiesCertValuesData.java new file mode 100644 index 00000000..e666feaa --- /dev/null +++ b/src/main/java/xades4j/properties/data/AttrAuthoritiesCertValuesData.java @@ -0,0 +1,24 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.properties.data; + +/** + * @author Hubert Kario + */ +public class AttrAuthoritiesCertValuesData extends BaseEncapsulatedPKIData +{ +} diff --git a/src/main/java/xades4j/properties/data/AttributeRevocationValuesData.java b/src/main/java/xades4j/properties/data/AttributeRevocationValuesData.java new file mode 100644 index 00000000..e1531dac --- /dev/null +++ b/src/main/java/xades4j/properties/data/AttributeRevocationValuesData.java @@ -0,0 +1,21 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.properties.data; + +public class AttributeRevocationValuesData extends BaseEncapsulatedPKIData +{ +} diff --git a/src/main/java/xades4j/properties/data/BaseValidationDataData.java b/src/main/java/xades4j/properties/data/BaseValidationDataData.java new file mode 100644 index 00000000..02162078 --- /dev/null +++ b/src/main/java/xades4j/properties/data/BaseValidationDataData.java @@ -0,0 +1,59 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.properties.data; + +import java.util.Collection; + +public class BaseValidationDataData implements PropertyDataObject +{ + private final CertificateValuesData certificateValues; + private final RevocationValuesData crlData; + + public BaseValidationDataData( + Collection certificates, + Collection crlData) + { + this.certificateValues = new CertificateValuesData(certificates); + this.crlData = new RevocationValuesData(crlData); + } + + public BaseValidationDataData() + { + this.certificateValues = new CertificateValuesData(); + this.crlData = new RevocationValuesData(); + } + + public void addCertificateData(byte[] d) + { + this.certificateValues.addData(d); + } + + public void addCRLData(byte[] d) + { + this.crlData.addData(d); + } + + public Collection getCertificateData() + { + return certificateValues.getData(); + } + + public Collection getCRLData() + { + return crlData.getData(); + } +} diff --git a/src/main/java/xades4j/properties/data/BaseValidationDataStructureVerifier.java b/src/main/java/xades4j/properties/data/BaseValidationDataStructureVerifier.java new file mode 100644 index 00000000..5d416e4b --- /dev/null +++ b/src/main/java/xades4j/properties/data/BaseValidationDataStructureVerifier.java @@ -0,0 +1,38 @@ +package xades4j.properties.data; + +import java.util.Collection; + +public class BaseValidationDataStructureVerifier implements + PropertyDataObjectStructureVerifier +{ + private final String propName; + + BaseValidationDataStructureVerifier(String propName) + { + this.propName = propName; + } + + @Override + public void verifyStructure(PropertyDataObject propData) + throws PropertyDataStructureException + { + BaseValidationDataData validationData = (BaseValidationDataData) propData; + + Collection certData = validationData.getCertificateData(); + Collection revocData = validationData.getCRLData(); + if ((certData == null || certData.isEmpty()) && (revocData == null || revocData.isEmpty())) + throw new PropertyDataStructureException( + "Neither certificate nor revocation data provided", propName); + + for (byte[] d : certData) + { + if (d == null) + throw new PropertyDataStructureException("null cert data", propName); + } + for (byte[] d : revocData) + { + if (d == null) + throw new PropertyDataStructureException("null revoc data", propName); + } + } +} diff --git a/src/main/java/xades4j/properties/data/CertificateValuesData.java b/src/main/java/xades4j/properties/data/CertificateValuesData.java index 86104ed2..a03f6ee0 100644 --- a/src/main/java/xades4j/properties/data/CertificateValuesData.java +++ b/src/main/java/xades4j/properties/data/CertificateValuesData.java @@ -16,10 +16,21 @@ */ package xades4j.properties.data; +import java.util.Collection; + /** * * @author Luís */ public final class CertificateValuesData extends BaseEncapsulatedPKIData { + public CertificateValuesData() + { + super(); + } + + public CertificateValuesData(Collection certificates) + { + super(certificates); + } } diff --git a/src/main/java/xades4j/properties/data/PropertiesDataObjectsStructureVerifier.java b/src/main/java/xades4j/properties/data/PropertiesDataObjectsStructureVerifier.java index 2316b8a0..c484240e 100644 --- a/src/main/java/xades4j/properties/data/PropertiesDataObjectsStructureVerifier.java +++ b/src/main/java/xades4j/properties/data/PropertiesDataObjectsStructureVerifier.java @@ -25,12 +25,15 @@ import java.util.Set; import xades4j.properties.AllDataObjsTimeStampProperty; import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; import xades4j.properties.CertificateValuesProperty; import xades4j.properties.CompleteCertificateRefsProperty; import xades4j.properties.RevocationValuesProperty; import xades4j.properties.SigAndRefsTimeStampProperty; import xades4j.properties.SignatureTimeStampProperty; import xades4j.properties.SigningCertificateProperty; +import xades4j.properties.TimeStampValidationDataProperty; /** * @@ -86,12 +89,21 @@ public class PropertiesDataObjectsStructureVerifier structureVerifiers.put(CertificateValuesData.class, new BaseEncapsulatedPKIDataStructureVerifier(CertificateValuesProperty.PROP_NAME)); + structureVerifiers.put(AttrAuthoritiesCertValuesData.class, + new BaseEncapsulatedPKIDataStructureVerifier(AttrAuthoritiesCertValuesProperty.PROP_NAME)); + structureVerifiers.put(RevocationValuesData.class, new BaseEncapsulatedPKIDataStructureVerifier(RevocationValuesProperty.PROP_NAME)); + structureVerifiers.put(AttributeRevocationValuesData.class, + new BaseEncapsulatedPKIDataStructureVerifier(AttributeRevocationValuesProperty.PROP_NAME)); + structureVerifiers.put(ArchiveTimeStampData.class, new BaseXAdESTimeStampDataStructureVerifier(ArchiveTimeStampProperty.PROP_NAME)); + structureVerifiers.put(TimeStampValidationDataData.class, + new BaseValidationDataStructureVerifier(TimeStampValidationDataProperty.PROP_NAME)); + structureVerifiers.put(GenericDOMData.class, new GenericDOMDataStructureVerifier()); } @@ -113,6 +125,12 @@ public void verifiyPropertiesDataStructure( verifiyPropertiesDataStructure(propsData.getDataObjProps()); } + /** + * Checks if internal structure and included parameters of the property are present + * and sane + * @param propsData properties to check + * @throws PropertyDataStructureException + */ public void verifiyPropertiesDataStructure( Collection propsData) throws PropertyDataStructureException { diff --git a/src/main/java/xades4j/properties/data/RevocationValuesData.java b/src/main/java/xades4j/properties/data/RevocationValuesData.java index a42a5da8..42fc0c28 100644 --- a/src/main/java/xades4j/properties/data/RevocationValuesData.java +++ b/src/main/java/xades4j/properties/data/RevocationValuesData.java @@ -16,10 +16,21 @@ */ package xades4j.properties.data; +import java.util.Collection; + /** * * @author Luís */ public class RevocationValuesData extends BaseEncapsulatedPKIData { + public RevocationValuesData() + { + super(); + } + + public RevocationValuesData(Collection revocationData) + { + super(revocationData); + } } diff --git a/src/main/java/xades4j/properties/data/TimeStampValidationDataData.java b/src/main/java/xades4j/properties/data/TimeStampValidationDataData.java new file mode 100644 index 00000000..af4bafda --- /dev/null +++ b/src/main/java/xades4j/properties/data/TimeStampValidationDataData.java @@ -0,0 +1,22 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.properties.data; + +public class TimeStampValidationDataData extends BaseValidationDataData +{ + +} diff --git a/src/main/java/xades4j/providers/CertificateValidationProvider.java b/src/main/java/xades4j/providers/CertificateValidationProvider.java index 9216ce38..7e2af78e 100644 --- a/src/main/java/xades4j/providers/CertificateValidationProvider.java +++ b/src/main/java/xades4j/providers/CertificateValidationProvider.java @@ -16,6 +16,7 @@ */ package xades4j.providers; +import java.security.cert.X509CRL; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.Collection; @@ -46,4 +47,33 @@ ValidationData validate( X509CertSelector certSelector, Date validationDate, Collection otherCerts) throws CertificateValidationException, UnexpectedJCAException; + + /** + * adds CRLs to certificate stores of this validation provider, later to be used to + * validate certificates + *

+ * CRLs are validated whatever they have been signed by a trusted CA using trustworthy + * algorithms at time {@code now}. + *

+ *

+ * invalid CRLs are ignored + *

+ * @param crls set of CRLs to add + * @param now date at which the CRL has to be valid (isn't published after this date, + * CA that issued this CRL links to a valid trust anchor and used algorithms have + * been safe at this point in time) + */ + void addCRLs(Collection crls, Date now); + + /** + * adds intermediate and end-entity certificates used for creating cert paths + *

+ * Certificates are validated whatever they have been issued by a valid trust anchor + * at time {@code now} and using algorithms that have been safe at this point in time + *

invalid Certificates are ignored, certificates are not validated + * using CRLs or OCSP + * @param otherCerts set of certificates to add + * @param now time of validation for added certificates + */ + void addCertificates(Collection otherCerts, Date now); } diff --git a/src/main/java/xades4j/providers/TSACertificateValidationProvider.java b/src/main/java/xades4j/providers/TSACertificateValidationProvider.java new file mode 100644 index 00000000..3de6988a --- /dev/null +++ b/src/main/java/xades4j/providers/TSACertificateValidationProvider.java @@ -0,0 +1,34 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario -QBS. + * + * 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 . + */ +package xades4j.providers; + +/** + * {@link CertificateValidationProvider} made specifically for validating certificates + * used by Time Stamping Authorities. + *

+ * verify() method should gracefully handle certificates with time stamping extended + * critical key usage extension. + *

+ * interface made specifically to differentiate verifiers used for signature + * verification and used for attribute verification (TimeStamps) + * @author Hubert Kario + * + */ +public interface TSACertificateValidationProvider extends + CertificateValidationProvider +{ +} diff --git a/src/main/java/xades4j/providers/TimeStampVerificationData.java b/src/main/java/xades4j/providers/TimeStampVerificationData.java new file mode 100644 index 00000000..b829794a --- /dev/null +++ b/src/main/java/xades4j/providers/TimeStampVerificationData.java @@ -0,0 +1,55 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.providers; + +import java.util.Date; + +/** + * Container for validation data and verification result of a time stamp. + *

+ * Contains both all certificate and CRLs used for verification of time stamp (in + * {@code ValidationData} object), as well as the time inside the token. + * + * @author Hubert Kario + * + */ +public class TimeStampVerificationData +{ + private final ValidationData validationData; + private final Date timeStampDate; + + public TimeStampVerificationData( + ValidationData validationData, + Date timeFromTimeStampToken) + { + if (validationData == null || timeFromTimeStampToken == null) + throw new NullPointerException("Neither ValidationData nor " + + "timeFromTimeStampToken parameters can be null"); + this.validationData = validationData; + this.timeStampDate = timeFromTimeStampToken; + } + + public ValidationData getValidationData() + { + return validationData; + } + + public Date getTimeStampTokenTime() + { + return timeStampDate; + } +} diff --git a/src/main/java/xades4j/providers/TimeStampVerificationProvider.java b/src/main/java/xades4j/providers/TimeStampVerificationProvider.java index 247e246b..9ddfe803 100644 --- a/src/main/java/xades4j/providers/TimeStampVerificationProvider.java +++ b/src/main/java/xades4j/providers/TimeStampVerificationProvider.java @@ -16,8 +16,8 @@ */ package xades4j.providers; -import java.util.Date; - +import xades4j.verification.QualifyingPropertyVerificationContext; + /** * Provides verification of time-stamp tokens. This is used whenever a time-stamp * property needs to be verified. @@ -33,7 +33,9 @@ public interface TimeStampVerificationProvider * @return the time-stamp * @throws TimeStampTokenVerificationException if the token cannot be validated (see subclasses of the exception) */ - public Date verifyToken( + public TimeStampVerificationData verifyToken( byte[] timeStampToken, - byte[] tsDigestInput) throws TimeStampTokenVerificationException; + byte[] tsDigestInput, + QualifyingPropertyVerificationContext ctx) + throws TimeStampTokenVerificationException; } diff --git a/src/main/java/xades4j/providers/ValidationData.java b/src/main/java/xades4j/providers/ValidationData.java index dad94b39..014df973 100644 --- a/src/main/java/xades4j/providers/ValidationData.java +++ b/src/main/java/xades4j/providers/ValidationData.java @@ -26,7 +26,8 @@ * Container of validation data (certificates and corresponding CRLs). *

* Contains the full certification chain, starting with the signing certificate - * and endind with the trust-anchor. + * and ending with the trust-anchor. Depending on verification profile, it may or may not + * contain CRLs used for checking the revocation information of certificates. * @author Luís */ public class ValidationData diff --git a/src/main/java/xades4j/providers/impl/DefaultTimeStampVerificationProvider.java b/src/main/java/xades4j/providers/impl/DefaultTimeStampVerificationProvider.java index 7294fd26..16a52554 100644 --- a/src/main/java/xades4j/providers/impl/DefaultTimeStampVerificationProvider.java +++ b/src/main/java/xades4j/providers/impl/DefaultTimeStampVerificationProvider.java @@ -44,15 +44,17 @@ import org.bouncycastle.util.Selector; import xades4j.UnsupportedAlgorithmException; import xades4j.XAdES4jException; -import xades4j.providers.CertificateValidationProvider; import xades4j.providers.MessageDigestEngineProvider; +import xades4j.providers.TSACertificateValidationProvider; import xades4j.providers.TimeStampTokenDigestException; import xades4j.providers.TimeStampTokenSignatureException; import xades4j.providers.TimeStampTokenStructureException; import xades4j.providers.TimeStampTokenTSACertException; import xades4j.providers.TimeStampTokenVerificationException; +import xades4j.providers.TimeStampVerificationData; import xades4j.providers.TimeStampVerificationProvider; import xades4j.providers.ValidationData; +import xades4j.verification.QualifyingPropertyVerificationContext; /** * Default implementation of {@code TimeStampVerificationProvider}. It verifies @@ -82,7 +84,7 @@ private static String uriForDigest(ASN1ObjectIdentifier digestalgOid) { return digestOidToUriMappings.get(digestalgOid); } - private final CertificateValidationProvider certificateValidationProvider; + private final TSACertificateValidationProvider tsaCertificateValidationProvider; private final MessageDigestEngineProvider messageDigestProvider; private final JcaSimpleSignerInfoVerifierBuilder signerInfoVerifierBuilder; private final JcaX509CertificateConverter x509CertificateConverter; @@ -90,10 +92,10 @@ private static String uriForDigest(ASN1ObjectIdentifier digestalgOid) @Inject public DefaultTimeStampVerificationProvider( - CertificateValidationProvider certificateValidationProvider, + TSACertificateValidationProvider certificateValidationProvider, MessageDigestEngineProvider messageDigestProvider) { - this.certificateValidationProvider = certificateValidationProvider; + this.tsaCertificateValidationProvider = certificateValidationProvider; this.messageDigestProvider = messageDigestProvider; Provider bcProv = new BouncyCastleProvider(); @@ -103,7 +105,9 @@ public DefaultTimeStampVerificationProvider( } @Override - public Date verifyToken(byte[] timeStampToken, byte[] tsDigestInput) throws TimeStampTokenVerificationException + public TimeStampVerificationData verifyToken(byte[] timeStampToken, byte[] tsDigestInput, + QualifyingPropertyVerificationContext ctx) + throws TimeStampTokenVerificationException { TimeStampToken tsToken; try @@ -121,6 +125,7 @@ public Date verifyToken(byte[] timeStampToken, byte[] tsDigestInput) throws Time } X509Certificate tsaCert = null; + ValidationData vData = null; try { /* Validate the TSA certificate */ @@ -129,10 +134,19 @@ public Date verifyToken(byte[] timeStampToken, byte[] tsDigestInput) throws Time { certs.add(this.x509CertificateConverter.getCertificate((X509CertificateHolder) certHolder)); } + if (ctx != null) + { + // add only certificates and CRLs that use currently secure algorithms + // and are signed by currently valid certificate authorities + tsaCertificateValidationProvider.addCertificates( + ctx.getAttributeCertificates(), ctx.getCurrentTime()); + tsaCertificateValidationProvider.addCRLs( + ctx.getAttributeCRLs(), ctx.getCurrentTime()); + } - ValidationData vData = this.certificateValidationProvider.validate( + vData = this.tsaCertificateValidationProvider.validate( x509CertSelectorConverter.getCertSelector(tsToken.getSID()), - tsToken.getTimeStampInfo().getGenTime(), + (ctx == null) ? null : ctx.getCurrentTime(), // unit tests provision certs); tsaCert = vData.getCerts().get(0); @@ -176,7 +190,12 @@ public Date verifyToken(byte[] timeStampToken, byte[] tsDigestInput) throws Time throw new TimeStampTokenVerificationException("The token's digest algorithm is not supported", ex); } - return tsTokenInfo.getGenTime(); + // token verified successfully, remember the validation data used for its + // verification + if (ctx != null) + ctx.addAttributeValidationData(vData); + + return new TimeStampVerificationData(vData, tsTokenInfo.getGenTime()); } /** Selector selecting all certificates. */ diff --git a/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProvider.java b/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProvider.java index be297e3a..c0596daf 100644 --- a/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProvider.java +++ b/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProvider.java @@ -1,6 +1,6 @@ /* * XAdES4j - A Java library for generation and verification of XAdES signatures. - * Copyright (C) 2010 Luis Goncalves. + * Copyright (C) 2012 Hubert Kario - QBS. * * 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 @@ -16,58 +16,29 @@ */ package xades4j.providers.impl; -import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.cert.CertPathBuilder; -import java.security.cert.CertPathBuilderException; import java.security.cert.CertStore; -import java.security.cert.CertStoreException; -import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; -import java.security.cert.PKIXCertPathBuilderResult; -import java.security.cert.X509CRL; -import java.security.cert.X509CRLSelector; -import java.security.cert.X509CertSelector; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.security.auth.x500.X500Principal; -import xades4j.providers.CannotBuildCertificationPathException; -import xades4j.providers.CannotSelectCertificateException; -import xades4j.providers.CertificateValidationException; + import xades4j.providers.CertificateValidationProvider; -import xades4j.providers.ValidationData; -import xades4j.verification.UnexpectedJCAException; /** - * Implementation of {@code CertificateValidationProvider} using a PKIX {@code CertPathBuilder}. + * Implementation of {@code CertificateValidationProvider} using a PKIX + * {@code CertPathBuilder}. *

* Since the Java's PKIX API doesn't allow to access the CRLs used in the certification * path validation, this is manually done. There has to be a CRL for each issuer * in the path which is valid at the moment of validation (signature and date). - * @author Luís + * @author Hubert Kario */ -public class PKIXCertificateValidationProvider implements CertificateValidationProvider +public class PKIXCertificateValidationProvider + extends PKIXCertificateValidationProviderBase + implements CertificateValidationProvider { private static final int DEFAULT_MAX_PATH_LENGTH = 6; - private final KeyStore trustAnchors; - private final boolean revocationEnabled; - private final int maxPathLength; - private final CertStore[] intermCertsAndCrls; - private final CertPathBuilder certPathBuilder; - private final String signatureProvider; - /** * Initializes a new instance that uses the specified JCE providers for CertPathBuilder * and Signature. @@ -88,19 +59,11 @@ public PKIXCertificateValidationProvider( int maxPathLength, String certPathBuilderProvider, String signatureProvider, - CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + CertStore[] intermCertsAndCrls) + throws NoSuchAlgorithmException, NoSuchProviderException { - if (null == trustAnchors) - { - throw new NullPointerException("Trust anchors cannot be null"); - } - - this.trustAnchors = trustAnchors; - this.revocationEnabled = revocationEnabled; - this.maxPathLength = maxPathLength; - this.certPathBuilder = certPathBuilderProvider == null ? CertPathBuilder.getInstance("PKIX") : CertPathBuilder.getInstance("PKIX", certPathBuilderProvider); - this.signatureProvider = signatureProvider; - this.intermCertsAndCrls = intermCertsAndCrls; + super(trustAnchors, revocationEnabled, maxPathLength, certPathBuilderProvider, + signatureProvider, intermCertsAndCrls); } /** @@ -121,7 +84,8 @@ public PKIXCertificateValidationProvider( boolean revocationEnabled, String certPathBuilderProvider, String signatureProvider, - CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + CertStore... intermCertsAndCrls) + throws NoSuchAlgorithmException, NoSuchProviderException { this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, certPathBuilderProvider, signatureProvider, intermCertsAndCrls); } @@ -211,141 +175,10 @@ public PKIXCertificateValidationProvider( } @Override - public ValidationData validate( - X509CertSelector certSelector, - Date validationDate, - Collection otherCerts) throws CertificateValidationException, UnexpectedJCAException + protected void addImplSpecificPKIXBuilderParams(PKIXBuilderParameters pkixbp) { - PKIXBuilderParameters builderParams; - try - { - builderParams = new PKIXBuilderParameters(trustAnchors, certSelector); - } catch (KeyStoreException ex) - { - throw new CannotBuildCertificationPathException(certSelector, "Trust anchors KeyStore is not initialized", ex); - } catch (InvalidAlgorithmParameterException ex) - { - throw new CannotBuildCertificationPathException(certSelector, "Trust anchors KeyStore has no trusted certificate entries", ex); - } - - PKIXCertPathBuilderResult builderRes; - try - { - // Certificates to be used to build the certification path. - // - The other certificates from the signature (e.g. from KeyInfo). - if (otherCerts != null) - { - CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(otherCerts); - CertStore othersCertStore = CertStore.getInstance("Collection", ccsp); - builderParams.addCertStore(othersCertStore); - } - // - The external certificates/CRLs. - for (int i = 0; i < intermCertsAndCrls.length; i++) - { - builderParams.addCertStore(intermCertsAndCrls[i]); - } - - builderParams.setRevocationEnabled(revocationEnabled); - builderParams.setMaxPathLength(maxPathLength); - builderParams.setDate(validationDate); - builderParams.setSigProvider(this.signatureProvider); - - builderRes = (PKIXCertPathBuilderResult) certPathBuilder.build(builderParams); - } - catch (CertPathBuilderException ex) - { - throw new CannotBuildCertificationPathException(certSelector, ex.getMessage(), ex); - } catch (InvalidAlgorithmParameterException ex) - { - // SHOULD NOT be thrown due to wrong type of parameters. - // Seems to be thrown when the CertSelector (in builderParams) criteria - // cannot be applied. - throw new CannotSelectCertificateException(certSelector, ex); - } catch (NoSuchAlgorithmException ex) - { - // SHOULD NOT be thrown. - throw new UnexpectedJCAException("No provider for Collection CertStore", ex); - } - - // The cert path returned by the builder ends in a certificate issued by - // the trust anchor. However, the complete path may be needed for property - // verification. - List certPath = (List) builderRes.getCertPath().getCertificates(); - // - Create a new list since the previous is immutable. - certPath = new ArrayList(certPath); - // - Add the trust anchor certificate. - certPath.add(builderRes.getTrustAnchor().getTrustedCert()); - - if (revocationEnabled) - { - return new ValidationData(certPath, getCRLsForCertPath(certPath, validationDate)); - } - return new ValidationData(certPath); - } - - private Collection getCRLsForCertPath( - List certPath, - Date validationDate) throws CertificateValidationException - { - // Map the issuers certificates in the chain. This is used to know the issuers - // and later to verify the signatures in the CRLs. - Map issuersCerts = new HashMap(certPath.size() - 1); - for (int i = 0; i < certPath.size() - 1; i++) - { - // The issuer of one certificate is the subject of the following one. - issuersCerts.put(certPath.get(i).getIssuerX500Principal(), certPath.get(i + 1)); - } - - // Select all the CRLs from the issuers involved in the certification path - // that are valid at the moment. - X509CRLSelector crlSelector = new X509CRLSelector(); - for (X500Principal issuer : issuersCerts.keySet()) - { - // - "The issuer distinguished name in the X509CRL must match at least - // one of the specified distinguished names." - crlSelector.addIssuer(issuer); - } - // - "The specified date must be equal to or later than the value of the - // thisUpdate component of the X509CRL and earlier than the value of the - // nextUpdate component." - crlSelector.setDateAndTime(validationDate); - - Set crls = new HashSet(); - try - { - // Get the CRLs on each CertStore. - for (int i = 0; i < intermCertsAndCrls.length; i++) - { - Collection storeCRLs = intermCertsAndCrls[i].getCRLs(crlSelector); - crls.addAll(Collections.checkedCollection(storeCRLs, X509CRL.class)); - - } - } catch (CertStoreException ex) - { - throw new CertificateValidationException(null, "Cannot get CRLs", ex); - } - - // Verify the CRLs' signatures. The issuers' certificates were validated - // as part of the cert path creation. - for (X509CRL crl : crls) - { - try - { - X509Certificate crlIssuerCert = issuersCerts.get(crl.getIssuerX500Principal()); - if (null == this.signatureProvider) - { - crl.verify(crlIssuerCert.getPublicKey()); - } - else - { - crl.verify(crlIssuerCert.getPublicKey(), this.signatureProvider); - } - } - catch (Exception ex) - { - throw new CertificateValidationException(null, "Invalid CRL signature from " + crl.getIssuerX500Principal().getName(), ex); - } - } - return crls; - } + // no implementation specific validators + return; + } + } diff --git a/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProviderBase.java b/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProviderBase.java new file mode 100644 index 00000000..767608a6 --- /dev/null +++ b/src/main/java/xades4j/providers/impl/PKIXCertificateValidationProviderBase.java @@ -0,0 +1,476 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2010 Luis Goncalves. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.providers.impl; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.cert.CRL; +import java.security.cert.CRLSelector; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.Certificate; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.X509CRL; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import xades4j.providers.CannotBuildCertificationPathException; +import xades4j.providers.CannotSelectCertificateException; +import xades4j.providers.CertificateValidationException; +import xades4j.providers.ValidationData; +import xades4j.verification.UnexpectedJCAException; + +/** + * + * @author Luís + * @author Hubert Kario + * + */ +public abstract class PKIXCertificateValidationProviderBase +{ + private final KeyStore trustAnchors; + private final boolean revocationEnabled; + private final int maxPathLength; + private CertStore[] intermCertsAndCrls; + private final CertPathBuilder certPathBuilder; + private final String signatureProvider; + + /** + * Initializes a new instance that uses the specified JCE providers for CertPathBuilder + * and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param maxPathLength the maximum length of the certification paths + * @param certPathBuilderProvider the CertPathBuilder provider + * @param signatureProvider the Signature provider + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXCertificateValidationProviderBase( + KeyStore trustAnchors, + boolean revocationEnabled, + int maxPathLength, + String certPathBuilderProvider, + String signatureProvider, + CertStore... intermCertsAndCrls) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (null == trustAnchors) + { + throw new NullPointerException("Trust anchors cannot be null"); + } + + this.trustAnchors = trustAnchors; + this.revocationEnabled = revocationEnabled; + this.maxPathLength = maxPathLength; + this.certPathBuilder = + certPathBuilderProvider == null ? + CertPathBuilder.getInstance("PKIX", "BC") : + CertPathBuilder.getInstance("PKIX", certPathBuilderProvider); + this.signatureProvider = signatureProvider; + this.intermCertsAndCrls = intermCertsAndCrls; + } + + public ValidationData validate( + X509CertSelector certSelector, + Date validationDate, + Collection otherCerts) + throws CertificateValidationException, UnexpectedJCAException + { + PKIXBuilderParameters builderParams; + try + { + builderParams = new PKIXBuilderParameters(trustAnchors, certSelector); + } catch (KeyStoreException ex) + { + throw new CannotBuildCertificationPathException(certSelector, + "Trust anchors KeyStore is not initialized", ex); + } catch (InvalidAlgorithmParameterException ex) + { + throw new CannotBuildCertificationPathException(certSelector, + "Trust anchors KeyStore has no trusted certificate entries", ex); + } + + PKIXCertPathBuilderResult builderRes; + try + { + // Certificates to be used to build the certification path. + // - The other certificates from the signature (e.g. from KeyInfo). + if (otherCerts != null) + { + CollectionCertStoreParameters ccsp = + new CollectionCertStoreParameters(otherCerts); + CertStore othersCertStore = CertStore.getInstance("Collection", ccsp); + builderParams.addCertStore(othersCertStore); + } + // - The external certificates/CRLs. + for (int i = 0; i < intermCertsAndCrls.length; i++) + { + builderParams.addCertStore(intermCertsAndCrls[i]); + } + + builderParams.setRevocationEnabled(revocationEnabled); + builderParams.setMaxPathLength(maxPathLength); + builderParams.setDate(validationDate); + builderParams.setSigProvider(this.signatureProvider); + addImplSpecificPKIXBuilderParams(builderParams); + + builderRes = (PKIXCertPathBuilderResult) certPathBuilder.build(builderParams); + } + catch (CertPathBuilderException ex) + { + throw new CannotBuildCertificationPathException(certSelector, + ex.getMessage(), ex); + } catch (InvalidAlgorithmParameterException ex) + { + // SHOULD NOT be thrown due to wrong type of parameters. + // Seems to be thrown when the CertSelector (in builderParams) criteria + // cannot be applied. + throw new CannotSelectCertificateException(certSelector, ex); + } catch (NoSuchAlgorithmException ex) + { + // SHOULD NOT be thrown. + throw new UnexpectedJCAException("No provider for Collection CertStore", ex); + } + + // The cert path returned by the builder ends in a certificate issued by + // the trust anchor. However, the complete path may be needed for property + // verification. + @SuppressWarnings("unchecked") + List certPath = + (List) builderRes.getCertPath().getCertificates(); + // - Create a new list since the previous is immutable. + certPath = new ArrayList(certPath); + // - Add the trust anchor certificate. + certPath.add(builderRes.getTrustAnchor().getTrustedCert()); + + if (revocationEnabled) + { + return new ValidationData(certPath, + getCRLsForCertPath(certPath, validationDate)); + } + return new ValidationData(certPath); + } + + private Collection getCRLsForCertPath( + List certPath, + Date validationDate) throws CertificateValidationException + { + // Map the issuers certificates in the chain. This is used to know the issuers + // and later to verify the signatures in the CRLs. + Map issuersCerts = + new HashMap(certPath.size() - 1); + for (int i = 0; i < certPath.size() - 1; i++) + { + // The issuer of one certificate is the subject of the following one. + issuersCerts.put(certPath.get(i).getIssuerX500Principal(), certPath.get(i + 1)); + } + + Set crls = new HashSet(); + try + { + for (int i = 0; i < certPath.size() - 1; i++) + { + X509Certificate cert = certPath.get(i); + + /* + * because the Sun X509CRLSelector is broken (won't find CRLs published + * in "future") we need to use our own or we won't be able to create + * C form from T form + */ + CRLSelector crlSelector = new CustomCRLSelector(cert, validationDate); + + // Get the CRLs on each CertStore. + for (int j = 0; j < intermCertsAndCrls.length; j++) + { + @SuppressWarnings("unchecked") + Collection storeCRLs = + (Collection) intermCertsAndCrls[j].getCRLs(crlSelector); + crls.addAll(Collections.checkedCollection(storeCRLs, X509CRL.class)); + } + } + } catch (CertStoreException ex) + { + throw new CertificateValidationException(null, "Cannot get CRLs", ex); + } + + // Verify the CRLs' signatures. The issuers' certificates were validated + // as part of the cert path creation. + for (X509CRL crl : crls) + { + try + { + X509Certificate crlIssuerCert = + issuersCerts.get(crl.getIssuerX500Principal()); + if (null == this.signatureProvider) + { + crl.verify(crlIssuerCert.getPublicKey()); + } + else + { + crl.verify(crlIssuerCert.getPublicKey(), this.signatureProvider); + } + } + catch (Exception ex) + { + throw new CertificateValidationException(null, + "Invalid CRL signature from " + + crl.getIssuerX500Principal().getName(), ex); + } + } + return crls; + } + + public void addCRLs(Collection crls, Date now) + { + if (crls == null) + return; + Collection validCRLs = new ArrayList(); + + for(X509CRL crl : crls) + { + // TODO check algorithms used in CRL + + X509CertSelector certSel = new X509CertSelector(); + certSel.setSubject(crl.getIssuerX500Principal()); + + PKIXBuilderParameters params; + try + { + params = new PKIXBuilderParameters(trustAnchors, certSel); + } catch (Exception e) + { + // parameters are invalid, ignore CRL + continue; + } + params.setDate(now); + params.setRevocationEnabled(false); + for (int i=0; i < intermCertsAndCrls.length; i++) + { + params.addCertStore(intermCertsAndCrls[i]); + } + addImplSpecificPKIXBuilderParams(params); + + PKIXCertPathBuilderResult res; + try + { + res = (PKIXCertPathBuilderResult) this.certPathBuilder.build(params); + } catch (Exception ex) + { + // CRL is invalid, ignore + continue; + } + + List certs = + new ArrayList(res.getCertPath().getCertificates()); + // TODO check algorithms used in this cert path + PublicKey crlSigningKey; + if (certs.size() != 0) + crlSigningKey = certs.get(0).getPublicKey(); + else + crlSigningKey = res.getTrustAnchor().getCAPublicKey(); + if (crlSigningKey == null) + crlSigningKey = res.getTrustAnchor().getTrustedCert().getPublicKey(); + + try + { + crl.verify(crlSigningKey); + } catch (Exception ex) + { + // invalid CRL, ignore + continue; + } + + validCRLs.add(crl); + } + + // don't create empty CertStores + if (validCRLs.size() == 0) + return; + + CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(validCRLs); + CertStore crlsCertStore; + try + { + crlsCertStore = CertStore.getInstance("Collection", ccsp); + } catch (Exception e) + { + throw new RuntimeException("General crypto failure", e); + } + + CertStore[] newIntermCertsAndCrls; + newIntermCertsAndCrls = Arrays.copyOf(intermCertsAndCrls, intermCertsAndCrls.length + 1); + newIntermCertsAndCrls[intermCertsAndCrls.length] = crlsCertStore; + + intermCertsAndCrls = newIntermCertsAndCrls; + } + + public void addCertificates(Collection otherCerts, Date now) + { + if (otherCerts == null) + return; + Collection validCerts = new ArrayList(); + + /* + * To validate certificates we need to have all issuer certificates. + * Needed issuer certificates can be among otherCerts. + * + * To work around this problem, we include otherCerts in PKIXBuilderParameters + * but add them to intermCertsAndCrls only if they validate successfully. + */ + CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(otherCerts); + CertStore otherCertsCertStore; + try + { + otherCertsCertStore = CertStore.getInstance("Collection", ccsp); + } catch (Exception ex) + { + throw new RuntimeException("General crypto failure", ex); + } + + // find good certificates + for (X509Certificate cert : otherCerts) + { + // TODO check algorithms used in certificate creation + + // TODO iff certificate matches entry in TSL, add them to trustAnchors + + X509CertSelector certSel = new X509CertSelector(); + certSel.setCertificate(cert); + + PKIXBuilderParameters params; + try { + params = new PKIXBuilderParameters(trustAnchors, certSel); + } catch (Exception e) + { + // parameters are invalid, ignore certificate + continue; + } + params.setDate(now); + params.setRevocationEnabled(false); + for (int i=0; i < intermCertsAndCrls.length; i++) + { + params.addCertStore(intermCertsAndCrls[i]); + } + params.addCertStore(otherCertsCertStore); + addImplSpecificPKIXBuilderParams(params); + + PKIXCertPathBuilderResult res; + try + { + res = (PKIXCertPathBuilderResult)this.certPathBuilder.build(params); + } catch (Exception ex) + { + // certificate or certificates invalid, ignore + continue; + } + + List certs = res.getCertPath().getCertificates(); + if (certs.size() == 0) // cert is a trustAnchor, we can ignore it + continue; + + validCerts.add(cert); + } + + ccsp = new CollectionCertStoreParameters(validCerts); + CertStore validCertCertStore; + try + { + validCertCertStore = CertStore.getInstance("Collection", ccsp); + } catch (Exception e) + { + throw new RuntimeException("General crypto failure", e); + } + + CertStore[] newIntermCertsAndCrls; + newIntermCertsAndCrls = Arrays.copyOf(intermCertsAndCrls, intermCertsAndCrls.length + 1); + newIntermCertsAndCrls[intermCertsAndCrls.length] = validCertCertStore; + + intermCertsAndCrls = newIntermCertsAndCrls; + } + + + private class CustomCRLSelector implements CRLSelector + { + private X509Certificate subjectCert; + private Date now; + + public CustomCRLSelector(X509Certificate subjectCertificate, Date checkingDate) + { + subjectCert = subjectCertificate; + now = checkingDate; + } + + public Object clone() + { + return new CustomCRLSelector(subjectCert, now); + } + + @Override + public boolean match(CRL crl) + { + if (!(crl instanceof X509CRL)) + return false; + X509CRL x509crl = (X509CRL) crl; + + // check if issuer of CRL is the same as the issuer of the certificate + X500Principal principal = x509crl.getIssuerX500Principal(); + if (!subjectCert.getIssuerX500Principal().equals(principal)) + return false; + + // CRL has to be valid for current time (but it can come from "future") + if (x509crl.getNextUpdate().getTime() < now.getTime()) + return false; + + // CRL must be published before certificate looses its validity + if (x509crl.getThisUpdate().getTime() >= subjectCert.getNotAfter().getTime()) + return false; + + return true; + } + } + + /** + * Add usage or implementation specific handlers to CertPathBuilderParameters + * @param pkixbp + */ + protected abstract void addImplSpecificPKIXBuilderParams(PKIXBuilderParameters pkixbp); +} diff --git a/src/main/java/xades4j/providers/impl/PKIXTSACertificateValidationProvider.java b/src/main/java/xades4j/providers/impl/PKIXTSACertificateValidationProvider.java new file mode 100644 index 00000000..2863ee81 --- /dev/null +++ b/src/main/java/xades4j/providers/impl/PKIXTSACertificateValidationProvider.java @@ -0,0 +1,243 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.providers.impl; + +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import xades4j.providers.TSACertificateValidationProvider; + +/** + * @author Luís + * @author Hubert Kario + */ +public class PKIXTSACertificateValidationProvider + extends PKIXCertificateValidationProviderBase + implements TSACertificateValidationProvider +{ + private static final int DEFAULT_MAX_PATH_LENGTH = 6; + + /** + * Initializes a new instance that uses the specified JCE providers for CertPathBuilder + * and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param maxPathLength the maximum length of the certification paths + * @param certPathBuilderProvider the CertPathBuilder provider + * @param signatureProvider the Signature provider + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider(KeyStore trustAnchors, + boolean revocationEnabled, int maxPathLength, + String certPathBuilderProvider, String signatureProvider, + CertStore[] intermCertsAndCrls) throws NoSuchAlgorithmException, + NoSuchProviderException + { + super(trustAnchors, revocationEnabled, maxPathLength, certPathBuilderProvider, + signatureProvider, intermCertsAndCrls); + } + + /** + * Initializes a new instance that uses the specified JCE providers for CertPathBuilder + * and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param certPathBuilderProvider the CertPathBuilder provider + * @param signatureProvider the Signature provider + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider( + KeyStore trustAnchors, + boolean revocationEnabled, + String certPathBuilderProvider, + String signatureProvider, + CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, certPathBuilderProvider, signatureProvider, intermCertsAndCrls); + } + + /** + * Initializes a new instance that uses the specified JCE provider for both + * CertPathBuilder and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param maxPathLength the maximum length of the certification paths + * @param jceProvider the CertPathBuilder and Signature provider + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider( + KeyStore trustAnchors, + boolean revocationEnabled, + int maxPathLength, + String jceProvider, + CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(trustAnchors, revocationEnabled, maxPathLength, jceProvider, jceProvider, intermCertsAndCrls); + } + + /** + * Initializes a new instance that uses the specified JCE provider for both + * CertPathBuilder and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param jceProvider the CertPathBuilder and Signature provider + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider( + KeyStore trustAnchors, + boolean revocationEnabled, + String jceProvider, + CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, jceProvider, intermCertsAndCrls); + } + + /** + * Initializes a new instance without specifying the JCE providers for CertPathBuilder + * and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param maxPathLength the maximum length of the certification paths + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider( + KeyStore trustAnchors, + boolean revocationEnabled, + int maxPathLength, + CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(trustAnchors, revocationEnabled, maxPathLength, null, null, intermCertsAndCrls); + } + + /** + * Initializes a new instance without specifying the JCE providers for CertPathBuilder + * and Signature. + * @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry}) + * @param revocationEnabled whether revocation is enabled + * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be + * used in the construction of the certification path. May contain CRLs to be used + * if revocation is enabled + * @see xades4j.utils.FileSystemDirectoryCertStore + * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder + */ + public PKIXTSACertificateValidationProvider( + KeyStore trustAnchors, + boolean revocationEnabled, + CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, null, null, intermCertsAndCrls); + } + + @Override + protected void addImplSpecificPKIXBuilderParams(PKIXBuilderParameters pkixbp) + { + pkixbp.addCertPathChecker(new TimeStampingPropertyChecker()); + } + + private class TimeStampingPropertyChecker extends PKIXCertPathChecker + { + private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; + private static final String TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8"; + + @Override + public void check(Certificate cert, + Collection unresolvedCritExts) + throws CertPathValidatorException + { + X509Certificate myCert = (X509Certificate) cert; + + // can't do anything meaningful + if (!unresolvedCritExts.contains(EXTENDED_KEY_USAGE_OID)) + return; + + List extendedKeyUses; + try + { + extendedKeyUses = myCert.getExtendedKeyUsage(); + } catch (CertificateParsingException e) + { + e.printStackTrace(); + throw new CertPathValidatorException("Can't parse certificate!"); + } + + for (int i = 0; i < extendedKeyUses.size(); i++) + { + if (extendedKeyUses.get(i).equals(TIMESTAMPING_OID)) + { + unresolvedCritExts.remove(EXTENDED_KEY_USAGE_OID); + return; + } + } + throw new CertPathValidatorException( + "Certificate does not allow for Time Stamping"); + } + + @Override + public Set getSupportedExtensions() + { + Set supportedExtensions = new HashSet(); + supportedExtensions.add(TIMESTAMPING_OID); + return supportedExtensions; + } + + @Override + public void init(boolean forward) throws CertPathValidatorException + { + if (forward) + throw new CertPathValidatorException( + "Forward Cert Path checking not supported!"); + } + + @Override + public boolean isForwardCheckingSupported() + { + return false; + } + } +} diff --git a/src/main/java/xades4j/utils/FileSystemDirectoryCertStore.java b/src/main/java/xades4j/utils/FileSystemDirectoryCertStore.java index 519ef249..b25d53ef 100644 --- a/src/main/java/xades4j/utils/FileSystemDirectoryCertStore.java +++ b/src/main/java/xades4j/utils/FileSystemDirectoryCertStore.java @@ -18,7 +18,8 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -162,16 +163,30 @@ private void transverseDirToFindContent( if (f.isDirectory()) transverseDirToFindContent(f, contentList, certsFilesExts, crlsFilesExts, cf); else if (f.isFile()) - try - { - if (hasExt(f, certsFilesExts)) - contentList.add((X509Certificate)cf.generateCertificate(new FileInputStream(f))); - else if (hasExt(f, crlsFilesExts)) - contentList.add((X509CRL)cf.generateCRL(new FileInputStream(f))); - } catch (FileNotFoundException ex) - { - // The file existed right up there! If somehow it doesn't exist - // now, nevermind. + if (hasExt(f, certsFilesExts)) { + try { + InputStream is = new FileInputStream(f); + try { + contentList.add((X509Certificate) cf.generateCertificate(is)); + } finally { + is.close(); + } + } catch (IOException e) { + // The file existed and somehow it doesn't exist now, or + // it was not possible to close is, nevermind. + } + } else if (hasExt(f, crlsFilesExts)) { + try { + InputStream is = new FileInputStream(f); + try { + contentList.add((X509CRL) cf.generateCRL(is)); + } finally { + is.close(); + } + } catch (IOException e) { + // The file existed and somehow it doesn't exist now, or + // it was not possible to close is, nevermind. + } } } } diff --git a/src/main/java/xades4j/utils/PropertiesList.java b/src/main/java/xades4j/utils/PropertiesList.java new file mode 100644 index 00000000..2fab1d96 --- /dev/null +++ b/src/main/java/xades4j/utils/PropertiesList.java @@ -0,0 +1,136 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import xades4j.properties.PropertyTargetException; + +/** + * A generic bag of properties used to store properties that apply to a specific target + * (data object descriptions, signature properties collector. + * + * @author Hubert Kario + */ +public class PropertiesList +{ + List properties; + Set> classes; + + /** + * Initializes the property bag with the given number of different initial property + * types. + *

+ * The order in which the properties are added is maintained. + * @param initialNPropTypes the initial number of different property types. + */ + public PropertiesList(int initialNPropTypes) + { + this.properties = new ArrayList(initialNPropTypes); + this.classes = new HashSet>(); + } + + /** + * Puts a property in the bag. The put operation doesn't allow repeated property + * types. If a property of this type was previously added an exception is + * thrown. + * + * @param prop the property + * + * @throws NullPointerException if {@code prop} is {@code null} + * @throws PropertyTargetException if the given property type is already present in + * the bag + */ + public void put(T prop) + { + if (prop == null) + throw new NullPointerException("Property cannot be null"); + + if(classes.contains(prop.getClass())) + throw new PropertyTargetException(String.format( + "A property of type %s was already added", + prop.getClass().getSimpleName())); + + classes.add(prop.getClass()); + properties.add(prop); + } + + /** + * Adds a property to the bag. The add operation allows multiple properties of the + * same type and repeated instances. + * + * @param prop the property + * @throws NullPointerExceptino if {@code prop} is {@code null} + */ + public void add(T prop) + { + if (prop == null) + throw new NullPointerException("Property cannot be null"); + + if (!classes.contains(prop.getClass())) + classes.add(prop.getClass()); + properties.add(prop); + } + + /** + * Removes a property from the bag. + * + * @param prop the property to be removed + * @throws NullPointerException if the property is {@code null} + * @throws IllegalStateException if the property is not present + */ + public void remove(T prop) + { + if (prop == null) + throw new NullPointerException("Property cannot be null"); + + if (!classes.remove(prop.getClass())) + throw new IllegalStateException("Property not present"); + if (!properties.remove(prop)) + throw new IllegalStateException("Property not present"); + } + + /** + * Indicates whatever the bag has any properties + * @return {@code true} if the bag has no properties + */ + public boolean isEmpty() + { + return properties.isEmpty(); + } + + /** + * Gets the properties in the bag + * @return unmodifiable list of properties + */ + public List getProperties() + { + if (properties.isEmpty()) + return Collections.emptyList(); + + return Collections.unmodifiableList(properties); + } + + public int size() + { + return properties.size(); + } +} diff --git a/src/main/java/xades4j/utils/PropertiesSet.java b/src/main/java/xades4j/utils/PropertiesSet.java index 29a1f278..f99ba821 100644 --- a/src/main/java/xades4j/utils/PropertiesSet.java +++ b/src/main/java/xades4j/utils/PropertiesSet.java @@ -18,10 +18,10 @@ import xades4j.properties.PropertyTargetException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -34,7 +34,7 @@ */ public class PropertiesSet { - private final Map> properties; + private final Map, Set> properties; /** * Initializes the property bag with the given initial diferent property types. @@ -42,7 +42,7 @@ public class PropertiesSet */ public PropertiesSet(int initialNPropTypes) { - this.properties = new HashMap>(initialNPropTypes); + this.properties = new HashMap, Set>(initialNPropTypes); } /** @@ -132,12 +132,12 @@ public boolean isEmpty() * Gets the properties in the bag. * @return un unmodifiable collection of properties */ - public Collection getProperties() + public List getProperties() { if (properties.isEmpty()) return Collections.emptyList(); - Collection props = new ArrayList(properties.size() + 3); + List props = new ArrayList(properties.size() + 3); for (Set propsOfType : properties.values()) { props.addAll(propsOfType); diff --git a/src/main/java/xades4j/utils/PropertiesUtils.java b/src/main/java/xades4j/utils/PropertiesUtils.java index c8184a83..e0a771e2 100644 --- a/src/main/java/xades4j/utils/PropertiesUtils.java +++ b/src/main/java/xades4j/utils/PropertiesUtils.java @@ -16,22 +16,32 @@ */ package xades4j.utils; +import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; + +import xades4j.properties.AllDataObjsTimeStampProperty; import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.properties.CertificateValuesProperty; import xades4j.properties.CompleteCertificateRefsProperty; import xades4j.properties.CompleteRevocationRefsProperty; +import xades4j.properties.IndividualDataObjsTimeStampProperty; import xades4j.properties.RevocationValuesProperty; import xades4j.properties.SigAndRefsTimeStampProperty; import xades4j.properties.SignaturePolicyBase; import xades4j.properties.SignatureTimeStampProperty; import xades4j.properties.SignedSignatureProperty; +import xades4j.properties.TimeStampValidationDataProperty; import xades4j.properties.UnsignedSignatureProperty; import xades4j.providers.SignaturePolicyInfoProvider; import xades4j.providers.ValidationData; +import xades4j.verification.XAdESVerificationResult; /** * @@ -39,6 +49,8 @@ */ public class PropertiesUtils { + private static final long ONE_WEEK = 7 * 24 * 60 * 60 * 1000; + private PropertiesUtils() { } @@ -79,10 +91,21 @@ public static void addXadesXProperties( public static void addXadesXLProperties( Collection usp, - ValidationData vData) + ValidationData vData, + Collection tstValData) { usp.add(new CertificateValuesProperty(vData.getCerts())); usp.add(new RevocationValuesProperty(vData.getCrls())); + + Collection allTSACerts = new HashSet(); + Collection allTSACRLs = new HashSet(); + for (ValidationData valData : tstValData) + { + allTSACerts.addAll(valData.getCerts()); + allTSACRLs.addAll(valData.getCrls()); + } + usp.add(new AttrAuthoritiesCertValuesProperty(allTSACerts)); + usp.add(new AttributeRevocationValuesProperty(allTSACRLs)); } public static void addXadesAProperties( @@ -90,4 +113,155 @@ public static void addXadesAProperties( { usp.add(new ArchiveTimeStampProperty()); } + + public static void addXadesAVDProperties( + Collection usp, + XAdESVerificationResult res) + { + /* + * The problem: the only CRLs that can contribute to validation of already + * time stamped time stamps (SigAndRefsTimeStamp when the last time stamp is + * AchiveTimeStamp) are ones that have been published after the time of last + * time stamp. See "grace period" in XAdES standard. + * + * The problem is multiplied by the fact, that we can have multiple + * ArchiveTimeStamps from different TSAs created in short succession. + * + * To detect which CRLs are published after the grace period, we assume that the + * sum of grace period time and the difference between time of creation of + * ArchiveTimeStamps is no greater than 1 week. + * We then add only CRLs that have been published a week after + */ + Collection allCertificates = new HashSet(); + Collection crlsAfterGracePeriod = new HashSet(); + + Collection validationTimeStamps = + new ArrayList( + res.getPropertiesFilter().getOfType(AllDataObjsTimeStampProperty.class)); + validationTimeStamps.addAll( + res.getPropertiesFilter().getOfType(IndividualDataObjsTimeStampProperty.class)); + validationTimeStamps.addAll( + res.getPropertiesFilter().getOfType(SignatureTimeStampProperty.class)); + validationTimeStamps.addAll( + res.getPropertiesFilter().getOfType(SigAndRefsTimeStampProperty.class)); + validationTimeStamps.addAll( + res.getPropertiesFilter().getOfType(ArchiveTimeStampProperty.class)); + + /* get validation data that may be useful to add to TimeStampValidationData */ + for (BaseXAdESTimeStampProperty ts : validationTimeStamps) + { + allCertificates.addAll(ts.getValidationData().getCerts()); + + for (X509CRL crl : ts.getValidationData().getCrls()) + { + // see big comment above + if (ts.getTime().getTime() + ONE_WEEK < crl.getThisUpdate().getTime()) + crlsAfterGracePeriod.add(crl); + } + } + + Collection savedCertificates = new ArrayList(); + Collection savedCRLs = new ArrayList(); + + // collect all CRLs already present in signature + Collection revValProps = + res.getPropertiesFilter().getOfType(RevocationValuesProperty.class); + Collection attrRevValProps = + res.getPropertiesFilter().getOfType(AttributeRevocationValuesProperty.class); + Collection tsValDatProps = + res.getPropertiesFilter().getOfType(TimeStampValidationDataProperty.class); + for (RevocationValuesProperty revValProp : revValProps) + { + savedCRLs.addAll(revValProp.getCrls()); + } + for (AttributeRevocationValuesProperty attrRevValProp : attrRevValProps) + { + savedCRLs.addAll(attrRevValProp.getCrls()); + } + for (TimeStampValidationDataProperty tsValDataProp : tsValDatProps) + { + savedCRLs.addAll(tsValDataProp.getCrls()); + savedCertificates.addAll(tsValDataProp.getCertificates()); + } + + // collect all certificates specified in properties (there can be certificates + // in time stamp tokens or basic XML signature KeyInfo, but duplication of them + // is not an error in itself) + Collection certValProps = + res.getPropertiesFilter().getOfType(CertificateValuesProperty.class); + Collection attrAuthCerValProps = + res.getPropertiesFilter().getOfType(AttrAuthoritiesCertValuesProperty.class); + for (CertificateValuesProperty cvProp : certValProps) + { + savedCertificates.addAll(cvProp.getCertificates()); + } + for (AttrAuthoritiesCertValuesProperty aacvProp : attrAuthCerValProps) + { + savedCertificates.addAll(aacvProp.getCertificates()); + } + + // remove already saved validation information + Collection certsToSave = + new ArrayList(allCertificates); + Collection crlsToSave = new ArrayList(crlsAfterGracePeriod); + certsToSave.removeAll(savedCertificates); + crlsToSave.removeAll(savedCRLs); + + // don't create empty property + if ((!certsToSave.isEmpty()) || (!crlsToSave.isEmpty())) + usp.add(new TimeStampValidationDataProperty(certsToSave, crlsToSave)); + } + + /** + * Walk over all properties with time stamps that can appear before X-L form + * creation and extract Validation Data from each and every one of them. + */ + public static Collection extractTTimeStampValidationData( + XAdESVerificationResult res) + { + Collection tTimeStampValidationData = + new ArrayList(); + + /* + * Collect validation data of properties previous to the last time stamp + * (SigAndRefsTimeStamp or RefsOnlyTimeStamp). + * + * This is done because the SigAndRefsTimeStamp must be valid using + * current revocation information, the previous time stamps may be invalid + * using currently available revocation information. In effect we save + * only the information that will not change in future. + * + * To reduce amount of information needed to add in the future, we add all + * certificates needed to verify the SigAndRefsTimeStamp too + */ + Collection allDataObjTSProps = + res.getPropertiesFilter().getOfType(AllDataObjsTimeStampProperty.class); + Collection indivDataObjTSProps = + res.getPropertiesFilter().getOfType(IndividualDataObjsTimeStampProperty.class); + Collection sigTSProps = + res.getPropertiesFilter().getOfType(SignatureTimeStampProperty.class); + Collection sigAndRefsProps = + res.getPropertiesFilter().getOfType(SigAndRefsTimeStampProperty.class); + + for (AllDataObjsTimeStampProperty tsProp : allDataObjTSProps) + { + tTimeStampValidationData.add(tsProp.getValidationData()); + } + for (IndividualDataObjsTimeStampProperty tsProp : indivDataObjTSProps) + { + tTimeStampValidationData.add(tsProp.getValidationData()); + } + for (SignatureTimeStampProperty tsProp : sigTSProps) + { + tTimeStampValidationData.add(tsProp.getValidationData()); + } + for (SigAndRefsTimeStampProperty tsProp : sigAndRefsProps) + { + ValidationData savedValData; + savedValData = tsProp.getValidationData(); + ValidationData newValData = new ValidationData(savedValData.getCerts()); + tTimeStampValidationData.add(newValData); + } + return tTimeStampValidationData; + } } diff --git a/src/main/java/xades4j/verification/AllDataObjsTimeStampVerifier.java b/src/main/java/xades4j/verification/AllDataObjsTimeStampVerifier.java index 1932efaa..582bd24d 100644 --- a/src/main/java/xades4j/verification/AllDataObjsTimeStampVerifier.java +++ b/src/main/java/xades4j/verification/AllDataObjsTimeStampVerifier.java @@ -18,7 +18,11 @@ import com.google.inject.Inject; import java.util.Collection; + +import org.w3c.dom.Element; + import xades4j.properties.AllDataObjsTimeStampProperty; +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.utils.CannotAddDataToDigestInputException; import xades4j.properties.QualifyingProperty; import xades4j.utils.TimeStampDigestInput; @@ -42,7 +46,12 @@ public AllDataObjsTimeStampVerifier( } @Override - protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty(AllDataObjsTimeStampData propData, TimeStampDigestInput digestInput, QualifyingPropertyVerificationContext ctx) throws CannotAddDataToDigestInputException + protected BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( + AllDataObjsTimeStampData propData, + Element location, + TimeStampDigestInput digestInput, + QualifyingPropertyVerificationContext ctx) + throws CannotAddDataToDigestInputException { Collection dataObjs = ctx.getSignedObjectsData().getAllDataObjects(); @@ -53,4 +62,11 @@ protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty(AllD return new AllDataObjsTimeStampProperty(); } + + @Override + protected void updateContextAfterVerification(QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx) + { + // noop + } } diff --git a/src/main/java/xades4j/verification/ArchiveTimeStampVerifier.java b/src/main/java/xades4j/verification/ArchiveTimeStampVerifier.java new file mode 100644 index 00000000..8e32526b --- /dev/null +++ b/src/main/java/xades4j/verification/ArchiveTimeStampVerifier.java @@ -0,0 +1,301 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.signature.ObjectContainer; +import org.apache.xml.security.signature.Reference; +import org.apache.xml.security.signature.SignedInfo; +import org.apache.xml.security.utils.Constants; +import org.w3c.dom.Element; + +import com.google.inject.Inject; + +import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; +import xades4j.properties.BaseXAdESTimeStampProperty; +import xades4j.properties.CertificateValuesProperty; +import xades4j.properties.CompleteCertificateRefsProperty; +import xades4j.properties.CompleteRevocationRefsProperty; +import xades4j.properties.CounterSignatureProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.RevocationValuesProperty; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.SignatureTimeStampProperty; +import xades4j.properties.TimeStampValidationDataProperty; +import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.providers.TimeStampVerificationProvider; +import xades4j.utils.CannotAddDataToDigestInputException; +import xades4j.utils.DOMHelper; +import xades4j.utils.TimeStampDigestInput; +import xades4j.utils.TimeStampDigestInputFactory; + +public class ArchiveTimeStampVerifier extends + TimeStampVerifierBase +{ + + @Inject + public ArchiveTimeStampVerifier( + TimeStampVerificationProvider timeStampVerifier, + TimeStampDigestInputFactory timeStampDigestInputFactory) + { + super(timeStampVerifier, timeStampDigestInputFactory, + ArchiveTimeStampProperty.PROP_NAME); + } + + @Override + protected BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( + ArchiveTimeStampData propData, Element location, + TimeStampDigestInput digestInput, + QualifyingPropertyVerificationContext ctx) + throws CannotAddDataToDigestInputException, + TimeStampVerificationException + { + // Archive time stamp is taken over: References, SignedInfor, SignatureValue, + // KeyInfo and all UnsignedSignatureProperties in order of appearance + + // References, processed accordingly to XML-DSIG. + SignedInfo signedInfo = ctx.getSignature().getSignedInfo(); + try + { + for (int i=0; i < signedInfo.getLength(); i++) + { + Reference ref = signedInfo.item(i); + digestInput.addReference(ref); + } + } catch (XMLSecurityException e) + { + throw new CannotAddDataToDigestInputException(e); + } + + // SignedInfo + Element e = ctx.getSignature().getSignedInfo().getElement(); + digestInput.addNode(e); + + // SignatureValue. + e = DOMHelper.getFirstDescendant( + ctx.getSignature().getElement(), + Constants.SignatureSpecNS, Constants._TAG_SIGNATUREVALUE); + digestInput.addNode(e); + + // KeyInfo, if present + KeyInfo ki = ctx.getSignature().getKeyInfo(); + if (ki != null) + digestInput.addNode(ki.getElement()); + + /* + * XAdES v1.4.2 standard, section G.2.2.16.2.4, implicit mechanism: " + * 1) Step 5) is performed as indicated. The verifier will take, + * among the unsigned properties that appear before the property that + * is being verified, those that appear in the following list, and in + * their order of appearance: SignatureTimeStamp, CounterSignature, + * CompleteCertificateRefs, CompleteRevocationRefs, + * AttributeCertificateRefs, AttributeRevocationRefs, + * CertificateValues, RevocationValues, SigAndRefsTimeStamp, + * RefsOnlyTimeStamp, xadesv141:ArchiveTimeStamp, + * xadesv141:TimeStampDataValidation and all the ds:Object elements + * different to the one containing the QualifyingProperties." + * + * section 8.2.1, item 4), requires the presence of CertificateValues and + * RevocationValues. If the signature contains other TimeStamps then + * AttrAuthoritiesCertValues and AttributeRevocationValues must be present, + * but only if Attribute Authorities don't share CRLs/OCSP or (sub)CAs with + * Signature. Because of that, we will only require presence of CertValues and + * RevocationValues. + */ + boolean certificateValuesPresent = false; + boolean revocationValuesPresent = false; + // TODO test with document extended from T to X-L form without C and X intermediates + + // requirements from SigAndRefsTimeStamp, only enforce singletonity, not presence + boolean completeCertRefsPresent = false; + boolean completeRevocRefsPresent = false; + + // all previous properties in order of appearance + for(Element elem = DOMHelper.getFirstChildElement(location.getParentNode()); + elem != location; + elem = DOMHelper.getNextSiblingElement(elem)) + { + if (isElementMatchingProperty(elem, + SignatureTimeStampProperty.class)) + { + // there are no specific requirements for SignatureTimeStamp + // so just add them + digestInput.addNode(elem); + // TODO check if there are no SignatureTimestamps *after* + // SigAndRefsTimeStamp + } else if (isElementMatchingProperty(elem, + CounterSignatureProperty.class)) + { + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + CompleteCertificateRefsProperty.class)) + { + // there must be exactly one CompleteCertificateRefs element + if (completeCertRefsPresent) + throw new CannotAddDataToDigestInputException( + new Exception( + "Duplicate CompleteCertificateRefs property" + + " in Signature")); + completeCertRefsPresent = true; + digestInput.addNode(elem); + // TODO check if there are no CompleteCertificateRefs + // *after* SigAndRefsTimeStamp + } else if (isElementMatchingProperty(elem, + CompleteRevocationRefsProperty.class)) + { + // there must be exactly one CompleteRevocationRefs element + if (completeRevocRefsPresent) + throw new CannotAddDataToDigestInputException( + new Exception( + "Duplicate CompleteRevocationRefs property" + + " in Singature")); + completeRevocRefsPresent = true; + digestInput.addNode(elem); + // TODO check if there are no CompleteRevocationRefs *after* + // SigAndRefsTimeStamp + } else if (elem.getLocalName().equalsIgnoreCase( + "AttributeCertificateRefs") + && elem.getNamespaceURI().equalsIgnoreCase( + QualifyingProperty.XADES_XMLNS)) + { + // TODO implement AttributeCertificateRefs support + throw new CannotAddDataToDigestInputException( + new Exception("Can't verify SigAndRefsTimeStamp: " + + "AttributeCertificateRefs is unsupported")); + } else if (elem.getLocalName().equalsIgnoreCase( + "AttributeRevocationRefs") + && elem.getNamespaceURI().equalsIgnoreCase( + QualifyingProperty.XADES_XMLNS)) + { + // TODO implement AttributeRevocationRefs support + throw new CannotAddDataToDigestInputException( + new Exception("Can't verify SigAndRefsTimeStamp: " + + "AttributeRevocationRefs is unsupported")); + } else if (isElementMatchingProperty(elem, + CertificateValuesProperty.class)) + { + if (certificateValuesPresent) + throw new CannotAddDataToDigestInputException(new Exception( + "Duplicate CertificateValues property in Signature")); + + certificateValuesPresent = true; + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + RevocationValuesProperty.class)) + { + if (revocationValuesPresent) + throw new CannotAddDataToDigestInputException(new Exception( + "Duplicate RevocationValues property in Signature")); + revocationValuesPresent = true; + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + SigAndRefsTimeStampProperty.class)) + { + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + AttrAuthoritiesCertValuesProperty.class)) + { + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + AttributeRevocationValuesProperty.class)) + { + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + ArchiveTimeStampProperty.class)) + { + digestInput.addNode(elem); + } else if (isElementMatchingProperty(elem, + TimeStampValidationDataProperty.class)) + { + digestInput.addNode(elem); + } else { + // TS 101 903 V1.4.1 defined new unsigned properties making use + // of the extension mechanism specified in + // xades:UnsignedSignatureProperties, namely the element. + digestInput.addNode(elem); + } + } + + /* take "all the ds:Object elements different to the one + * containing the QualifyingProperties." */ + for (int i = 0; i < ctx.getSignature().getObjectLength(); i++) + { + ObjectContainer obj = ctx.getSignature().getObjectItem(i); + if (null == DOMHelper.getFirstDescendant(obj.getElement(), QualifyingProperty.XADES_XMLNS, "*")) + digestInput.addNode(obj.getElement()); + } + + if (certificateValuesPresent && revocationValuesPresent) + return new ArchiveTimeStampProperty(); + else + throw new CannotAddDataToDigestInputException(new Exception( + "Missing mandatory properties: CertificateValues" + + " or RevocationValues")); + } + + + private boolean isElementMatchingProperty(Element elem, + Class prop) + { + try + { + if (prop.equals(ArchiveTimeStampProperty.class) || + prop.equals(TimeStampValidationDataProperty.class)) + return elem.getLocalName().equalsIgnoreCase( + (String) prop.getField("PROP_NAME").get(null)) + && elem.getNamespaceURI().equalsIgnoreCase( + (String) prop.getField("XADESV141_XMLNS").get(null)); + else + return elem.getLocalName().equalsIgnoreCase( + (String) prop.getField("PROP_NAME").get(null)) + && elem.getNamespaceURI().equalsIgnoreCase( + (String) prop.getField("XADES_XMLNS").get(null)); + } catch (IllegalArgumentException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (SecurityException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (IllegalAccessException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (NoSuchFieldException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } + } + + @Override + protected void updateContextAfterVerification(QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx) + { + if (!(prop instanceof ArchiveTimeStampProperty)) + throw new RuntimeException("Can't update time after verification" + + " of ArchiveTimeStamp"); + + ArchiveTimeStampProperty archivTS = (ArchiveTimeStampProperty) prop; + ctx.setCurrentTime(archivTS.getTime()); + } +} diff --git a/src/main/java/xades4j/verification/AttrAuthoritiesCertValuesVerifier.java b/src/main/java/xades4j/verification/AttrAuthoritiesCertValuesVerifier.java new file mode 100644 index 00000000..d2eb5c92 --- /dev/null +++ b/src/main/java/xades4j/verification/AttrAuthoritiesCertValuesVerifier.java @@ -0,0 +1,53 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.security.cert.X509Certificate; +import java.util.Collection; + +import com.google.inject.Inject; + +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; + +/** + * + * @author Hubert Kario + * + */ +public class AttrAuthoritiesCertValuesVerifier extends EncapsulatedPKIDataVerifierBase +{ + @Inject + public AttrAuthoritiesCertValuesVerifier(String propName) + { + super(propName); + } + + @Override + public QualifyingProperty createProperty(Collection certs) + { + return new AttrAuthoritiesCertValuesProperty(certs); + } + + public void addCertificatesToValidationProvider( + Collection certificates, + QualifyingPropertyVerificationContext ctx) + { + ctx.addAttributeCertificates(certificates); + } +} diff --git a/src/main/java/xades4j/verification/AttributeRevocationValuesVerifier.java b/src/main/java/xades4j/verification/AttributeRevocationValuesVerifier.java new file mode 100644 index 00000000..60c3c144 --- /dev/null +++ b/src/main/java/xades4j/verification/AttributeRevocationValuesVerifier.java @@ -0,0 +1,51 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.security.cert.X509CRL; +import java.util.Collection; + +import com.google.inject.Inject; + +import xades4j.properties.AttributeRevocationValuesProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.providers.TSACertificateValidationProvider; + +public class AttributeRevocationValuesVerifier + extends EncapsulatedPKIRevocationDataVerifierBase +{ + @Inject + public AttributeRevocationValuesVerifier(String propName) + { + super(propName); + } + + @Override + public QualifyingProperty createProperty(Collection crls) + { + return new AttributeRevocationValuesProperty(crls); + } + + @Override + public void addCRLsToValidationProvider( + Collection crls, + QualifyingPropertyVerificationContext ctx) + { + ctx.addAttributeCRLs(crls); + } +} diff --git a/src/main/java/xades4j/verification/CertificateValuesVerifier.java b/src/main/java/xades4j/verification/CertificateValuesVerifier.java new file mode 100644 index 00000000..67d1ec1c --- /dev/null +++ b/src/main/java/xades4j/verification/CertificateValuesVerifier.java @@ -0,0 +1,58 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.security.cert.X509Certificate; +import java.util.Collection; + +import org.w3c.dom.Element; + +import com.google.inject.Inject; + +import xades4j.properties.CertificateValuesProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.data.CertificateValuesData; +import xades4j.providers.CertificateValidationProvider; +import xades4j.providers.TSACertificateValidationProvider; + +/** + * + * @author Hubert Kario + * + */ +public class CertificateValuesVerifier extends EncapsulatedPKIDataVerifierBase +{ + @Inject + public CertificateValuesVerifier(String propName) + { + super(propName); + } + + @Override + public QualifyingProperty createProperty(Collection certs) + { + return new CertificateValuesProperty(certs); + } + + @Override + public void addCertificatesToValidationProvider( + Collection certificates, + QualifyingPropertyVerificationContext ctx) + { + ctx.addSignatureCertificates(certificates); + } +} diff --git a/src/main/java/xades4j/verification/CommitmentTypeVerifier.java b/src/main/java/xades4j/verification/CommitmentTypeVerifier.java index 9137326b..8253b464 100644 --- a/src/main/java/xades4j/verification/CommitmentTypeVerifier.java +++ b/src/main/java/xades4j/verification/CommitmentTypeVerifier.java @@ -45,7 +45,7 @@ public QualifyingProperty verify( { // "Check that all the ObjectReference elements actually reference // ds:Reference elements from the signature." - + SignedObjectsData signedObjsData = ctx.getSignedObjectsData(); CommitmentTypeProperty commitmentTypeProperty = new CommitmentTypeProperty(uri, desc); @@ -82,4 +82,12 @@ else if (q instanceof Element) return property; } + + @Override + public QualifyingProperty verify(CommitmentTypeData propData, Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/main/java/xades4j/verification/CompleteCertRefsVerifier.java b/src/main/java/xades4j/verification/CompleteCertRefsVerifier.java index 96922f4d..9e4556a6 100644 --- a/src/main/java/xades4j/verification/CompleteCertRefsVerifier.java +++ b/src/main/java/xades4j/verification/CompleteCertRefsVerifier.java @@ -17,14 +17,19 @@ package xades4j.verification; import com.google.inject.Inject; + import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Collections; import java.util.List; + +import org.w3c.dom.Element; + import xades4j.properties.CompleteCertificateRefsProperty; import xades4j.properties.QualifyingProperty; import xades4j.properties.data.CertRef; import xades4j.properties.data.CompleteCertificateRefsData; +import xades4j.providers.CertificateValidationProvider; import xades4j.providers.MessageDigestEngineProvider; /** @@ -37,7 +42,8 @@ class CompleteCertRefsVerifier implements QualifyingPropertyVerifier unkownElemsBinder = MapBinder.newMapBinder(binder(), QName.class, QualifyingPropertyVerifier.class); unkownElemsBinder .addBinding(new QName(QualifyingProperty.XADES_XMLNS, CounterSignatureProperty.PROP_NAME)) diff --git a/src/main/java/xades4j/verification/EncapsulatedPKIDataVerificationException.java b/src/main/java/xades4j/verification/EncapsulatedPKIDataVerificationException.java new file mode 100644 index 00000000..601cb3bc --- /dev/null +++ b/src/main/java/xades4j/verification/EncapsulatedPKIDataVerificationException.java @@ -0,0 +1,44 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.security.cert.CertificateException; + +public class EncapsulatedPKIDataVerificationException extends InvalidPropertyException +{ + private static final long serialVersionUID = 1L; + private final String propName; + + public EncapsulatedPKIDataVerificationException(CertificateException ex, String propName) + { + super(ex); + this.propName = propName; + } + + @Override + protected String getVerificationMessage() + { + return "Verification failure, can't parse certificates"; + } + + @Override + public String getPropertyName() + { + return propName; + } + +} diff --git a/src/main/java/xades4j/verification/EncapsulatedPKIDataVerifierBase.java b/src/main/java/xades4j/verification/EncapsulatedPKIDataVerifierBase.java new file mode 100644 index 00000000..4eaaf151 --- /dev/null +++ b/src/main/java/xades4j/verification/EncapsulatedPKIDataVerifierBase.java @@ -0,0 +1,88 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Element; + +import xades4j.properties.QualifyingProperty; +import xades4j.properties.data.BaseEncapsulatedPKIData; + +public abstract class EncapsulatedPKIDataVerifierBase + implements QualifyingPropertyVerifier +{ + private final String propName; + + public EncapsulatedPKIDataVerifierBase(String propName) + { + this.propName = propName; + } + + public QualifyingProperty verify(BaseEncapsulatedPKIData propData, + Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } + + public QualifyingProperty verify(BaseEncapsulatedPKIData propData, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + Collection rawCerts = propData.getData(); + CertificateFactory certFactory; + try + { + certFactory = CertificateFactory.getInstance("X509"); + } catch (CertificateException ex) + { + throw new EncapsulatedPKIDataVerificationException(ex, propName); + } + + List certificates = new ArrayList(); + + for (byte[] cert : rawCerts) + { + InputStream inStream = new ByteArrayInputStream(cert); + try + { + certificates.add((X509Certificate) certFactory.generateCertificate(inStream)); + } catch (CertificateException ex) + { + throw new EncapsulatedPKIDataVerificationException(ex, propName); + } + } + + addCertificatesToValidationProvider(certificates, ctx); + + return createProperty(certificates); + } + + public abstract QualifyingProperty createProperty(Collection certs); + + public abstract void addCertificatesToValidationProvider(Collection certificates, + QualifyingPropertyVerificationContext ctx); +} diff --git a/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerificationException.java b/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerificationException.java new file mode 100644 index 00000000..c20a69cd --- /dev/null +++ b/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerificationException.java @@ -0,0 +1,43 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +public class EncapsulatedPKIRevocationDataVerificationException extends + InvalidPropertyException +{ + private static final long serialVersionUID = 1L; + private final String propName; + + public EncapsulatedPKIRevocationDataVerificationException(Exception ex, String propName) + { + super(ex); + this.propName = propName; + } + + @Override + protected String getVerificationMessage() + { + return "Can't verify revocation information"; + } + + @Override + public String getPropertyName() + { + return propName; + } + +} diff --git a/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerifierBase.java b/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerifierBase.java new file mode 100644 index 00000000..b03470d5 --- /dev/null +++ b/src/main/java/xades4j/verification/EncapsulatedPKIRevocationDataVerifierBase.java @@ -0,0 +1,90 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509CRL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Element; + +import xades4j.properties.QualifyingProperty; +import xades4j.properties.data.BaseEncapsulatedPKIData; + +public abstract class EncapsulatedPKIRevocationDataVerifierBase + implements QualifyingPropertyVerifier +{ + private final String propName; + + public EncapsulatedPKIRevocationDataVerifierBase(String propName) + { + this.propName = propName; + } + + public QualifyingProperty verify(BaseEncapsulatedPKIData propData, + Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } + + public QualifyingProperty verify(BaseEncapsulatedPKIData propData, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + Collection rawRevocationData = propData.getData(); + CertificateFactory certFactory; + try + { + certFactory = CertificateFactory.getInstance("X509"); + } catch (CertificateException ex) + { + throw new EncapsulatedPKIRevocationDataVerificationException(ex, propName); + } + + List crls = new ArrayList(); + + for (byte[] crl : rawRevocationData) + { + InputStream inStream = new ByteArrayInputStream(crl); + try + { + crls.add((X509CRL) certFactory.generateCRL(inStream)); + } catch (CRLException ex) + { + throw new EncapsulatedPKIRevocationDataVerificationException(ex, propName); + } + } + + addCRLsToValidationProvider(crls, ctx); + + return createProperty(crls); + } + + public abstract QualifyingProperty createProperty(Collection crls); + + public abstract void addCRLsToValidationProvider( + Collection crls, + QualifyingPropertyVerificationContext ctx); +} diff --git a/src/main/java/xades4j/verification/GenericDOMDataVerifier.java b/src/main/java/xades4j/verification/GenericDOMDataVerifier.java index efe63e3d..3f73b8d6 100644 --- a/src/main/java/xades4j/verification/GenericDOMDataVerifier.java +++ b/src/main/java/xades4j/verification/GenericDOMDataVerifier.java @@ -64,4 +64,12 @@ public String getPropertyName() return propVerifier.verify(propData, ctx); } + + @Override + public QualifyingProperty verify(GenericDOMData propData, Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/main/java/xades4j/verification/HybridQualifPropsDataCollectorImpl.java b/src/main/java/xades4j/verification/HybridQualifPropsDataCollectorImpl.java new file mode 100644 index 00000000..cd2911f3 --- /dev/null +++ b/src/main/java/xades4j/verification/HybridQualifPropsDataCollectorImpl.java @@ -0,0 +1,265 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import xades4j.properties.data.AllDataObjsTimeStampData; +import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.CertificateValuesData; +import xades4j.properties.data.CommitmentTypeData; +import xades4j.properties.data.CompleteCertificateRefsData; +import xades4j.properties.data.CompleteRevocationRefsData; +import xades4j.properties.data.DataObjectFormatData; +import xades4j.properties.data.GenericDOMData; +import xades4j.properties.data.IndividualDataObjsTimeStampData; +import xades4j.properties.data.OtherPropertyData; +import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.RevocationValuesData; +import xades4j.properties.data.SigAndRefsTimeStampData; +import xades4j.properties.data.SignaturePolicyData; +import xades4j.properties.data.SignatureProdPlaceData; +import xades4j.properties.data.SignatureTimeStampData; +import xades4j.properties.data.SignerRoleData; +import xades4j.properties.data.SigningCertificateData; +import xades4j.properties.data.SigningTimeData; +import xades4j.properties.data.TimeStampValidationDataData; +import xades4j.utils.PropertiesList; +import xades4j.utils.PropertiesSet; +import xades4j.xml.unmarshalling.QualifyingPropertiesDataCollector; + +public class HybridQualifPropsDataCollectorImpl implements + QualifyingPropertiesDataCollector +{ + private final PropertiesSet signedSigProperties; + private final PropertiesSet signedDataObjectProperties; + private final PropertiesList unsignedSigProperties; + private final PropertiesList unsignedDataObjectProperties; + private final List unsignedSigPropertiesElements; + + public HybridQualifPropsDataCollectorImpl() + { + signedSigProperties = new PropertiesSet(1); + signedDataObjectProperties = new PropertiesSet(1); + unsignedSigProperties = new PropertiesList(1); + unsignedDataObjectProperties = new PropertiesList(1); + unsignedSigPropertiesElements = new ArrayList(); + } + + @Override + public void setSigningTime(SigningTimeData sigTimeData) + { + signedSigProperties.put(sigTimeData); + } + + @Override + public void setSignatureProdPlace(SignatureProdPlaceData sigProdPlaceData) + { + signedSigProperties.put(sigProdPlaceData); + } + + @Override + public void setSignerRole(SignerRoleData signerRoleData) + { + signedSigProperties.put(signerRoleData); + } + + @Override + public void setSigningCertificate(SigningCertificateData signingCertData) + { + signedSigProperties.put(signingCertData); + } + + @Override + public void setSignaturePolicy(SignaturePolicyData sigPolicyData) + { + signedSigProperties.put(sigPolicyData); + } + + @Override + public void setCompleteCertificateRefs( + CompleteCertificateRefsData completeCertRefsData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(completeCertRefsData); + } + + @Override + public void setCompleteRevocRefs( + CompleteRevocationRefsData completeRecovRefsData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(completeRecovRefsData); + } + + @Override + public void addSignatureTimeStamp(SignatureTimeStampData sigTSData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.add(sigTSData); + } + + @Override + public void addCommitmentType(CommitmentTypeData commitmentData) + { + signedDataObjectProperties.add(commitmentData); + } + + @Override + public void addDataObjectFormat(DataObjectFormatData formatData) + { + signedDataObjectProperties.add(formatData); + } + + @Override + public void addAllDataObjsTimeStamp(AllDataObjsTimeStampData objsTSData) + { + signedDataObjectProperties.add(objsTSData); + } + + @Override + public void addIndividualDataObjsTimeStamp( + IndividualDataObjsTimeStampData objsTSData) + { + signedDataObjectProperties.add(objsTSData); + } + + @Override + public void addGenericDOMData(GenericDOMData domData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.add(domData); + } + + @Override + public void addOther(OtherPropertyData otherData) + { + unsignedDataObjectProperties.add(otherData); + } + + @Override + public void addSigAndRefsTimeStamp(SigAndRefsTimeStampData tsData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.add(tsData); + } + + @Override + public void setCertificateValues(CertificateValuesData certificateValuesData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(certificateValuesData); + } + + @Override + public void setRevocationValues(RevocationValuesData revocationValuesData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(revocationValuesData); + } + + @Override + public void setAttrAuthoritiesCertValues( + AttrAuthoritiesCertValuesData attrAuthoritiesCertValuesData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(attrAuthoritiesCertValuesData); + } + + @Override + public void setAttributeRevocationValues( + AttributeRevocationValuesData attrRevocValData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.put(attrRevocValData); + } + + @Override + public void addArchiveTimeStamp(ArchiveTimeStampData tsData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property!"); + + unsignedSigProperties.add(tsData); + } + + @Override + public void addTimeStampValidationDataData( + TimeStampValidationDataData timeStampValidationDataData) + { + if (unsignedSigProperties.size() != unsignedSigPropertiesElements.size()) + throw new IllegalStateException("No Element linked to previous property"); + + unsignedSigProperties.add(timeStampValidationDataData); + } + + List getUnsignedPropertiesData() + { + return unsignedSigProperties.getProperties(); + } + + @Override + public List getPropertiesData() + { + List ret = new ArrayList(); + ret.addAll(signedSigProperties.getProperties()); + ret.addAll(signedDataObjectProperties.getProperties()); + ret.addAll(unsignedSigProperties.getProperties()); + ret.addAll(unsignedDataObjectProperties.getProperties()); + return ret; + } + + @Override + public void linkPropertyToElem(Element node) + { + unsignedSigPropertiesElements.add(node); + } + + @Override + public Node getPropertyNode(PropertyDataObject pdo) + { + int index = unsignedSigProperties.getProperties().indexOf(pdo); + if (index == -1) + return null; + + return unsignedSigPropertiesElements.get(index); + } +} diff --git a/src/main/java/xades4j/verification/HybridQualifyingPropertiesVerifierImpl.java b/src/main/java/xades4j/verification/HybridQualifyingPropertiesVerifierImpl.java new file mode 100644 index 00000000..04c19208 --- /dev/null +++ b/src/main/java/xades4j/verification/HybridQualifyingPropertiesVerifierImpl.java @@ -0,0 +1,344 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +import org.w3c.dom.Element; + +import com.google.inject.Inject; + +import xades4j.XAdES4jException; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.SignatureTimeStampProperty; +import xades4j.properties.data.AllDataObjsTimeStampData; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.CertificateValuesData; +import xades4j.properties.data.CommitmentTypeData; +import xades4j.properties.data.CompleteCertificateRefsData; +import xades4j.properties.data.CompleteRevocationRefsData; +import xades4j.properties.data.DataObjectFormatData; +import xades4j.properties.data.IndividualDataObjsTimeStampData; +import xades4j.properties.data.PropertiesDataObjectsStructureVerifier; +import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.PropertyDataStructureException; +import xades4j.properties.data.RevocationValuesData; +import xades4j.properties.data.SignaturePolicyData; +import xades4j.properties.data.SignatureProdPlaceData; +import xades4j.properties.data.SignatureTimeStampData; +import xades4j.properties.data.SignerRoleData; +import xades4j.properties.data.SigningCertificateData; +import xades4j.properties.data.SigningTimeData; +import xades4j.properties.data.TimeStampValidationDataData; +import xades4j.xml.unmarshalling.QualifyingPropertiesDataCollector; + +public class HybridQualifyingPropertiesVerifierImpl implements + QualifyingPropertiesVerifier +{ + private final QualifyingPropertyVerifiersMapper propertyVerifiersMapper; + private final PropertiesDataObjectsStructureVerifier dataObjectsStructureVerifier; + private final Set> sigAndRefsPropertySignedProperties; + private final Set> signedProperties; + + @Inject + public HybridQualifyingPropertiesVerifierImpl( + QualifyingPropertyVerifiersMapper propVerMapp, + PropertiesDataObjectsStructureVerifier dataObjStructVerif) + { + propertyVerifiersMapper = propVerMapp; + dataObjectsStructureVerifier = dataObjStructVerif; + Set> tmp = new HashSet>(); + // TODO tmp.add(AttributeRevocationRefsData.class); + // TODO tmp.add(AttributeCertificateRefsData.class); + tmp.add(CompleteRevocationRefsData.class); + tmp.add(CompleteCertificateRefsData.class); + tmp.add(SignatureTimeStampData.class); + sigAndRefsPropertySignedProperties = tmp; + + tmp = new HashSet>(); + tmp.add(IndividualDataObjsTimeStampData.class); + tmp.add(AllDataObjsTimeStampData.class); + tmp.add(CommitmentTypeData.class); + tmp.add(DataObjectFormatData.class); + tmp.add(SignerRoleData.class); + tmp.add(SignatureProdPlaceData.class); + tmp.add(SignaturePolicyData.class); + tmp.add(SigningCertificateData.class); + tmp.add(SigningTimeData.class); + signedProperties = tmp; + } + + /* + * new invocation method (one that knows about XML structure of properties) + */ + @Override + public List verifyProperties( + QualifyingPropertiesDataCollector dataCollector, + QualifyingPropertyVerificationContext ctx) + throws PropertyDataStructureException, InvalidPropertyException, + QualifyingPropertyVerifierNotAvailableException + { + // verify internal data structure of properties (presence of mandatory elements + // and sanity of all elements) + List unmarshalledProperties = dataCollector.getPropertiesData(); + dataObjectsStructureVerifier.verifiyPropertiesDataStructure(unmarshalledProperties); + + /* + * Because TSA or CA certificates may have been used in the past (in previous + * ArchiveTimeStamp) and included in TimeStampVerificationData or one of + * XAdES-X-L properties, it does not have to be present in subsequent + * TimeStampValidationData, we have to first go over all properties that can + * contain certificates other than time stamps themselves. + * + * Because the certificates have been added in chronological order, we too have + * to validate them in normal order + * + * TODO time stamp verifier won't use the certificates if the algorithms used + * to secure them are currently insecure (at the time of last validated timestamp) + * + * TODO make it work for CA certificates included in previous time stamp tokens + */ + for(PropertyDataObject pdo : unmarshalledProperties) + { + if (pdo instanceof CertificateValuesData || + pdo instanceof RevocationValuesData || + pdo instanceof AttrAuthoritiesCertValuesData || + pdo instanceof AttributeRevocationValuesData || + pdo instanceof TimeStampValidationDataData) + { + QualifyingPropertyVerifier propVerifier = + this.propertyVerifiersMapper.getVerifier(pdo); + + Element elem = (Element) dataCollector.getPropertyNode(pdo); + + // the verifier adds certificates and CRLs to context (ctx) + propVerifier.verify(pdo, elem, ctx); + } // else do nothing + } + + List props = new LinkedList(); + + /* + * go over all properties and verify them + * because the most recent additions to the signature are (or at least SHOULD be) + * last, we go in reverse order + */ + ListIterator property = + unmarshalledProperties.listIterator(unmarshalledProperties.size()); + + PropertyDataObject propData; + do + { + propData = property.previous(); + /* + * Because ArchiveTimeStamp signs everything before its location, we update + * the currentTime in ctx just after its successful verification. + * This is not so easy with SignatureTimeStamp or SigAndRefsTimeStamp, there + * we can update the currentTime only after we went over all those + * properties, that is, we found the first property that should have been + * signed by SigAndRefsTimeStamp. + * + * With SignatureTimeStamp similar situation occurs when there are only + * SignedProperties left or all properties have been handled + * + * TODO The situation is even more complex in the unsupported case of + * RefsOnlyTimeStamp. + * + * TODO handle gracefully multiple ArchiveTimeStamps with similar times + * The above described behavior may cause failure of verification for one + * of the ArchiveTimeStamps if there are multiple ArchiveTimeStamps present, + * but it won't cause failure in verification of the whole signature. + * If the first ArchiveTimeStamp is invalid, it won't change the time, so the + * second one will verify successfully. + */ + try { + // when we encounter property that needs to be signed by + // sigAndRefsTimeStamp it means we should have parsed all + // SigAndRefsTimeStamps and can change time to the earliest one from + // those properties + if (sigAndRefsPropertySignedProperties.contains(propData.getClass())) + setDateFromSigAndRefsProperty(ctx, props); + // similar situation as with sigAndRefsTimeStamp but for SignatureTimeStamp + if (signedProperties.contains(propData.getClass())) + setDateFromSignatureTimeStamp(ctx, props); + // verificator of ArchiveTimeStamp changes time itself + + /* + * To verify those three properties we need result of Signature + * verification, so we add place holders for them and will verify them + * after Signature verification + */ + if (propData instanceof CompleteCertificateRefsData || + propData instanceof CompleteRevocationRefsData || + propData instanceof SigningCertificateData) + { + props.add(0, new PropertyInfo(propData, null)); + continue; + } + + QualifyingPropertyVerifier propVerifier = + this.propertyVerifiersMapper.getVerifier(propData); + + Element elem = (Element) dataCollector.getPropertyNode(propData); + + QualifyingProperty p = propVerifier.verify(propData, elem, ctx); + if (p == null) + throw new PropertyVerifierErrorException(propData + .getClass().getName()); + + // we go over properties in reverse order so we have to insert + // them at the beginning of the returned list to preserve order + props.add(0, new PropertyInfo(propData, p)); + + } catch (XAdES4jException e) // TODO make it less generic + { + // critical error + if (e instanceof SigningTimeVerificationException) + throw (InvalidPropertyException)e; + + // failure in verification of property will only cause its ommitance + // in returned property list + System.out.println("Error when verifying " + propData.getClass() + + ", stack trace follows: "); + e.printStackTrace(); + continue; + } + + } while (property.hasPrevious()); + + // SignedProperties are not mandatory so we have to try changing the time after + // parsing all properties + setDateFromSignatureTimeStamp(ctx, props); + + return Collections.unmodifiableList(props); + } + + private void setDateFromSignatureTimeStamp( + QualifyingPropertyVerificationContext ctx, + List props) + { + Date oldest = null; + for (PropertyInfo propInfo : props) + { + QualifyingProperty p = propInfo.getProperty(); + if (p instanceof SignatureTimeStampProperty) + { + SignatureTimeStampProperty sigTimeStamp = (SignatureTimeStampProperty) p; + if (oldest == null) + oldest = sigTimeStamp.getTime(); + else if (oldest.getTime() > sigTimeStamp.getTime().getTime()) + oldest = sigTimeStamp.getTime(); + } + } + if (oldest != null && oldest.getTime() != ctx.getCurrentTime().getTime()) + ctx.setCurrentTime(oldest); + } + + private void setDateFromSigAndRefsProperty( + QualifyingPropertyVerificationContext ctx, + List props) + { + Date oldest = null; + for (PropertyInfo propInfo : props) + { + QualifyingProperty p = propInfo.getProperty(); + if (p instanceof SigAndRefsTimeStampProperty) + { + SigAndRefsTimeStampProperty sigAndRefs = (SigAndRefsTimeStampProperty) p; + if (oldest == null) + oldest = sigAndRefs.getTime(); + else if (oldest.getTime() > sigAndRefs.getTime().getTime()) + oldest = sigAndRefs.getTime(); + } + } + + if (oldest != null && oldest.getTime() != ctx.getCurrentTime().getTime()) + ctx.setCurrentTime(oldest); + } + + @Deprecated + @Override + public Collection verifyProperties( + List unmarshalledProperties, + QualifyingPropertyVerificationContext ctx) + throws PropertyDataStructureException, InvalidPropertyException, + QualifyingPropertyVerifierNotAvailableException + { + // old invocation method so use old implementation + QualifyingPropertiesVerifier qpv = new QualifyingPropertiesVerifierImpl( + propertyVerifiersMapper, + dataObjectsStructureVerifier); + + return qpv.verifyProperties(unmarshalledProperties, ctx); + } + + @Override + public List verifyProperties( + HybridQualifPropsDataCollectorImpl propsDataCollector, + QualifyingPropertyVerificationContext ctx, + List props) + { + // now we have full revocation information about Signature, go and verify + // *Refs and SignatureCertificate properties + + List newProps = new ArrayList(props.size()); + for (PropertyInfo p : props) + { + if (p.getProperty() == null) + { + PropertyDataObject propData = p.getPropertyData(); + try { + + QualifyingPropertyVerifier propVerifier = + this.propertyVerifiersMapper.getVerifier(propData); + + Element elem = (Element) propsDataCollector.getPropertyNode(propData); + + QualifyingProperty qp = propVerifier.verify(propData, elem, ctx); + if (qp == null) + throw new PropertyVerifierErrorException(propData + .getClass().getName()); + newProps.add(new PropertyInfo(propData, qp)); + + } catch (XAdES4jException e) + { + System.out.println("Error when verifying " + + p.getPropertyData().getClass() + ", stack trace follows: "); + e.printStackTrace(); + continue; + } + + } else { + newProps.add(p); + } + } + + return newProps; + } + +} diff --git a/src/main/java/xades4j/verification/IndivDataObjsTimeStampVerifier.java b/src/main/java/xades4j/verification/IndivDataObjsTimeStampVerifier.java index c7001d02..87bfaa5c 100644 --- a/src/main/java/xades4j/verification/IndivDataObjsTimeStampVerifier.java +++ b/src/main/java/xades4j/verification/IndivDataObjsTimeStampVerifier.java @@ -16,8 +16,11 @@ */ package xades4j.verification; +import org.w3c.dom.Element; + import com.google.inject.Inject; import xades4j.utils.CannotAddDataToDigestInputException; +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.properties.IndividualDataObjsTimeStampProperty; import xades4j.properties.QualifyingProperty; import xades4j.utils.TimeStampDigestInput; @@ -41,8 +44,9 @@ public IndivDataObjsTimeStampVerifier( } @Override - protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty( + protected BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( IndividualDataObjsTimeStampData propData, + Element location, TimeStampDigestInput digestInput, QualifyingPropertyVerificationContext ctx) throws CannotAddDataToDigestInputException, TimeStampVerificationException { @@ -63,4 +67,11 @@ protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty( } return prop; } + + @Override + protected void updateContextAfterVerification(QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx) + { + // noop + } } diff --git a/src/main/java/xades4j/verification/QualifPropsDataCollectorImpl.java b/src/main/java/xades4j/verification/QualifPropsDataCollectorImpl.java index 2388450e..017d7e13 100644 --- a/src/main/java/xades4j/verification/QualifPropsDataCollectorImpl.java +++ b/src/main/java/xades4j/verification/QualifPropsDataCollectorImpl.java @@ -16,16 +16,27 @@ */ package xades4j.verification; -import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + import xades4j.properties.data.AllDataObjsTimeStampData; +import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.CertificateValuesData; import xades4j.properties.data.CompleteCertificateRefsData; import xades4j.properties.data.CompleteRevocationRefsData; import xades4j.properties.data.GenericDOMData; import xades4j.properties.data.IndividualDataObjsTimeStampData; import xades4j.properties.data.OtherPropertyData; +import xades4j.properties.data.RevocationValuesData; +import xades4j.properties.data.SigAndRefsTimeStampData; import xades4j.properties.data.SignaturePolicyData; import xades4j.properties.data.SignatureTimeStampData; import xades4j.properties.data.SignerRoleData; +import xades4j.properties.data.TimeStampValidationDataData; import xades4j.utils.PropertiesSet; import xades4j.properties.data.CommitmentTypeData; import xades4j.properties.data.DataObjectFormatData; @@ -99,6 +110,36 @@ public void addSignatureTimeStamp(SignatureTimeStampData sigTSData) } @Override + public void addSigAndRefsTimeStamp(SigAndRefsTimeStampData tsData) + { + propsData.add(tsData); + } + + @Override + public void setCertificateValues(CertificateValuesData certificateValuesData) + { + propsData.add(certificateValuesData); + } + + @Override + public void setAttrAuthoritiesCertValues(AttrAuthoritiesCertValuesData attrAuthoritiesCertValuesData) + { + propsData.add(attrAuthoritiesCertValuesData); + } + + @Override + public void setRevocationValues(RevocationValuesData revocationValuesData) + { + propsData.add(revocationValuesData); + } + + @Override + public void setAttributeRevocationValues(AttributeRevocationValuesData attrRevocValData) + { + propsData.add(attrRevocValData); + } + + @Override public void addCommitmentType(CommitmentTypeData commitmentData) { propsData.add(commitmentData); @@ -136,8 +177,34 @@ public void addOther(OtherPropertyData otherData) } /**/ - Collection getPropertiesData() + public List getPropertiesData() { return propsData.getProperties(); } + + @Override + public void linkPropertyToElem(Element node) + { + //noop + } + + @Override + public Node getPropertyNode(PropertyDataObject pdo) + { + throw new UnsupportedOperationException("QualifPropsDataCollectorImpl does not"+ + " support getPropertyNode() method. Use HybridQualifPropsDataCollectorImpl."); + } + + @Override + public void addArchiveTimeStamp(ArchiveTimeStampData tsData) + { + propsData.add(tsData); + } + + @Override + public void addTimeStampValidationDataData( + TimeStampValidationDataData timeStampValidationDataData) + { + propsData.add(timeStampValidationDataData); + } } diff --git a/src/main/java/xades4j/verification/QualifyingPropertiesVerifier.java b/src/main/java/xades4j/verification/QualifyingPropertiesVerifier.java index 896f966c..524ba8ac 100644 --- a/src/main/java/xades4j/verification/QualifyingPropertiesVerifier.java +++ b/src/main/java/xades4j/verification/QualifyingPropertiesVerifier.java @@ -17,8 +17,14 @@ package xades4j.verification; import java.util.Collection; +import java.util.List; + +import xades4j.properties.data.CompleteCertificateRefsData; +import xades4j.properties.data.CompleteRevocationRefsData; import xades4j.properties.data.PropertyDataObject; import xades4j.properties.data.PropertyDataStructureException; +import xades4j.properties.data.SigningCertificateData; +import xades4j.xml.unmarshalling.QualifyingPropertiesDataCollector; /** * @@ -29,7 +35,32 @@ interface QualifyingPropertiesVerifier /** * Verifies the data objects' structure and the XAdES rules. */ - Collection verifyProperties( - Collection unmarshalledProperties, + List verifyProperties( + QualifyingPropertiesDataCollector unmarshalledProperties, QualifyingPropertyVerificationContext ctx) throws PropertyDataStructureException, InvalidPropertyException, QualifyingPropertyVerifierNotAvailableException; + + /** + * Verifies the data objects' structure and the XAdES rules. + * Use {@link QualifyingPropertiesVerifier#verifyProperties(QualifyingPropertiesDataCollector, QualifyingPropertyVerificationContext)} + * TODO left because legacy parts of verifiers need to verify only some properties, + * for example just SignatureTimeStamp + */ + @Deprecated + Collection verifyProperties( + List unmarshalledProperties, + QualifyingPropertyVerificationContext ctx) throws PropertyDataStructureException, InvalidPropertyException, QualifyingPropertyVerifierNotAvailableException; + + /** + * Verifies properties that can be verified only after Signature has been verified, + * namely {@link CompleteCertificateRefsData}, {@link CompleteRevocationRefsData} and + * {@link SigningCertificateData}. + * @param propsDataCollector + * @param qPropsCtx + * @param props + * @return + */ + List verifyProperties( + HybridQualifPropsDataCollectorImpl propsDataCollector, + QualifyingPropertyVerificationContext qPropsCtx, + List props); } diff --git a/src/main/java/xades4j/verification/QualifyingPropertiesVerifierImpl.java b/src/main/java/xades4j/verification/QualifyingPropertiesVerifierImpl.java index 29686e2c..20a61d5c 100644 --- a/src/main/java/xades4j/verification/QualifyingPropertiesVerifierImpl.java +++ b/src/main/java/xades4j/verification/QualifyingPropertiesVerifierImpl.java @@ -20,10 +20,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; + import xades4j.properties.QualifyingProperty; import xades4j.properties.data.PropertiesDataObjectsStructureVerifier; import xades4j.properties.data.PropertyDataObject; import xades4j.properties.data.PropertyDataStructureException; +import xades4j.xml.unmarshalling.QualifyingPropertiesDataCollector; /** * @@ -44,14 +47,25 @@ class QualifyingPropertiesVerifierImpl implements QualifyingPropertiesVerifier } @Override - public Collection verifyProperties( - Collection unmarshalledProperties, + public List verifyProperties( + QualifyingPropertiesDataCollector dataCollector, QualifyingPropertyVerificationContext ctx) throws PropertyDataStructureException, InvalidPropertyException, QualifyingPropertyVerifierNotAvailableException + { + List unmarshalledProperties = dataCollector.getPropertiesData(); + return verifyProperties(unmarshalledProperties, ctx); + } + + @Override + public List verifyProperties( + List unmarshalledProperties, + QualifyingPropertyVerificationContext ctx) + throws PropertyDataStructureException, InvalidPropertyException, + QualifyingPropertyVerifierNotAvailableException { dataObjectsStructureVerifier.verifiyPropertiesDataStructure(unmarshalledProperties); - Collection props = new ArrayList(unmarshalledProperties.size()); - + List props = new ArrayList(unmarshalledProperties.size()); + for (PropertyDataObject propData : unmarshalledProperties) { QualifyingPropertyVerifier propVerifier = this.propertyVerifiersMapper.getVerifier(propData); @@ -63,6 +77,15 @@ public Collection verifyProperties( props.add(new PropertyInfo(propData, p)); } - return Collections.unmodifiableCollection(props); + return Collections.unmodifiableList(props); + } + + @Override + public List verifyProperties( + HybridQualifPropsDataCollectorImpl propsDataCollector, + QualifyingPropertyVerificationContext qPropsCtx, + List props) + { + throw new UnsupportedOperationException("Use HybridQualifyingPropertiesVerifierImpl"); } } diff --git a/src/main/java/xades4j/verification/QualifyingPropertyVerificationContext.java b/src/main/java/xades4j/verification/QualifyingPropertyVerificationContext.java index 143a1901..312d9190 100644 --- a/src/main/java/xades4j/verification/QualifyingPropertyVerificationContext.java +++ b/src/main/java/xades4j/verification/QualifyingPropertyVerificationContext.java @@ -19,11 +19,17 @@ import java.math.BigInteger; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; + import javax.security.auth.x500.X500Principal; import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial; import org.apache.xml.security.signature.ObjectContainer; @@ -36,6 +42,9 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; +import xades4j.providers.ValidationData; +import xades4j.verification.SignatureUtils.KeyInfoRes; + /** * The context available during the verification of the qualifying properties. * @see QualifyingPropertyVerifier @@ -44,17 +53,102 @@ public class QualifyingPropertyVerificationContext { private final XMLSignature signature; - private final CertificationChainData certChainData; + private CertificationChainData certChainData; private final SignedObjectsData signedObjectsData; + private final KeyInfoRes keyInfoRes; + // validation data collected during verification of attributes (trusted) + private Collection attributeValidationData; + // validation data collected during verification of signature (trusted) + private Collection signatureValidationData; + private final Set untrustedAttributeCertificates; + private final Set untrustedAttributeCRLs; + private final Set untrustedSignatureCertificates; + private final Set untrustedSignatureCRLs; + + private Date currentTime; QualifyingPropertyVerificationContext( XMLSignature signature, CertificationChainData certChainData, - SignedObjectsData signedObjectsData) + SignedObjectsData signedObjectsData, + Date currentTime) { this.signature = signature; this.certChainData = certChainData; this.signedObjectsData = signedObjectsData; + attributeValidationData = new ArrayList(); + signatureValidationData = new ArrayList(); + this.keyInfoRes = null; + this.currentTime = currentTime; + untrustedAttributeCertificates = new HashSet(); + untrustedAttributeCRLs = new HashSet(); + untrustedSignatureCertificates = new HashSet(); + untrustedSignatureCRLs = new HashSet(); + } + + public QualifyingPropertyVerificationContext( + XMLSignature signature, + KeyInfoRes keyInfoRes, + SignedObjectsData signedObjectsData, + Date currentTime) + { + this.signature = signature; + this.signedObjectsData = signedObjectsData; + this.certChainData = null; + this.keyInfoRes = keyInfoRes; + attributeValidationData = new ArrayList(); + signatureValidationData = new ArrayList(); + this.currentTime = currentTime; + untrustedAttributeCertificates = new HashSet(); + untrustedAttributeCRLs = new HashSet(); + untrustedSignatureCertificates = new HashSet(); + untrustedSignatureCRLs = new HashSet(); + } + + public Collection getAttributeValidationData() + { + return attributeValidationData; + } + + public void addAttributeValidationData(ValidationData validationData) + { + attributeValidationData.add(validationData); + } + + public Collection getSignatureValidationData() + { + return signatureValidationData; + } + + public void addSignatureValidationData(ValidationData validationData) + { + signatureValidationData.add(validationData); + } + + /** + * Changes the time at which subsequent verifications take place, used to ensure + * monotonicity of time in time stamps. + * + * @param currentTime new time at which subsequent verifications should happen + * @throws IllegalArgumentException when currentTime is later (in future) + * than time saved in this context + * ({@code currentTime.getTime() > this.currentTime.getTime()}) + */ + public void setCurrentTime(Date currentTime) + throws IllegalArgumentException + { + if (this.currentTime.getTime() < currentTime.getTime()) { + final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + throw new IllegalArgumentException(String.format("New time from TimeStamp is in the future %s < %s", + formatter.format(this.currentTime.getTime()), formatter.format(currentTime.getTime()))); + } + + this.currentTime = new Date(currentTime.getTime()); + } + + public Date getCurrentTime() + { + return currentTime; } public XMLSignature getSignature() @@ -72,6 +166,11 @@ public SignedObjectsData getSignedObjectsData() return signedObjectsData; } + public KeyInfoRes getKeyInfoRes() + { + return keyInfoRes; + } + /** * */ @@ -208,4 +307,77 @@ private TObj getReferencedElement( } } } + + public void setCertificationChainData( + CertificationChainData certificationChainData) + { + this.certChainData = certificationChainData; + } + + /** + * Remember the untrusted certificates found in properties. + * The same certificate can be provided multiple times and it will be saved only once. + * @param certificates found properties + */ + public void addAttributeCertificates(Collection certificates) + { + untrustedAttributeCertificates.addAll(certificates); + } + + /** + * Remember the untrusted attribute certificates found in properties. + * The same CRL can be provided multiple times and it will be saved only once. + * @param crls found crls + */ + public void addAttributeCRLs(Collection crls) + { + untrustedAttributeCRLs.addAll(crls); + } + + /** + * @return list of untrusted CRLs read from properties + */ + public Collection getAttributeCRLs() + { + return untrustedAttributeCRLs; + } + + /** + * @return list of untrusted certificates read from properties + */ + public Collection getAttributeCertificates() + { + return untrustedAttributeCertificates; + } + + /** + * Remember the untrusted signature certificates found in properties. + * The same certificates can be provided multiple times and it will be saved only + * once. + * @param certificates certificates to save + */ + public void addSignatureCertificates(Collection certificates) + { + untrustedSignatureCertificates.addAll(certificates); + } + + /** + * Remember the untrusted signature revocation data (CRLs) found in properties. + * The same CRL can be provided multiple times and it will be save only one. + * @param crls CRLs to save + */ + public void addSignatureCRLs(Collection crls) + { + untrustedSignatureCRLs.addAll(crls); + } + + public Collection getSignatureCRLs() + { + return untrustedSignatureCRLs; + } + + public Collection getSignatureCertificates() + { + return untrustedSignatureCertificates; + } } diff --git a/src/main/java/xades4j/verification/QualifyingPropertyVerifier.java b/src/main/java/xades4j/verification/QualifyingPropertyVerifier.java index 46628981..768de456 100644 --- a/src/main/java/xades4j/verification/QualifyingPropertyVerifier.java +++ b/src/main/java/xades4j/verification/QualifyingPropertyVerifier.java @@ -16,6 +16,8 @@ */ package xades4j.verification; +import org.w3c.dom.Element; + import xades4j.properties.QualifyingProperty; import xades4j.properties.data.PropertyDataObject; @@ -43,4 +45,22 @@ public interface QualifyingPropertyVerifier public QualifyingProperty verify( TData propData, QualifyingPropertyVerificationContext ctx) throws InvalidPropertyException; + + /** + * Verifies the property data and return the corresponding 'high-level' + * property instance. In case of failure, an exception should be thrown. This + * is done in order to prevent a failure to be undetected by erroneous code. + * Furthermore, the data structures resulting for the verification process + * become simpler. + * + * @param propData the property data + * @param elem the XML element from which the property data originated from + * @param ctx the context with data for validation + * @return the verified QualifyingProperty (never {@code null}) + * @throws InvalidPropertyException (or subclasses) if the property validation fails + */ + public QualifyingProperty verify( + TData propData, + Element elem, + QualifyingPropertyVerificationContext ctx) throws InvalidPropertyException; } diff --git a/src/main/java/xades4j/verification/RevocationValuesVerifier.java b/src/main/java/xades4j/verification/RevocationValuesVerifier.java new file mode 100644 index 00000000..7626ae26 --- /dev/null +++ b/src/main/java/xades4j/verification/RevocationValuesVerifier.java @@ -0,0 +1,56 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.verification; + +import java.security.cert.X509CRL; +import java.util.Collection; + +import xades4j.properties.QualifyingProperty; +import xades4j.properties.RevocationValuesProperty; +import xades4j.properties.data.RevocationValuesData; + +import com.google.inject.Inject; + +/** + * + * @author Hubert Kario + * + */ +public class RevocationValuesVerifier extends EncapsulatedPKIRevocationDataVerifierBase +{ + @Inject + public RevocationValuesVerifier(String propName) + { + super(propName); + } + + @Override + public QualifyingProperty createProperty(Collection crls) + { + return new RevocationValuesProperty(crls); + } + + @Override + public void addCRLsToValidationProvider( + Collection crls, + QualifyingPropertyVerificationContext ctx) + { + ctx.addSignatureCRLs(crls); + } + + // TODO verify if the values in this property are matching the CompleteRevocationRefs +} diff --git a/src/main/java/xades4j/verification/SigAndRefsTimeStampVerifier.java b/src/main/java/xades4j/verification/SigAndRefsTimeStampVerifier.java new file mode 100644 index 00000000..886846b7 --- /dev/null +++ b/src/main/java/xades4j/verification/SigAndRefsTimeStampVerifier.java @@ -0,0 +1,259 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import org.apache.xml.security.utils.Constants; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import com.google.inject.Inject; + +import xades4j.properties.BaseXAdESTimeStampProperty; +import xades4j.properties.CompleteCertificateRefsProperty; +import xades4j.properties.CompleteRevocationRefsProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.SignatureTimeStampProperty; +import xades4j.properties.data.SigAndRefsTimeStampData; +import xades4j.providers.TimeStampVerificationProvider; +import xades4j.utils.CannotAddDataToDigestInputException; +import xades4j.utils.DOMHelper; +import xades4j.utils.TimeStampDigestInput; +import xades4j.utils.TimeStampDigestInputFactory; + +/** + * Verifier for the XAdES-X time stamp property - SigAndRefsTimeStamp + * XAdES v1.4.2 G.2.2.16.2.3 + * + * @author Hubert Kario + */ +public class SigAndRefsTimeStampVerifier extends + TimeStampVerifierBase +{ + @Inject + public SigAndRefsTimeStampVerifier( + TimeStampVerificationProvider timeStampVerifier, + TimeStampDigestInputFactory timeStampDigestInputFactory) + { + super(timeStampVerifier, timeStampDigestInputFactory, + SigAndRefsTimeStampProperty.PROP_NAME); + } + + @Override + protected BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( + SigAndRefsTimeStampData propData, Element location, + TimeStampDigestInput digestInput, + QualifyingPropertyVerificationContext ctx) + throws CannotAddDataToDigestInputException, + TimeStampVerificationException + { + // TimeStamp is taken over SignatureValue element + Element sigValueElem = DOMHelper.getFirstDescendant(ctx.getSignature() + .getElement(), Constants.SignatureSpecNS, + Constants._TAG_SIGNATUREVALUE); + digestInput.addNode(sigValueElem); + + /* + * "Take each one of the signed properties and not property elements in + * the signature that the normative part dictates that must be + * time-stamped, in the order specified in the normative clause defining + * the time-stamp token container type. Canonicalize them and + * concatenate the resulting bytes in one octet stream. If the + * CanonicalizationMethod element of the property is present, use it for + * canonicalizing. Otherwise, use the standard canonicalization method + * as specified by XMLDSIG." + * + * XAdES 1.4.2, section G.2.2.16.2.3, Implicit mechanism: " + * 1) In step 4) the verifier should check that the CompleteCertificateRefs and + * CompleteRevocationRefs properties are present in the signature. She + * should also check that they and, if present, SignatureTimeStamp, + * AttributeCertificateRefs and AttributeRevocationRefs, appear before + * SigAndRefsTimeStamp. + * 2) In step 5), the verifier should take the aforementioned properties in + * their order of appearance within the signature." + */ + + if (location == null) // use fallback mechanism + { + // XXX legacy code, to be removed when whole library is using new + // hybrid verifier + + // next elements that should have been timestamped are all + // SignatureTimeStamps + // there may be no SignatureTimeStamps + NodeList signatureTimeStamps = ctx + .getSignature() + .getElement() + .getElementsByTagNameNS( + SignatureTimeStampProperty.XADES_XMLNS, + SignatureTimeStampProperty.PROP_NAME); + for (int i = 0; i < signatureTimeStamps.getLength(); i++) + { + Element timeStampElem = (Element) signatureTimeStamps.item(i); + digestInput.addNode(timeStampElem); + } + + // then CompleteCertificateRefs + Element completeCertificateRefsElem = DOMHelper.getFirstDescendant( + ctx.getSignature().getElement(), + CompleteCertificateRefsProperty.XADES_XMLNS, + CompleteCertificateRefsProperty.PROP_NAME); + digestInput.addNode(completeCertificateRefsElem); + + // ...and CompleteRevocationRefs + Element completeRevocationRefsElem = DOMHelper.getFirstDescendant( + ctx.getSignature().getElement(), + CompleteRevocationRefsProperty.XADES_XMLNS, + CompleteRevocationRefsProperty.PROP_NAME); + digestInput.addNode(completeRevocationRefsElem); + + // AttributeCertificateRefs are optional + Element attributeCertificateRefs = DOMHelper.getFirstDescendant(ctx + .getSignature().getElement(), + CompleteRevocationRefsProperty.XADES_XMLNS, + "AttributeCertificateRefs"); + if (attributeCertificateRefs != null) + throw new CannotAddDataToDigestInputException(new Exception( + "Can't verify SigAndRefsTimeStamp: " + + "AttributeCertificateRefs is unsupported")); + + // AttributeRevocationRefs are optional + Element attributeRevocationRefs = DOMHelper.getFirstDescendant(ctx + .getSignature().getElement(), + CompleteRevocationRefsProperty.XADES_XMLNS, + "AttributeRevocationRefs"); + if (attributeRevocationRefs != null) + throw new CannotAddDataToDigestInputException(new Exception( + "Can't verify SigAndRefsTimeStamp: " + + "AttributeRevocationRefs is unsupported")); + + return new SigAndRefsTimeStampProperty(); + } else { + // TODO write test for document with SigAndRefsTimeStamp but no + // SignatureTimeStamp + + // remember whatever we found mandatory elements + boolean foundCompleteRevocRefs = false; + boolean foundCompleteCertRefs = false; + + // iterate over elements in order in which they are present in XML + for (Element elem = DOMHelper.getFirstChildElement(location + .getParentNode()); elem != location; elem = DOMHelper + .getNextSiblingElement(elem)) + { + if (isElementMatchingProperty(elem, + SignatureTimeStampProperty.class)) + { + // there are no specific requirements for SignatureTimeStamp + // so just add them + digestInput.addNode(elem); + // TODO check if there are no SignatureTimestamps *after* + // SigAndRefsTimeStamp + } else if (isElementMatchingProperty(elem, + CompleteCertificateRefsProperty.class)) + { + // there must be exactly one CompleteCertificateRefs element + if (foundCompleteCertRefs) + throw new CannotAddDataToDigestInputException( + new Exception( + "Duplicate CompleteCertificateRefs property" + + " in Signature")); + foundCompleteCertRefs = true; + digestInput.addNode(elem); + // TODO check if there are no CompleteCertificateRefs + // *after* SigAndRefsTimeStamp + } else if (isElementMatchingProperty(elem, + CompleteRevocationRefsProperty.class)) + { + // there must be exactly one CompleteRevocationRefs element + if (foundCompleteRevocRefs) + throw new CannotAddDataToDigestInputException( + new Exception( + "Duplicate CompleteRevocationRefs property" + + " in Singature")); + foundCompleteRevocRefs = true; + digestInput.addNode(elem); + // TODO check if there are no CompleteRevocationRefs *after* + // SigAndRefsTimeStamp + } else if (elem.getLocalName().equalsIgnoreCase( + "AttributeCertificateRefs") + && elem.getNamespaceURI().equalsIgnoreCase( + QualifyingProperty.XADES_XMLNS)) + { + // TODO implement AttributeCertificateRefs support + throw new CannotAddDataToDigestInputException( + new Exception("Can't verify SigAndRefsTimeStamp: " + + "AttributeCertificateRefs is unsupported")); + } else if (elem.getLocalName().equalsIgnoreCase( + "AttributeRevocationRefs") + && elem.getNamespaceURI().equalsIgnoreCase( + QualifyingProperty.XADES_XMLNS)) + { + // TODO implement AttributeRevocationRefs support + throw new CannotAddDataToDigestInputException( + new Exception("Can't verify SigAndRefsTimeStamp: " + + "AttributeRevocationRefs is unsupported")); + } // else: ignore other properties + } + + if (foundCompleteRevocRefs && foundCompleteCertRefs) + return new SigAndRefsTimeStampProperty(); + else + throw new CannotAddDataToDigestInputException(new Exception( + "Missing mandatory properties: CompleteCertificateRefs" + + " or CompleteRevocationRefs")); + } + } + + private boolean isElementMatchingProperty(Element elem, + Class prop) + { + try + { + return elem.getLocalName().equalsIgnoreCase( + (String) prop.getField("PROP_NAME").get(null)) + && elem.getNamespaceURI().equalsIgnoreCase( + (String) prop.getField("XADES_XMLNS").get(null)); + } catch (IllegalArgumentException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (SecurityException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (IllegalAccessException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } catch (NoSuchFieldException e) + { + e.printStackTrace(); + throw new InternalError("Wrong property class"); + } + } + + @Override + protected void updateContextAfterVerification(QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx) + { + /* + * noop, we can set time only after *all* sigAndRefsTimeStamps have been verified + * this is done in QualifyingPropertiesVerifier + */ + } +} diff --git a/src/main/java/xades4j/verification/SigProdPlaceVerifier.java b/src/main/java/xades4j/verification/SigProdPlaceVerifier.java index ece1e4af..b152e6a1 100644 --- a/src/main/java/xades4j/verification/SigProdPlaceVerifier.java +++ b/src/main/java/xades4j/verification/SigProdPlaceVerifier.java @@ -16,6 +16,8 @@ */ package xades4j.verification; +import org.w3c.dom.Element; + import xades4j.properties.QualifyingProperty; import xades4j.properties.SignatureProductionPlaceProperty; import xades4j.properties.data.SignatureProdPlaceData; @@ -37,4 +39,12 @@ public QualifyingProperty verify( propData.getPostalCode(), propData.getCountry()); } + + @Override + public QualifyingProperty verify(SignatureProdPlaceData propData, + Element elem, QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/main/java/xades4j/verification/SignaturePolicyVerifier.java b/src/main/java/xades4j/verification/SignaturePolicyVerifier.java index da605fe0..79cedbcd 100644 --- a/src/main/java/xades4j/verification/SignaturePolicyVerifier.java +++ b/src/main/java/xades4j/verification/SignaturePolicyVerifier.java @@ -21,6 +21,9 @@ import java.io.InputStream; import java.security.MessageDigest; import java.util.Arrays; + +import org.w3c.dom.Element; + import xades4j.properties.ObjectIdentifier; import xades4j.properties.QualifyingProperty; import xades4j.properties.SignaturePolicyIdentifierProperty; @@ -98,4 +101,12 @@ public QualifyingProperty verify( } } } + + @Override + public QualifyingProperty verify(SignaturePolicyData propData, + Element elem, QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/main/java/xades4j/verification/SignatureSpecificVerificationOptions.java b/src/main/java/xades4j/verification/SignatureSpecificVerificationOptions.java index c7c15927..00c97563 100644 --- a/src/main/java/xades4j/verification/SignatureSpecificVerificationOptions.java +++ b/src/main/java/xades4j/verification/SignatureSpecificVerificationOptions.java @@ -28,14 +28,17 @@ * Represents verification options that are specific to a signature, i.e., * options that are not profile-wide. *

- * It includes base URI, data for anonymous references or resource resolvers + * It includes base URI for file containing signature, data for anonymous references + * or resource resolvers (used to retrieve external files referenced by signature). * * @see xades4j.verification.XadesVerifier * @author Luís */ public class SignatureSpecificVerificationOptions { - static final SignatureSpecificVerificationOptions empty = new SignatureSpecificVerificationOptions(); + static SignatureSpecificVerificationOptions empty() { + return new SignatureSpecificVerificationOptions(); + } private String baseUriForRelativeReferences; private InputStream dataForAnonymousReference; diff --git a/src/main/java/xades4j/verification/SignatureTimeStampVerifier.java b/src/main/java/xades4j/verification/SignatureTimeStampVerifier.java index 414121d4..1655973e 100644 --- a/src/main/java/xades4j/verification/SignatureTimeStampVerifier.java +++ b/src/main/java/xades4j/verification/SignatureTimeStampVerifier.java @@ -20,6 +20,7 @@ import org.apache.xml.security.utils.Constants; import org.w3c.dom.Element; import xades4j.utils.CannotAddDataToDigestInputException; +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.properties.QualifyingProperty; import xades4j.properties.SignatureTimeStampProperty; import xades4j.utils.TimeStampDigestInput; @@ -43,8 +44,9 @@ public SignatureTimeStampVerifier( } @Override - protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty( + protected BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( SignatureTimeStampData propData, + Element location, TimeStampDigestInput digestInput, QualifyingPropertyVerificationContext ctx) throws CannotAddDataToDigestInputException { @@ -54,4 +56,14 @@ protected QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty( digestInput.addNode(sigValueElem); return new SignatureTimeStampProperty(); } + + @Override + protected void updateContextAfterVerification(QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx) + { + /* + * noop, we can set time only after *all* SignatureTimeStamps have been verified + * this is done in QualifyingPropertiesVerifier + */ + } } diff --git a/src/main/java/xades4j/verification/SignatureUtils.java b/src/main/java/xades4j/verification/SignatureUtils.java index 94a55488..6bb4bb74 100644 --- a/src/main/java/xades4j/verification/SignatureUtils.java +++ b/src/main/java/xades4j/verification/SignatureUtils.java @@ -1,6 +1,7 @@ /* * XAdES4j - A Java library for generation and verification of XAdES signatures. * Copyright (C) 2010 Luis Goncalves. + * Copyrigth (C) 2012 Hubert Kario - QBS. * * 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 @@ -16,11 +17,13 @@ */ package xades4j.verification; +import java.security.PublicKey; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import javax.security.auth.x500.X500Principal; import org.apache.xml.security.exceptions.XMLSecurityException; @@ -32,6 +35,10 @@ import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.signature.XMLSignatureException; import org.apache.xml.security.transforms.Transforms; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.util.encoders.Hex; import org.w3c.dom.Element; import org.w3c.dom.Node; import xades4j.XAdES4jXMLSigException; @@ -44,308 +51,346 @@ * * @author Luís */ -class SignatureUtils -{ - - private SignatureUtils() - { - } - /**/ - - static class KeyInfoRes - { - - List keyInfoCerts; - X509CertSelector certSelector; - XMLX509IssuerSerial issuerSerial; - - KeyInfoRes( - List keyInfoCerts, - X509CertSelector certSelector, - XMLX509IssuerSerial issuerSerial) - { - this.keyInfoCerts = keyInfoCerts; - this.certSelector = certSelector; - this.issuerSerial = issuerSerial; - } - } - - static KeyInfoRes processKeyInfo( - KeyInfo keyInfo) throws CertificateValidationException - { - if (null == keyInfo || !keyInfo.containsX509Data()) - { - throw new InvalidKeyInfoDataException("No X509Data to identify the leaf certificate"); - } - - List keyInfoCerts = new ArrayList(1); - XMLX509IssuerSerial issuerSerial = null; - X509CertSelector certSelector = new X509CertSelector(); - - // XML-DSIG 4.4.4: "Any X509IssuerSerial, X509SKI, and X509SubjectName elements - // that appear MUST refer to the certificate or certificates containing the - // validation key." - // "All certificates appearing in an X509Data element MUST relate to the - // validation key by either containing it or being part of a certification - // chain that terminates in a certificate containing the validation key". - - // Scan ds:X509Data to find ds:IssuerSerial or ds:SubjectName elements. The - // first to be found is used to select the leaf certificate. If none of those - // elements is present, the first ds:X509Certificate is assumed as the signing - // certificate. - boolean hasSelectionCriteria = false; - - try - { - for (int i = 0; i < keyInfo.lengthX509Data(); ++i) - { - X509Data x509Data = keyInfo.itemX509Data(i); - - if(!hasSelectionCriteria) - { - if (x509Data.containsIssuerSerial()) - { - issuerSerial = x509Data.itemIssuerSerial(0); - certSelector.setIssuer(new X500Principal(issuerSerial.getIssuerName())); - certSelector.setSerialNumber(issuerSerial.getSerialNumber()); - hasSelectionCriteria = true; - } - else if (x509Data.containsSubjectName()) - { - certSelector.setSubject(new X500Principal(x509Data.itemSubjectName(0).getSubjectName())); - hasSelectionCriteria = true; - } - } - - // Collect all certificates as they may be needed to build the cert path. - if (x509Data.containsCertificate()) - { - for (int j = 0; j < x509Data.lengthCertificate(); ++j) - { - keyInfoCerts.add(x509Data.itemCertificate(j).getX509Certificate()); - } - } - } - - if(!hasSelectionCriteria) - { - if(keyInfoCerts.isEmpty()) - { - // No criteria to select the leaf certificate. - // Improvement: search the SigningCertiticate property and try to - // find the "bottom" certificate. - throw new InvalidKeyInfoDataException("No criteria to select the leaf certificate"); - } - certSelector.setCertificate(keyInfoCerts.get(0)); - } - } - catch (XMLSecurityException ex) - { - throw new InvalidKeyInfoDataException("Cannot process X509Data", ex); - } - - return new KeyInfoRes(keyInfoCerts, certSelector, issuerSerial); - } - - /**************************************************************************/ - static class ReferencesRes - { - - /** - * In signature order. - */ - List dataObjsReferences; - Reference signedPropsReference; - - ReferencesRes( - List dataObjsReferences, - Reference signedPropsReference) - { - this.dataObjsReferences = Collections.unmodifiableList(dataObjsReferences); - this.signedPropsReference = signedPropsReference; - } - } - - static ReferencesRes processReferences( - XMLSignature signature) throws QualifyingPropertiesIncorporationException, XAdES4jXMLSigException - { - SignedInfo signedInfo = signature.getSignedInfo(); - - List dataObjsReferences = new ArrayList(signedInfo.getLength() - 1); - Reference signedPropsRef = null; - - for (int i = 0; i < signedInfo.getLength(); i++) - { - Reference ref; - try - { - ref = signedInfo.item(i); - } catch (XMLSecurityException ex) - { - throw new XAdES4jXMLSigException(String.format("Cannot process the %dth reference", i), ex); - } - - String refTypeUri = ref.getType(); - - // XAdES 6.3.1: "In order to protect the properties with the signature, - // a ds:Reference element MUST be added to the XMLDSIG signature (...) - // composed in such a way that it uses the SignedProperties element (...) - // as the input for computing its corresponding digest. Additionally, - // (...) use the Type attribute of this particular ds:Reference element, - // with its value set to: http://uri.etsi.org/01903#SignedProperties." - if (QualifyingProperty.SIGNED_PROPS_TYPE_URI.equals(refTypeUri)) - { - if (signedPropsRef != null) - { - throw new QualifyingPropertiesIncorporationException("Multiple references to SignedProperties"); - } - signedPropsRef = ref; - } else - { - RawDataObjectDesc dataObj = new RawDataObjectDesc(ref); - dataObjsReferences.add(dataObj); - try - { - Transforms transfs = ref.getTransforms(); - if (transfs != null) - { - for (int j = 0; j < transfs.getLength(); ++j) - { - dataObj.withTransform(new GenericAlgorithm(transfs.item(j).getURI())); - } - } - } catch (XMLSecurityException ex) - { - throw new XAdES4jXMLSigException("Cannot process transfroms", ex); - } - - } - } - - if (null == signedPropsRef) - // !!! - // Still may be a XAdES signature, if the signing certificate is - // protected. For now, that scenario is not supported. - { - throw new QualifyingPropertiesIncorporationException("SignedProperties reference not found"); - } - - return new ReferencesRes(dataObjsReferences, signedPropsRef); - } - - /***************************************************************************/ - static Element getQualifyingPropertiesElement(XMLSignature signature) throws QualifyingPropertiesIncorporationException - { - boolean foundXAdESContainerObject = false; - Element qualifyingPropsElem = null; - - for (int i = 0; i < signature.getObjectLength(); ++i) - { - Element objElem = signature.getObjectItem(i).getElement(); - Collection xadesElems = getXAdESChildElements(objElem); - - if (!xadesElems.isEmpty()) - { - // XAdES 6.3: "all instances of the QualifyingProperties and the - // QualifyingPropertiesReference elements MUST occur within a single - // ds:Object element". This could be tested with qualifyingPropsNode - // because I'm only supporting QualifyingProperties. Anyway, the - // exception message is more specific this way. - if (foundXAdESContainerObject) - { - throw new QualifyingPropertiesIncorporationException("All instances of the QualifyingProperties element must occur within a single ds:Object element"); - } - - // If this Object had XAdES elements, it is "the Object". The for - // cycle over the Objects is not interrupted because I need to - // check the correct incorporation of properties (XAdES G.2.2.1). - foundXAdESContainerObject = true; - - for (Element e : xadesElems) - { - if (e.getLocalName().equals(QualifyingProperty.QUALIFYING_PROPS_TAG)) - { - // XAdES 6.3: "at most one instance of the QualifyingProperties - // element MAY occur within this ds:Object element". - if (qualifyingPropsElem != null) - { - throw new QualifyingPropertiesIncorporationException("Only a single QualifyingProperties element is allowed inside the ds:Object element"); - } - qualifyingPropsElem = e; - - } else - // QualifyingPropertiesReference is not supported, so - // nothing else on this namespace should appear. - { - throw new QualifyingPropertiesIncorporationException("Only the QualifyingProperties element is supported"); - } - } - } - } - - if (!foundXAdESContainerObject) - { - throw new QualifyingPropertiesIncorporationException("Couldn't find any XAdES elements"); - } - - return qualifyingPropsElem; - } - - private static Collection getXAdESChildElements( - Element xmlObjectElem) - { - Collection xadesElems = new ArrayList(1); - - Node child = xmlObjectElem.getFirstChild(); - while (child != null) - { - if (child.getNodeType() == Node.ELEMENT_NODE && QualifyingProperty.XADES_XMLNS.equals(child.getNamespaceURI())) - { - xadesElems.add((Element) child); - } - child = child.getNextSibling(); - } - - return xadesElems; - } - - static void checkSignedPropertiesIncorporation(Element qualifyingPropsElem, Reference signedPropsRef) throws QualifyingPropertiesIncorporationException - { - Element signedPropsElem = DOMHelper.getFirstChildElement(qualifyingPropsElem); - if (signedPropsElem == null - || !signedPropsElem.getLocalName().equals(QualifyingProperty.SIGNED_PROPS_TAG) - || !signedPropsElem.getNamespaceURI().equals(QualifyingProperty.XADES_XMLNS)) - { - throw new QualifyingPropertiesIncorporationException("SignedProperties not found as the first child of QualifyingProperties."); - } - - DOMHelper.useIdAsXmlId(signedPropsElem); - - // Only QualifyingProperties in the signature's document are supported. - // XML-DSIG 4.3.3.2: "a same-document reference is defined as a URI-Reference - // that consists of a hash sign ('#') followed by a fragment" - if (!signedPropsRef.getURI().startsWith("#")) - { - throw new QualifyingPropertiesIncorporationException("Only QualifyingProperties in the signature's document are supported"); - } - - try - { - Node sPropsNode = signedPropsRef.getNodesetBeforeFirstCanonicalization().getSubNode(); - if (sPropsNode == null || sPropsNode.getNodeType() != Node.ELEMENT_NODE) - { - throw new QualifyingPropertiesIncorporationException("The supposed reference over signed properties doesn't cover an element."); - } - - // The referenced signed properties element must be the child of qualifying properties. - Element referencedSignedPropsElem = (Element) sPropsNode; - if (referencedSignedPropsElem != signedPropsElem) - { - throw new QualifyingPropertiesIncorporationException("The referenced SignedProperties are not contained by the proper QualifyingProperties element"); - } - } catch (XMLSignatureException ex) - { - throw new QualifyingPropertiesIncorporationException("Cannot get the referenced SignedProperties", ex); - } - } +class SignatureUtils { + + private SignatureUtils() { + } + /**/ + + /** + * Unique identifier of key used for Signature + */ + static class KeyInfoRes { + + List keyInfoCerts; + X509CertSelector certSelector; + XMLX509IssuerSerial issuerSerial; + + KeyInfoRes(List keyInfoCerts, X509CertSelector certSelector, + XMLX509IssuerSerial issuerSerial) { + this.keyInfoCerts = keyInfoCerts; + this.certSelector = certSelector; + this.issuerSerial = issuerSerial; + } + } + + static KeyInfoRes processKeyInfo(KeyInfo keyInfo) throws CertificateValidationException { + if (null == keyInfo || !keyInfo.containsX509Data()) { + throw new InvalidKeyInfoDataException("No X509Data to identify the leaf certificate"); + } + + List keyInfoCerts = new ArrayList(1); + XMLX509IssuerSerial issuerSerial = null; + X509CertSelector certSelector = new X509CertSelector(); + + // XML-DSIG 4.4.4: "Any X509IssuerSerial, X509SKI, and X509SubjectName + // elements + // that appear MUST refer to the certificate or certificates containing + // the + // validation key." + // "All certificates appearing in an X509Data element MUST relate to the + // validation key by either containing it or being part of a + // certification + // chain that terminates in a certificate containing the validation + // key". + + // Scan ds:X509Data to find ds:IssuerSerial or ds:SubjectName elements. + // The + // first to be found is used to select the leaf certificate. If none of + // those + // elements is present, the first ds:X509Certificate is assumed as the + // signing + // certificate. + boolean hasSelectionCriteria = false; + + try { + for (int i = 0; i < keyInfo.lengthX509Data(); ++i) { + X509Data x509Data = keyInfo.itemX509Data(i); + + if (!hasSelectionCriteria) { + if (x509Data.containsIssuerSerial()) { + issuerSerial = x509Data.itemIssuerSerial(0); + certSelector.setIssuer(new X500Principal(issuerSerial.getIssuerName())); + certSelector.setSerialNumber(issuerSerial.getSerialNumber()); + hasSelectionCriteria = true; + } else if (x509Data.containsSubjectName()) { + certSelector.setSubject(new X500Principal(x509Data.itemSubjectName(0).getSubjectName())); + hasSelectionCriteria = true; + } + } + + // Collect all certificates as they may be needed to build the + // cert path. + if (x509Data.containsCertificate()) { + for (int j = 0; j < x509Data.lengthCertificate(); ++j) { + keyInfoCerts.add(x509Data.itemCertificate(j).getX509Certificate()); + } + } + } + + if (!hasSelectionCriteria) { + if (keyInfoCerts.isEmpty()) { + // No criteria to select the leaf certificate. + // Improvement: search the SigningCertiticate property and + // try to + // find the "bottom" certificate. + throw new InvalidKeyInfoDataException("No criteria to select the leaf certificate"); + } + + keyInfoCerts.sort(new Comparator() { + @Override + public int compare(X509Certificate cert1, X509Certificate cert2) { + try { + cert1.verify(cert2.getPublicKey()); + return -1; + } catch (Throwable t) { + try { + cert2.verify(cert1.getPublicKey()); + return 1; + } catch (Throwable t2) { + return 0; + } + } + } + }); + certSelector.setCertificate(keyInfoCerts.get(0)); + } + } catch (XMLSecurityException ex) { + throw new InvalidKeyInfoDataException("Cannot process X509Data", ex); + } + + return new KeyInfoRes(keyInfoCerts, certSelector, issuerSerial); + } + + /**************************************************************************/ + static class ReferencesRes { + + /** + * In signature order. + */ + List dataObjsReferences; + Reference signedPropsReference; + + ReferencesRes(List dataObjsReferences, Reference signedPropsReference) { + this.dataObjsReferences = Collections.unmodifiableList(dataObjsReferences); + this.signedPropsReference = signedPropsReference; + } + } + + /** + * Extracts references to signed properties and signed data objects from + * provided signature. + * + * @param signature + * @return + * @throws QualifyingPropertiesIncorporationException + * @throws XAdES4jXMLSigException + */ + static ReferencesRes processReferences(XMLSignature signature) + throws QualifyingPropertiesIncorporationException, XAdES4jXMLSigException { + SignedInfo signedInfo = signature.getSignedInfo(); + + List dataObjsReferences = new ArrayList(signedInfo.getLength() - 1); + Reference signedPropsRef = null; + + for (int i = 0; i < signedInfo.getLength(); i++) { + Reference ref; + try { + ref = signedInfo.item(i); + } catch (XMLSecurityException ex) { + throw new XAdES4jXMLSigException(String.format("Cannot process the %dth reference", i), ex); + } + + String refTypeUri = ref.getType(); + + // XAdES 6.3.1: "In order to protect the properties with the + // signature, + // a ds:Reference element MUST be added to the XMLDSIG signature + // (...) + // composed in such a way that it uses the SignedProperties element + // (...) + // as the input for computing its corresponding digest. + // Additionally, + // (...) use the Type attribute of this particular ds:Reference + // element, + // with its value set to: + // http://uri.etsi.org/01903#SignedProperties." + if (QualifyingProperty.SIGNED_PROPS_TYPE_URI.equals(refTypeUri)) { + if (signedPropsRef != null) { + throw new QualifyingPropertiesIncorporationException("Multiple references to SignedProperties"); + } + signedPropsRef = ref; + } else { + RawDataObjectDesc dataObj = new RawDataObjectDesc(ref); + dataObjsReferences.add(dataObj); + try { + Transforms transfs = ref.getTransforms(); + if (transfs != null) { + for (int j = 0; j < transfs.getLength(); ++j) { + dataObj.withTransform(new GenericAlgorithm(transfs.item(j).getURI())); + } + } + } catch (XMLSecurityException ex) { + throw new XAdES4jXMLSigException("Cannot process transfroms", ex); + } + + } + } + + if (null == signedPropsRef) + // !!! + // Still may be a XAdES signature, if the signing certificate is + // protected. For now, that scenario is not supported. + { + throw new QualifyingPropertiesIncorporationException("SignedProperties reference not found"); + } + + return new ReferencesRes(dataObjsReferences, signedPropsRef); + } + + /***************************************************************************/ + static Element getQualifyingPropertiesElement(XMLSignature signature) + throws QualifyingPropertiesIncorporationException { + boolean foundXAdESContainerObject = false; + Element qualifyingPropsElem = null; + + for (int i = 0; i < signature.getObjectLength(); ++i) { + Element objElem = signature.getObjectItem(i).getElement(); + Collection xadesElems = getXAdESChildElements(objElem); + + if (!xadesElems.isEmpty()) { + // XAdES 6.3: "all instances of the QualifyingProperties and the + // QualifyingPropertiesReference elements MUST occur within a + // single + // ds:Object element". This could be tested with + // qualifyingPropsNode + // because I'm only supporting QualifyingProperties. Anyway, the + // exception message is more specific this way. + if (foundXAdESContainerObject) { + throw new QualifyingPropertiesIncorporationException( + "All instances of the QualifyingProperties element must occur within a single ds:Object element"); + } + + // If this Object had XAdES elements, it is "the Object". The + // for + // cycle over the Objects is not interrupted because I need to + // check the correct incorporation of properties (XAdES + // G.2.2.1). + foundXAdESContainerObject = true; + + for (Element e : xadesElems) { + if (e.getLocalName().equals(QualifyingProperty.QUALIFYING_PROPS_TAG)) { + // XAdES 6.3: "at most one instance of the + // QualifyingProperties + // element MAY occur within this ds:Object element". + if (qualifyingPropsElem != null) { + throw new QualifyingPropertiesIncorporationException( + "Only a single QualifyingProperties element is allowed inside the ds:Object element"); + } + qualifyingPropsElem = e; + + } else + // QualifyingPropertiesReference is not supported, so + // nothing else on this namespace should appear. + { + throw new QualifyingPropertiesIncorporationException( + "Only the QualifyingProperties element is supported"); + } + } + } + } + + if (!foundXAdESContainerObject) { + throw new QualifyingPropertiesIncorporationException("Couldn't find any XAdES elements"); + } + + return qualifyingPropsElem; + } + + private static Collection getXAdESChildElements(Element xmlObjectElem) { + Collection xadesElems = new ArrayList(1); + + Node child = xmlObjectElem.getFirstChild(); + while (child != null) { + if (child.getNodeType() == Node.ELEMENT_NODE + && QualifyingProperty.XADES_XMLNS.equals(child.getNamespaceURI())) { + xadesElems.add((Element) child); + } + child = child.getNextSibling(); + } + + return xadesElems; + } + + /** + * Check if signedProperties are properly embedded in qualifying properties + * element + * + * @param qualifyingPropsElem + * @param signedPropsRef + * @throws QualifyingPropertiesIncorporationException + */ + static void checkSignedPropertiesIncorporation(Element qualifyingPropsElem, Reference signedPropsRef) + throws QualifyingPropertiesIncorporationException { + Element signedPropsElem = DOMHelper.getFirstChildElement(qualifyingPropsElem); + if (signedPropsElem == null || !signedPropsElem.getLocalName().equals(QualifyingProperty.SIGNED_PROPS_TAG) + || !signedPropsElem.getNamespaceURI().equals(QualifyingProperty.XADES_XMLNS)) { + throw new QualifyingPropertiesIncorporationException( + "SignedProperties not found as the first child of QualifyingProperties."); + } + + DOMHelper.useIdAsXmlId(signedPropsElem); + + // Only QualifyingProperties in the signature's document are supported. + // XML-DSIG 4.3.3.2: "a same-document reference is defined as a + // URI-Reference + // that consists of a hash sign ('#') followed by a fragment" + if (!signedPropsRef.getURI().startsWith("#")) { + throw new QualifyingPropertiesIncorporationException( + "Only QualifyingProperties in the signature's document are supported"); + } + + try { + Node sPropsNode = signedPropsRef.getNodesetBeforeFirstCanonicalization().getSubNode(); + if (sPropsNode == null || sPropsNode.getNodeType() != Node.ELEMENT_NODE) { + throw new QualifyingPropertiesIncorporationException( + "The supposed reference over signed properties doesn't cover an element."); + } + + // The referenced signed properties element must be the child of + // qualifying properties. + Element referencedSignedPropsElem = (Element) sPropsNode; + if (referencedSignedPropsElem != signedPropsElem) { + throw new QualifyingPropertiesIncorporationException( + "The referenced SignedProperties are not contained by the proper QualifyingProperties element"); + } + } catch (XMLSignatureException ex) { + throw new QualifyingPropertiesIncorporationException("Cannot get the referenced SignedProperties", ex); + } + } + + /** + * Checks if QualifyingProperties references provided Signature + * + * @param signatureId + * @param qualifyingPropsElem + * @throws QualifyingPropertiesIncorporationException + */ + public static void checkQualifyingPropertiesTarget(String signatureId, Element qualifyingPropsElem) + throws QualifyingPropertiesIncorporationException { + Node targetAttr = qualifyingPropsElem.getAttributeNodeNS(null, QualifyingProperty.TARGET_ATTR); + if (null == targetAttr) { + targetAttr = qualifyingPropsElem.getAttributeNodeNS(QualifyingProperty.XADES_XMLNS, + QualifyingProperty.TARGET_ATTR); + if (null == targetAttr) { + throw new QualifyingPropertiesIncorporationException( + "QualifyingProperties Target attribute not present"); + } + } + String targetValue = targetAttr.getNodeValue(); + if (null == targetValue || !targetValue.startsWith("#") || !targetValue.substring(1).equals(signatureId)) { + throw new QualifyingPropertiesIncorporationException( + "QualifyingProperties target doesn't match the signature's Id"); + } + } } diff --git a/src/main/java/xades4j/verification/SignerRoleVerifier.java b/src/main/java/xades4j/verification/SignerRoleVerifier.java index 31597caf..fbb59c57 100644 --- a/src/main/java/xades4j/verification/SignerRoleVerifier.java +++ b/src/main/java/xades4j/verification/SignerRoleVerifier.java @@ -16,6 +16,8 @@ */ package xades4j.verification; +import org.w3c.dom.Element; + import xades4j.properties.QualifyingProperty; import xades4j.properties.SignerRoleProperty; import xades4j.properties.data.SignerRoleData; @@ -33,4 +35,12 @@ public QualifyingProperty verify( { return new SignerRoleProperty(propData.getClaimedRoles()); } + + @Override + public QualifyingProperty verify(SignerRoleData propData, Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/main/java/xades4j/verification/SigningCertificateVerifier.java b/src/main/java/xades4j/verification/SigningCertificateVerifier.java index 308e3e25..41253131 100644 --- a/src/main/java/xades4j/verification/SigningCertificateVerifier.java +++ b/src/main/java/xades4j/verification/SigningCertificateVerifier.java @@ -21,9 +21,13 @@ import java.util.Collection; import java.util.Iterator; import javax.security.auth.x500.X500Principal; + +import org.w3c.dom.Element; + import xades4j.properties.QualifyingProperty; import xades4j.properties.SigningCertificateProperty; import xades4j.properties.data.CertRef; +import xades4j.providers.CertificateValidationProvider; import xades4j.providers.MessageDigestEngineProvider; import xades4j.properties.data.SigningCertificateData; import xades4j.verification.QualifyingPropertyVerificationContext.CertificationChainData; @@ -38,7 +42,8 @@ class SigningCertificateVerifier implements QualifyingPropertyVerifier. + */ +package xades4j.verification; + +public class TimeStampValidationDataVerificationException extends + InvalidPropertyException +{ + private static final long serialVersionUID = -5708135575194239088L; + + public TimeStampValidationDataVerificationException(Throwable ex) + { + super(ex); + } + + @Override + protected String getVerificationMessage() + { + return "Can't verify TimeStamp validation data"; + } + + @Override + public String getPropertyName() + { + return "TimeStampValidationData"; + } + +} diff --git a/src/main/java/xades4j/verification/TimeStampValidationDataVerifier.java b/src/main/java/xades4j/verification/TimeStampValidationDataVerifier.java new file mode 100644 index 00000000..07812069 --- /dev/null +++ b/src/main/java/xades4j/verification/TimeStampValidationDataVerifier.java @@ -0,0 +1,103 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Element; + +import xades4j.properties.QualifyingProperty; +import xades4j.properties.TimeStampValidationDataProperty; +import xades4j.properties.data.TimeStampValidationDataData; + +public class TimeStampValidationDataVerifier implements + QualifyingPropertyVerifier +{ + @Override + public QualifyingProperty verify(TimeStampValidationDataData propData, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + Collection rawCertificateData = propData.getCertificateData(); + Collection rawRevocationData = propData.getCRLData(); + + CertificateFactory certFactory; + try + { + certFactory = CertificateFactory.getInstance("X509"); + } catch (CertificateException e) + { + throw new TimeStampValidationDataVerificationException(e); + } + + List certificates = new ArrayList(); + for (byte[] cert : rawCertificateData) + { + InputStream inStream = new ByteArrayInputStream(cert); + try + { + certificates.add((X509Certificate) certFactory.generateCertificate(inStream)); + } catch (CertificateException e) + { + throw new TimeStampValidationDataVerificationException(e); + } + } + + List crls = new ArrayList(); + for (byte[] crl : rawRevocationData) + { + InputStream inStream = new ByteArrayInputStream(crl); + try + { + crls.add((X509CRL) certFactory.generateCRL(inStream)); + } catch (CRLException e) + { + throw new TimeStampValidationDataVerificationException(e); + } + } + + addValidationDataToValidationProvider(certificates, crls, ctx); + + return new TimeStampValidationDataProperty(certificates, crls); + } + + private void addValidationDataToValidationProvider( + List certificates, List crls, + QualifyingPropertyVerificationContext ctx) + { + ctx.addAttributeCertificates(certificates); + ctx.addAttributeCRLs(crls); + } + + @Override + public QualifyingProperty verify(TimeStampValidationDataData propData, + Element elem, QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } + +} diff --git a/src/main/java/xades4j/verification/TimeStampVerifierBase.java b/src/main/java/xades4j/verification/TimeStampVerifierBase.java index b1532f71..66953bb4 100644 --- a/src/main/java/xades4j/verification/TimeStampVerifierBase.java +++ b/src/main/java/xades4j/verification/TimeStampVerifierBase.java @@ -16,16 +16,19 @@ */ package xades4j.verification; -import java.lang.reflect.Method; -import java.util.Date; import java.util.List; + +import org.w3c.dom.Element; + import xades4j.UnsupportedAlgorithmException; +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.properties.QualifyingProperty; import xades4j.properties.data.BaseXAdESTimeStampData; import xades4j.providers.TimeStampTokenDigestException; import xades4j.providers.TimeStampTokenSignatureException; import xades4j.providers.TimeStampTokenStructureException; import xades4j.providers.TimeStampTokenVerificationException; +import xades4j.providers.TimeStampVerificationData; import xades4j.providers.TimeStampVerificationProvider; import xades4j.utils.CannotAddDataToDigestInputException; import xades4j.utils.TimeStampDigestInput; @@ -52,28 +55,36 @@ public TimeStampVerifierBase(TimeStampVerificationProvider tsVerifier, TimeStamp @Override public final QualifyingProperty verify( TData propData, - QualifyingPropertyVerificationContext ctx) throws InvalidPropertyException - { + Element elem, + QualifyingPropertyVerificationContext ctx) throws InvalidPropertyException + { try { TimeStampDigestInput digestInput = this.tsInputFactory.newTimeStampDigestInput(propData.getCanonicalizationAlgorithm()); - QualifyingProperty prop = addPropSpecificTimeStampInputAndCreateProperty(propData, digestInput, ctx); + BaseXAdESTimeStampProperty prop = addPropSpecificTimeStampInputAndCreateProperty( + propData, + elem, + digestInput, + ctx); byte[] data = digestInput.getBytes(); /** * Verify the time-stamp tokens on a time-stamp property data object. All * the tokens are verified, but the returned time-stamp is from the last token. */ List tokens = propData.getTimeStampTokens(); - Date ts = null; + TimeStampVerificationData tsVerData = null; for (byte[] tkn : tokens) { - ts = this.tsVerifier.verifyToken(tkn, data); + tsVerData = this.tsVerifier.verifyToken(tkn, data, ctx); } - // By convention all timestamp property types have a setTime(Date) method - Method setTimeMethod = prop.getClass().getMethod("setTime", Date.class); - setTimeMethod.invoke(prop, ts); + prop.setTime(tsVerData.getTimeStampTokenTime()); + prop.setValidationData(tsVerData.getValidationData()); + + // should be a noop, only ArchiveTimeStamp should use it to change the + // verification time for all subsequent TimeStamps + updateContextAfterVerification(prop,ctx); return prop; } catch(UnsupportedAlgorithmException ex) @@ -95,11 +106,24 @@ public final QualifyingProperty verify( } } - protected abstract QualifyingProperty addPropSpecificTimeStampInputAndCreateProperty( + @Override + public final QualifyingProperty verify( + TData propData, + QualifyingPropertyVerificationContext ctx) throws InvalidPropertyException + { + return verify(propData, null, ctx); + } + + protected abstract BaseXAdESTimeStampProperty addPropSpecificTimeStampInputAndCreateProperty( TData propData, + Element location, TimeStampDigestInput digestInput, QualifyingPropertyVerificationContext ctx) throws CannotAddDataToDigestInputException, TimeStampVerificationException; + protected abstract void updateContextAfterVerification( + QualifyingProperty prop, + QualifyingPropertyVerificationContext ctx); + private static TimeStampVerificationException getEx( final Exception ex, String propName) @@ -121,6 +145,8 @@ private static TimeStampVerificationException getEx( return new TimeStampVerificationException(propName, ex) { + private static final long serialVersionUID = 1L; + @Override protected String getVerificationMessage() { diff --git a/src/main/java/xades4j/verification/XAdESForm.java b/src/main/java/xades4j/verification/XAdESForm.java index 46a07a8a..d8b01192 100644 --- a/src/main/java/xades4j/verification/XAdESForm.java +++ b/src/main/java/xades4j/verification/XAdESForm.java @@ -28,7 +28,8 @@ public enum XAdESForm C("C", "Electronic signature with complete validation data references"), X("X", "Extended signatures with time forms"), X_L("X-L", "Extended long electronic signatures with time"), - A("A", "Archival electronic signatures"); + A("A", "Archival electronic signatures"), + A_VD("A-VD", "Archival electronic signature with validation data"); /**/ private final String alias, fullName; diff --git a/src/main/java/xades4j/verification/XAdESFormChecker.java b/src/main/java/xades4j/verification/XAdESFormChecker.java index 0e7f462d..dcb025bd 100644 --- a/src/main/java/xades4j/verification/XAdESFormChecker.java +++ b/src/main/java/xades4j/verification/XAdESFormChecker.java @@ -28,6 +28,7 @@ import xades4j.properties.SignaturePolicyBase; import xades4j.properties.SignatureTimeStampProperty; import xades4j.properties.SigningCertificateProperty; +import xades4j.properties.TimeStampValidationDataProperty; /** * @@ -47,7 +48,7 @@ static XAdESForm checkForm(Collection props) throws InvalidXAdESFo availablePropsNames.add(propInfo.getProperty().getName()); } - XAdESFormDesc formDesc = XADES_C_DESC; + XAdESFormDesc formDesc = XADES_A_DESC; do { if (formDesc.check(availablePropsNames)) @@ -69,7 +70,8 @@ static XAdESForm checkForm(Collection props) throws InvalidXAdESFo 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(); + XADES_X_L_DESC = new XAdES_X_L_Desc(), + XADES_A_DESC = new XAdES_A_Desc(); /**************************************************************************/ /**/ @@ -280,7 +282,8 @@ public XAdES_A_Desc() @Override protected boolean checkProps(Set availablePropsNames) throws InvalidXAdESFormException { - return availablePropsNames.contains(ArchiveTimeStampProperty.PROP_NAME); + return availablePropsNames.contains(ArchiveTimeStampProperty.PROP_NAME) || + availablePropsNames.contains(TimeStampValidationDataProperty.PROP_NAME); } @Override diff --git a/src/main/java/xades4j/verification/XAdESVerificationResult.java b/src/main/java/xades4j/verification/XAdESVerificationResult.java index 635dca5f..c0b18389 100644 --- a/src/main/java/xades4j/verification/XAdESVerificationResult.java +++ b/src/main/java/xades4j/verification/XAdESVerificationResult.java @@ -19,6 +19,8 @@ import java.security.cert.X509Certificate; import java.util.Collection; import org.apache.xml.security.signature.XMLSignature; + +import xades4j.properties.BaseXAdESTimeStampProperty; import xades4j.properties.QualifyingProperties; import xades4j.properties.QualifyingProperty; import xades4j.properties.SignedDataObjectProperty; @@ -48,11 +50,13 @@ public class XAdESVerificationResult /**/ private final DataGetter propertiesGetter; private final QualifyingProperties qualifyingProperties; + private final Collection attributeValidationData; XAdESVerificationResult( XAdESForm signatureForm, XMLSignature xmlSignature, ValidationData validationData, + Collection attributeValidationData, Collection properties, Collection signedDataObjects) { @@ -64,6 +68,8 @@ public class XAdESVerificationResult this.propertiesGetter = createPropsGetter(properties); this.qualifyingProperties = createQualifProps(); + + this.attributeValidationData = attributeValidationData; } private DataGetter createPropsGetter( @@ -98,28 +104,41 @@ private QualifyingProperties createQualifProps() /**/ /**/ + /** + * @return the form of signature (BES, EPES, T, C, X, X-L, A) + */ public XAdESForm getSignatureForm() { return signatureForm; } + /** + * @return the validated basic XMLDsig object + */ public XMLSignature getXmlSignature() { return xmlSignature; } + /** + * @return the signature algorithm used by the basic XML digital signature + */ public String getSignatureAlgorithmUri() { return xmlSignature.getSignedInfo().getSignatureMethodURI(); } + /** + * the canonicalization algorithm used by the basic XML digital signature + * @return + */ public String getCanonicalizationAlgorithmUri() { return xmlSignature.getSignedInfo().getCanonicalizationMethodURI(); } /** - * Gets the certificates and CRLs used to verify the signature. + * Gets all the certificates and CRLs used to verify the basic signature. * @return the validation data */ public ValidationData getValidationData() @@ -174,4 +193,17 @@ public Collection getSignedDataObjects() { return signedDataObjects; } + + /** + * Returns all validation data (certs and CRLs) used to verify time stamps. + *

+ * If you're interested in validation data of single property, all time stamp + * property objects implement {@link BaseXAdESTimeStampProperty} interface, which + * defines the {@code getValidationData()} method. + * @return + */ + public Collection getAttributeValidationData() + { + return attributeValidationData; + } } diff --git a/src/main/java/xades4j/verification/XadesHybridVerifierImpl.java b/src/main/java/xades4j/verification/XadesHybridVerifierImpl.java new file mode 100644 index 00000000..b1d027e5 --- /dev/null +++ b/src/main/java/xades4j/verification/XadesHybridVerifierImpl.java @@ -0,0 +1,531 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.InputStream; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.signature.Reference; +import org.apache.xml.security.signature.SignedInfo; +import org.apache.xml.security.signature.XMLSignature; +import org.apache.xml.security.signature.XMLSignatureException; +import org.apache.xml.security.utils.resolver.ResourceResolver; +import org.apache.xml.security.utils.resolver.implementations.ResolverAnonymous; +import org.w3c.dom.Element; + +import com.google.inject.Inject; + +import xades4j.XAdES4jException; +import xades4j.XAdES4jXMLSigException; +import xades4j.production.XadesSignatureFormatExtender; +import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.SignatureTimeStampProperty; +import xades4j.properties.UnsignedProperties; +import xades4j.properties.UnsignedSignatureProperty; +import xades4j.providers.CertificateValidationProvider; +import xades4j.providers.ValidationData; +import xades4j.utils.CollectionUtils; +import xades4j.utils.PropertiesUtils; +import xades4j.verification.RawSignatureVerifier.RawSignatureVerifierContext; +import xades4j.verification.SignatureUtils.KeyInfoRes; +import xades4j.verification.SignatureUtils.ReferencesRes; +import xades4j.xml.unmarshalling.QualifyingPropertiesUnmarshaller; +import xades4j.xml.unmarshalling.UnmarshalException; + +public class XadesHybridVerifierImpl implements XadesVerifier +{ + static + { + org.apache.xml.security.Init.init(); + initFormExtension(); + } + private final CertificateValidationProvider certificateValidator; + private final QualifyingPropertiesVerifier qualifyingPropertiesVerifier; + private final Set rawSigVerifiers; + private final Set customSigVerifiers; + private QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller; + private boolean secureValidation; + + @Inject + protected XadesHybridVerifierImpl( + CertificateValidationProvider certificateValidator, + QualifyingPropertiesVerifier qualifyingPropertiesVerifier, + QualifyingPropertiesUnmarshaller qualifyingPropsUnmarshaller, + Set rawSigVerifiers, + Set customSigVerifiers) + { + this.certificateValidator = certificateValidator; + this.qualifyingPropertiesVerifier = qualifyingPropertiesVerifier; + this.qualifPropsUnmarshaller = qualifyingPropsUnmarshaller; + this.rawSigVerifiers = rawSigVerifiers; + this.customSigVerifiers = customSigVerifiers; + this.secureValidation = false; + } + + @Override + public XAdESVerificationResult verify(Element signatureElem, + SignatureSpecificVerificationOptions verificationOptions) + throws XAdES4jException + { + if (signatureElem == null) { + throw new NullPointerException("Signature node not specified"); + } + + if (verificationOptions == null) + { + verificationOptions = SignatureSpecificVerificationOptions.empty(); + } + + /* + * Unmarshal XMLdsig (basic XML signature) + */ + XMLSignature signature; + try + { + signature = new XMLSignature(signatureElem, verificationOptions.getBaseUri(), this.secureValidation); + } catch (XMLSecurityException ex) + { + throw new UnmarshalException("Bad XML signature", ex); + } + + // XMLDsig doesn't require Id, but XAdES does + String signatureId = signature.getId(); + if (null == signatureId) + { + throw new UnmarshalException("XML signature doesn't have an Id"); + } + + // extract references to SignedProperties element and signed data objects + ReferencesRes referencesRes = SignatureUtils.processReferences(signature); + + /* + * Apply early verifiers + */ + RawSignatureVerifierContext rawCtx = new RawSignatureVerifierContext(signature); + for (RawSignatureVerifier rawSignatureVerifier : this.rawSigVerifiers) + { + rawSignatureVerifier.verify(rawCtx); + } + + /* + * Get the QualifyingProperties element and check if it's properly embedded in + * signature (Signature references it and vice versa) + */ + Element qualifyingPropsElem = + SignatureUtils.getQualifyingPropertiesElement(signature); + SignatureUtils.checkSignedPropertiesIncorporation( + qualifyingPropsElem, + referencesRes.signedPropsReference); + SignatureUtils.checkQualifyingPropertiesTarget(signatureId, qualifyingPropsElem); + + /* Unmarshal the qualifying (XAdES, both signed and unsigned) properties */ + HybridQualifPropsDataCollectorImpl propsDataCollector = + new HybridQualifPropsDataCollectorImpl(); + qualifPropsUnmarshaller.unmarshalProperties(qualifyingPropsElem, propsDataCollector); + + /* + * extract data that uniquely identifies the key and/or certificate used for + * Signature signing from basic XML signature (XMLdsig) + */ + KeyInfoRes keyInfoRes = SignatureUtils.processKeyInfo(signature.getKeyInfo()); + + /* + * Create the object which the property verifiers will use to get and save + * the status (context) of verification + */ + QualifyingPropertyVerificationContext qPropsCtx = new QualifyingPropertyVerificationContext( + signature, + keyInfoRes, + /**/ + new QualifyingPropertyVerificationContext.SignedObjectsData( + referencesRes.dataObjsReferences, + signature), + verificationOptions.getDefaultVerificationDate()); + + /* + * go over all qualified properties in reverse order, verify the properties, + * ignore invalid and return only successfully verified. + * Data structure verification is included. + * + * This is the first verification that ignores certificate and CRL references + * (as we know which certificates are needed and which CRLs are used only after + * verification of Signature) + */ + List props = + this.qualifyingPropertiesVerifier.verifyProperties(propsDataCollector, qPropsCtx); + + /* create certification path */ + Date validationDate = getValidationDate(props, verificationOptions); + this.certificateValidator.addCertificates(qPropsCtx.getSignatureCertificates(), + validationDate); + this.certificateValidator.addCRLs(qPropsCtx.getSignatureCRLs(), validationDate); + + ValidationData certValidationRes = this.certificateValidator.validate( + keyInfoRes.certSelector, + validationDate, + keyInfoRes.keyInfoCerts); + + if (null == certValidationRes || certValidationRes.getCerts().isEmpty()) + { + throw new NullPointerException("Certificate validator returned null or empty data"); + } + X509Certificate validationCert = certValidationRes.getCerts().get(0); + + /* Signature verification */ + + // Core XML-DSIG verification. + doCoreVerification(signature, verificationOptions, validationCert); + + + if (null == certValidationRes || certValidationRes.getCerts().isEmpty()) + { + throw new NullPointerException("Certificate validator returned null or empty data"); + } + + // perform verification of references to certificates and CRLs (revocation data) + qPropsCtx.setCertificationChainData( + new QualifyingPropertyVerificationContext.CertificationChainData( + certValidationRes.getCerts(), + certValidationRes.getCrls(), + keyInfoRes.issuerSerial)); + props = this.qualifyingPropertiesVerifier.verifyProperties(propsDataCollector, qPropsCtx, props); + + XAdESVerificationResult res = new XAdESVerificationResult( + XAdESFormChecker.checkForm(props), + signature, + certValidationRes, + qPropsCtx.getAttributeValidationData(), + props, + referencesRes.dataObjsReferences); + + // Apply the custom signature verifiers. + for (CustomSignatureVerifier customVer : this.customSigVerifiers) + { + customVer.verify(res, qPropsCtx); + } + + return res; + } + + @Override + public XAdESVerificationResult verify(Element signatureElem, + SignatureSpecificVerificationOptions verificationOptions, + XadesSignatureFormatExtender formatExtender, XAdESForm finalForm) + throws XAdES4jException + { + if (null == finalForm || null == formatExtender) { + throw new NullPointerException("'finalForm' and 'formatExtender' cannot be null"); + } + + // The transitions matrix won't allow this, but this way I avoid the + // unnecessary processing. + if (finalForm.before(XAdESForm.T) || finalForm.after(XAdESForm.A_VD)) + { + throw new IllegalArgumentException("Signature format can only be extended to XAdES-T or above"); + } + + XAdESVerificationResult res = this.verify(signatureElem, verificationOptions); + XAdESForm actualForm = res.getSignatureForm(); + + if (!finalForm.before(actualForm)) + { + // Valid form transitions: + // * BES/EPES -> T + // * BES/EPES -> C + // * T -> T + // * T -> C + // * C -> X + // * C -> X-L + // * X -> X + // * X -> X-L + // * X-L -> A + // * A -> A + // * A -> A-VD (A-VD is not a real form, it's used to tell library to create + // TimesStampValidationData element) + + FormExtensionPropsCollector finalFormPropsColector = formsExtensionTransitions[actualForm.ordinal()][finalForm.ordinal()]; + + if (null == finalFormPropsColector) + { + throw new InvalidFormExtensionException(actualForm, finalForm); + } + + Collection usp = new ArrayList(3); + finalFormPropsColector.addProps(usp, res); + + formatExtender.enrichSignature(res.getXmlSignature(), new UnsignedProperties(usp)); + } + return res; + } + + private static interface FormExtensionPropsCollector { + + void addProps(Collection usp, + XAdESVerificationResult res); + } + private static FormExtensionPropsCollector[][] formsExtensionTransitions; + + private static void initFormExtension() + { + XAdESForm[] forms = XAdESForm.values(); + formsExtensionTransitions = new FormExtensionPropsCollector[forms.length][forms.length]; + + // BES/EPES -> T + FormExtensionPropsCollector tPropsCol = new FormExtensionPropsCollector() + { + + @Override + public void addProps( + Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesTProperties(usp); + } + }; + formsExtensionTransitions[XAdESForm.BES.ordinal()][XAdESForm.T.ordinal()] = tPropsCol; + formsExtensionTransitions[XAdESForm.EPES.ordinal()][XAdESForm.T.ordinal()] = tPropsCol; + // T -> T + formsExtensionTransitions[XAdESForm.T.ordinal()][XAdESForm.T.ordinal()] = tPropsCol; + + // BES/EPES -> C + FormExtensionPropsCollector cAndTPropsCol = new FormExtensionPropsCollector() + { + + @Override + public void addProps( + Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesTProperties(usp); + PropertiesUtils.addXadesCProperties(usp, res.getValidationData()); + } + }; + formsExtensionTransitions[XAdESForm.BES.ordinal()][XAdESForm.C.ordinal()] = cAndTPropsCol; + formsExtensionTransitions[XAdESForm.EPES.ordinal()][XAdESForm.C.ordinal()] = cAndTPropsCol; + + // T -> C + FormExtensionPropsCollector cPropsCol = new FormExtensionPropsCollector() + { + + @Override + public void addProps( + Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesCProperties(usp, res.getValidationData()); + } + }; + formsExtensionTransitions[XAdESForm.T.ordinal()][XAdESForm.C.ordinal()] = cPropsCol; + + // C -> X + FormExtensionPropsCollector xPropsCol = new FormExtensionPropsCollector() + { + + @Override + public void addProps( + Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesXProperties(usp); + } + }; + formsExtensionTransitions[XAdESForm.C.ordinal()][XAdESForm.X.ordinal()] = xPropsCol; + // X -> X + formsExtensionTransitions[XAdESForm.X.ordinal()][XAdESForm.X.ordinal()] = xPropsCol; + + // C -> X-L + FormExtensionPropsCollector xlAndXPropsCol = new FormExtensionPropsCollector() + { + + @Override + public void addProps( + Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesXProperties(usp); + + Collection tTimeStampValidationData = + PropertiesUtils.extractTTimeStampValidationData(res); + + PropertiesUtils.addXadesXLProperties(usp, res.getValidationData(), + tTimeStampValidationData); + } + }; + formsExtensionTransitions[XAdESForm.C.ordinal()][XAdESForm.X_L.ordinal()] = xlAndXPropsCol; + + // X -> X-L + FormExtensionPropsCollector xlPropsCol = new FormExtensionPropsCollector() + { + @Override + public void addProps(Collection usp, + XAdESVerificationResult res) + { + Collection tTimeStampValidationData = + PropertiesUtils.extractTTimeStampValidationData(res); + + PropertiesUtils.addXadesXLProperties( + usp, + res.getValidationData(), + tTimeStampValidationData); + } + + }; + formsExtensionTransitions[XAdESForm.X.ordinal()][XAdESForm.X_L.ordinal()] = xlPropsCol; + + // X-L -> A + FormExtensionPropsCollector aPropsCol = new FormExtensionPropsCollector() + { + @Override + public void addProps(Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesAProperties(usp); + } + }; + formsExtensionTransitions[XAdESForm.X_L.ordinal()][XAdESForm.A.ordinal()] = aPropsCol; + // A -> A + formsExtensionTransitions[XAdESForm.A.ordinal()][XAdESForm.A.ordinal()] = aPropsCol; + + // A -> A-VD + FormExtensionPropsCollector avdPropsCol = new FormExtensionPropsCollector() + { + @Override + public void addProps(Collection usp, + XAdESVerificationResult res) + { + PropertiesUtils.addXadesAVDProperties(usp, res); + } + }; + formsExtensionTransitions[XAdESForm.A.ordinal()][XAdESForm.A_VD.ordinal()] = avdPropsCol; + } + + public void setAcceptUnknownProperties(boolean acceptUnknownProperties) + { + this.qualifPropsUnmarshaller.setAcceptUnknownProperties(acceptUnknownProperties); + } + + void setSecureValidation(boolean secureValidation) { + this.secureValidation = secureValidation; + } + + private static void doCoreVerification( + XMLSignature signature, + SignatureSpecificVerificationOptions verificationOptions, + X509Certificate validationCert) throws XAdES4jXMLSigException, InvalidSignatureException + { + List resolvers = verificationOptions.getResolvers(); + if(!CollectionUtils.nullOrEmpty(resolvers)) + { + for (ResourceResolver resolver : resolvers) + { + signature.addResourceResolver(resolver); + } + } + + InputStream nullURIReferenceData = verificationOptions.getDataForAnonymousReference(); + if (nullURIReferenceData != null) + { + signature.addResourceResolver(new ResolverAnonymous(nullURIReferenceData)); + } + + try + { + if (signature.checkSignatureValue(validationCert)) + { + return; + } + } + catch (XMLSignatureException ex) + { + throw new XAdES4jXMLSigException("Error verifying the signature", ex); + } + + try + { + /* Failure due to the signature value or references validation? */ + + if (signature.getSignedInfo().verifyReferences()) + // References are OK; this is a problem on the signature value + // itself. + { + throw new SignatureValueException(signature); + } else + { + // References are NOT OK; get the first invalid Reference. + SignedInfo si = signature.getSignedInfo(); + for (int i = 0; i < si.getLength(); i++) + { + Reference r = si.item(i); + if (!r.verify()) + { + throw new ReferenceValueException(signature, r); + } + } + } + } + catch (XMLSecurityException ex) + { + throw new XAdES4jXMLSigException("Error verifying the references", ex); + } + } + + private Date getValidationDate( + List props, SignatureSpecificVerificationOptions verificationOptions) + throws XAdES4jException { + Date earliestDate = null; + for (PropertyInfo p : props) + { + QualifyingProperty qp = p.getProperty(); + if (qp instanceof SignatureTimeStampProperty) + { + Date timeStampDate = ((SignatureTimeStampProperty)qp).getTime(); + if (earliestDate == null) + earliestDate = timeStampDate; + else if (earliestDate.getTime() > timeStampDate.getTime()) + earliestDate = timeStampDate; + } else if (qp instanceof SigAndRefsTimeStampProperty) + { + Date timeStampDate = ((SigAndRefsTimeStampProperty)qp).getTime(); + if (earliestDate == null) + earliestDate = timeStampDate; + else if (earliestDate.getTime() > timeStampDate.getTime()) + earliestDate = timeStampDate; + } else if (qp instanceof ArchiveTimeStampProperty) + { + Date timeStampDate = ((ArchiveTimeStampProperty)qp).getTime(); + if (earliestDate == null) + earliestDate = timeStampDate; + else if (earliestDate.getTime() > timeStampDate.getTime()) + earliestDate = timeStampDate; + } + } + if (earliestDate == null) { + earliestDate = verificationOptions.getDefaultVerificationDate(); + } + + return earliestDate; + } + +} diff --git a/src/main/java/xades4j/verification/XadesVerificationProfile.java b/src/main/java/xades4j/verification/XadesVerificationProfile.java index 22df7444..c0ecad7d 100644 --- a/src/main/java/xades4j/verification/XadesVerificationProfile.java +++ b/src/main/java/xades4j/verification/XadesVerificationProfile.java @@ -25,6 +25,7 @@ import xades4j.providers.CertificateValidationProvider; import xades4j.providers.MessageDigestEngineProvider; import xades4j.providers.SignaturePolicyDocumentProvider; +import xades4j.providers.TSACertificateValidationProvider; import xades4j.providers.TimeStampVerificationProvider; import xades4j.utils.UtilsBindingsModule; import xades4j.xml.marshalling.algorithms.AlgorithmParametersBindingsModule; @@ -39,7 +40,8 @@ * verify signatures using the configured components. *

* The minimum configuration is a {@link xades4j.providers.CertificateValidationProvider} - * because the validation data (trust-anchors, CRLs, etc) has to be properly selected. All the other components + * because the validation data (trust-anchors, CRLs, etc) has to be properly selected. + * All the other components * have default implementations that are used if no other actions are taken. However, * all of them can be replaced through the corresponding methods, either by an instance * or a class. When a class is used it may have dependencies on other components, @@ -72,9 +74,14 @@ private XadesVerificationProfile() this.profileCore = new XadesProfileCore(); this.acceptUnknownProperties = false; this.secureValidation = false; - withBinding(XadesVerifier.class, XadesVerifierImpl.class); + withBinding(XadesVerifier.class, XadesHybridVerifierImpl.class); } + /** + * Certificate validation profile to be used only with signatures lacking time stamps + * + * @param certificateValidationProvider + */ public XadesVerificationProfile( CertificateValidationProvider certificateValidationProvider) { @@ -82,6 +89,24 @@ public XadesVerificationProfile( withBinding(CertificateValidationProvider.class, certificateValidationProvider); } + /** + * Certificate validation profile that can be used to validate both signature and + * time stamps in XML Advanced Electronic Signatures. + * + * @param certificateValidationProvider validation data and provider to be used for + * validation Signature only + * @param tsaCertificateValidationProvider validation data and provider to be used for + * validation of time stamps in signature + */ + public XadesVerificationProfile( + CertificateValidationProvider certificateValidationProvider, + TSACertificateValidationProvider tsaCertificateValidationProvider) + { + this(); + withBinding(CertificateValidationProvider.class, certificateValidationProvider); + withBinding(TSACertificateValidationProvider.class, tsaCertificateValidationProvider); + } + public XadesVerificationProfile( Class certificateValidationProviderClass) { @@ -89,6 +114,15 @@ public XadesVerificationProfile( withBinding(CertificateValidationProvider.class, certificateValidationProviderClass); } + public XadesVerificationProfile( + Class certificateValidationProviderClass, + Class tsaCertificateValProvClass) + { + this(); + withBinding(CertificateValidationProvider.class, certificateValidationProviderClass); + withBinding(TSACertificateValidationProvider.class, tsaCertificateValProvClass); + } + /** * Adds a type dependency mapping to the profile. This is typically done from an * interface to a type that implements that interface. When a dependency to @@ -142,10 +176,11 @@ public XadesVerificationProfile withBinding( * not be affected. Other verifiers can be created, accumulating the profile changes. * @return a {@code XadesVerifier} accordingly to this profile. * @throws XadesProfileResolutionException if the dependencies of the signer (direct and indirect) cannot be resolved + * @see XadesVerifier */ public final XadesVerifier newVerifier() throws XadesProfileResolutionException { - XadesVerifierImpl v = profileCore.getInstance(XadesVerifierImpl.class, overridableModules, sealedModules); + XadesHybridVerifierImpl v = profileCore.getInstance(XadesHybridVerifierImpl.class, overridableModules, sealedModules); v.setAcceptUnknownProperties(acceptUnknownProperties); v.setSecureValidation(secureValidation); return v; @@ -307,7 +342,7 @@ public XadesVerificationProfile withCustomSignatureVerifier( } public XadesVerificationProfile withElementVerifier( - QName elemName, Class vClass) + QName elemName, Class> vClass) { if (null == elemName || null == vClass) throw new NullPointerException(); diff --git a/src/main/java/xades4j/verification/XadesVerifier.java b/src/main/java/xades4j/verification/XadesVerifier.java index 742dea0b..7e96a584 100644 --- a/src/main/java/xades4j/verification/XadesVerifier.java +++ b/src/main/java/xades4j/verification/XadesVerifier.java @@ -39,30 +39,51 @@ * handling by catching exceptions in the different branches/depths of the tree. *

* With its default configuration the library supports verification of signatures - * up to XAdES-C. The format can be extended after verification through the {@link #verify(org.w3c.dom.Element, xades4j.verification.SignatureSpecificVerificationOptions, xades4j.production.XadesSignatureFormatExtender, xades4j.verification.XAdESForm) verify} - * method, even though extended formats cannot be validated afterwards. + * up to and including XAdES-A. The format can be extended after verification through the + * {@link #verify(org.w3c.dom.Element, xades4j.verification.SignatureSpecificVerificationOptions, xades4j.production.XadesSignatureFormatExtender, xades4j.verification.XAdESForm) verify} + * method. * @see XadesVerificationProfile * @author Luís */ public interface XadesVerifier { /** - * Verifies a signature. - * @param signatureElem the element containing the signature; must have an Id + * Verifies a signature. Checks if it is correctly formed, if required properties are + * present for the detected form, etc. + *

+ * It does not check whatever: + *

    + *
  • the complete signature is as as minimal as possible (there is no duplication + * of certificates or CRLs)
  • + *
  • grace periods for signature and subsequent TimeStamps are preserved
  • + *
+ *

+ * Limitations:
XAdES-C won't include the {@code AttributeCertificateRefs} + * and {@code AttributeRevocationRefs} properties. The library can't verify + * signatures that use OCSP responses for revocation information source. + * The library can't verify XAdES-X type 2 documents (the + * {@code RefsOnlyTimeStamp} property).

+ *
+ * @param signatureElem the element containing the signature; must have an {@code Id} + * attribute * @param verificationOptions signature verification options. If {@code null}, - * default options are used - * @return the verification result + * default options are used, see: {@link SignatureSpecificVerificationOptions} + * @return the verification result (if verification successful) * * @see xades4j.verification.SignatureSpecificVerificationOptions - * @throws XAdES4jException if an error occurs, including if signature verification fails + * @throws XAdES4jException if during verification a critical error occurs, causing + * signature verification failure. Failure to validate single element if there + * are other elements able to provide unbroken chain of time stamps will + * not cause an Exception to be thrown. * @throws NullPointerException if {@code signatureElem} is {@code null} + * @see XadesVerifier */ public XAdESVerificationResult verify( Element signatureElem, SignatureSpecificVerificationOptions verificationOptions) throws XAdES4jException; /** - * Verifies a signature and extends its format if needed. + * Verifies a signature and extends its format. *

* Note that, due to the library's internal design, the properties being added * to a signature cannot have dependencies on each other because the XML for @@ -70,34 +91,68 @@ public XAdESVerificationResult verify( * all the data needed to the properties. For instance, it's not possible to * correctly add properties from XAdES-C and XAdES-X at the same time, as the * last need the first's XML structure. This imposes some restrictions on the - * format extensions. Valid transitions are (actual signature form -> form - * after extension): + * format extensions. To work around this limitation you can first extend the form to + * XAdES-C form and then to XAdES-X form. + *

+ * Valid transitions are (actual signature form -> form after extension): *

    *
  • BES/EPES -> T
  • *
  • BES/EPES -> C
  • + *
  • T -> T
  • *
  • T -> C
  • + *
  • T -> X-L (not supported)
  • *
  • C -> X
  • *
  • C -> X-L
  • - *
  • X -> X-L (not supported by default because X cannot be verified)
  • - *
  • X-L -> A (not supported by default because X-L cannot be verified)
  • + *
  • X -> X
  • + *
  • X -> X-L
  • + *
  • X-L -> A
  • + *
  • A -> A
  • + *
  • A -> A-VD
  • *
+ * Note: {@code A-VD} form is an abstract form, that's used inside this library + * to notify the extender that we want to add {@code TimeStampVerificationData} + * property. TimeStampVerificationData is used to contain data needed to validate + * previous TimeStamps. Validation of A-VD form will return the A form so + * subsequent A-TimeStamping of A-VD form is possible.

+ *

+ * Caution: While the library allows you to extend BES form to A form with + * validation data in matter of seconds, this process may not create actually valid + * signature. This is caused by the fact that law in some countries (and general good + * practice) dictates presence of grace period (See: ETSI TS 101 903, "XAdES standard" + * v. 1.4.2, section 4.4.3.2, NOTE 4). This poses time limits on signature form + * extension. For example, if the grace period is 1h, you may not extend T form to + * C form earlier than 1h after T form creation (it may be longer if you're unable + * to obtain CRL published 1h after T form creation). Same issue exists when + * extending from T form to X-L form, from X form to X-L form and from A form to + * A-VD form. The lack of actual enforcement of this is caused by lack of support + * for grace period in underlying interface (Java crypto API) and used cryptographic + * library (Bouncy Castle). + *

+ *

* Note that the {@code XadesSignatureFormatExtender} can also be used separately, - * but no checks are made to ensure that the signature has the appropriate - * properties (form) to be extended with other properties. This can be used - * to created XAdES-A. + * but then no checks are made to ensure that the signature has the appropriate + * properties (form) to be extended with other properties. *

* The generated XAdES-X is type 1, with one {@code SigAndRefsTimeStamp} property. *

- * Limitations: XAdES-C won't include the {@code AttributeCertificateRefs} - * and {@code AttributeRevocationRefs} properties. XAdES-X-L won't include the - * {@code AttrAuthoritiesCertValues} and {@code AttributeRevocationValues} properties. + * Limitations: + *

    + *
  • XAdES-C won't include the optional {@code AttributeCertificateRefs} + * and {@code AttributeRevocationRefs} properties
  • + *
  • library can't verify or create signatures that use OCSP responses for + * revocation information
  • + *
  • library can't verify XAdES-X type 2 or documents that build on it + * (the {@code RefsOnlyTimeStamp} property) + *
  • + *
* * @param signatureElem the element containing the signature; must have an Id * @param verificationOptions signature verification options. If {@code null}, * default options are used * @param formatExtender the extender used to add the new unsigned properties * @param minForm the minimum format that the signature should have; if the - * original signature has a 'lower' format, the extender is used + * original signature has a 'lower' format (as per list above), the extender + * is used * @return the verification result * * @see xades4j.production.XadesFormatExtenderProfile diff --git a/src/main/java/xades4j/verification/XadesVerifierImpl.java b/src/main/java/xades4j/verification/XadesVerifierImpl.java index cf8b0eb3..be6e964b 100644 --- a/src/main/java/xades4j/verification/XadesVerifierImpl.java +++ b/src/main/java/xades4j/verification/XadesVerifierImpl.java @@ -34,11 +34,16 @@ import org.apache.xml.security.utils.resolver.implementations.ResolverAnonymous; import org.w3c.dom.Element; import org.w3c.dom.Node; + +import xades4j.properties.CertificateValuesProperty; import xades4j.properties.QualifyingProperty; +import xades4j.properties.RevocationValuesProperty; import xades4j.properties.UnsignedSignatureProperty; import xades4j.XAdES4jException; import xades4j.XAdES4jXMLSigException; +import xades4j.properties.data.CertificateValuesData; import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.RevocationValuesData; import xades4j.properties.UnsignedProperties; import xades4j.production.XadesSignatureFormatExtender; import xades4j.properties.SignatureTimeStampProperty; @@ -116,7 +121,7 @@ public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVe if (null == verificationOptions) { - verificationOptions = SignatureSpecificVerificationOptions.empty; + verificationOptions = SignatureSpecificVerificationOptions.empty(); } /* Unmarshal the signature */ @@ -175,6 +180,12 @@ public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVe qualifPropsUnmarshaller.unmarshalProperties(qualifyingPropsElem, propsDataCollector); Collection qualifPropsData = propsDataCollector.getPropertiesData(); + /* Read certificates and revocation values from extended forms */ + Collection crls = getRevocationValues(qualifPropsData, signature); + Collection otherCerts = getCertificateValues(qualifPropsData, signature); + this.certificateValidator.addCRLs(crls, new Date()); + this.certificateValidator.addCertificates(otherCerts, new Date()); + /* Certification path */ KeyInfoRes keyInfoRes = SignatureUtils.processKeyInfo(signature.getKeyInfo()); @@ -204,15 +215,17 @@ public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVe /**/ new QualifyingPropertyVerificationContext.SignedObjectsData( referencesRes.dataObjsReferences, - signature)); + signature), + new Date()); // Verify the properties. Data structure verification is included. - Collection props = this.qualifyingPropertiesVerifier.verifyProperties(qualifPropsData, qPropsCtx); + Collection props = this.qualifyingPropertiesVerifier.verifyProperties(propsDataCollector, qPropsCtx); XAdESVerificationResult res = new XAdESVerificationResult( XAdESFormChecker.checkForm(props), signature, certValidationRes, + qPropsCtx.getAttributeValidationData(), props, referencesRes.dataObjsReferences); @@ -224,7 +237,7 @@ public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVe return res; } - + /*************************************************************************************/ private Date getValidationDate( @@ -253,13 +266,77 @@ private Date getValidationDate( /**/ new QualifyingPropertyVerificationContext.SignedObjectsData( new ArrayList(0), - signature)); + signature), + new Date()); Collection props = this.qualifyingPropertiesVerifier.verifyProperties(sigTsData, ctx); QualifyingProperty sigTs = props.iterator().next().getProperty(); return ((SignatureTimeStampProperty) sigTs).getTime(); } + private Collection getCertificateValues( + Collection qualifPropsData, + XMLSignature signature) throws XAdES4jException + { + List certValData = CollectionUtils.filterByType(qualifPropsData, CertificateValuesData.class); + + // If no signature time-stamp is present, use the current date. + if (certValData.isEmpty()) + { + return new ArrayList(); + } + + // This is a temporary solution. + // - Properties should probably be verified in two stages (before and after cert path creation). + QualifyingPropertyVerificationContext ctx = new QualifyingPropertyVerificationContext( + signature, + new QualifyingPropertyVerificationContext.CertificationChainData( + new ArrayList(0), + new ArrayList(0), + null), + /**/ + new QualifyingPropertyVerificationContext.SignedObjectsData( + new ArrayList(0), + signature), + new Date()); + Collection props = this.qualifyingPropertiesVerifier.verifyProperties(certValData, ctx); + QualifyingProperty certVal = props.iterator().next().getProperty(); + + + return ((CertificateValuesProperty) certVal).getCertificates(); + } + + private Collection getRevocationValues( + Collection qualifPropsData, + XMLSignature signature) throws XAdES4jException + { + List revValData = CollectionUtils.filterByType(qualifPropsData, RevocationValuesData.class); + + // If no signature time-stamp is present, use the current date. + if (revValData.isEmpty()) + { + return new ArrayList(); + } + + // This is a temporary solution. + // - Properties should probably be verified in two stages (before and after cert path creation). + QualifyingPropertyVerificationContext ctx = new QualifyingPropertyVerificationContext( + signature, + new QualifyingPropertyVerificationContext.CertificationChainData( + new ArrayList(0), + new ArrayList(0), + null), + /**/ + new QualifyingPropertyVerificationContext.SignedObjectsData( + new ArrayList(0), + signature), + new Date()); + Collection props = this.qualifyingPropertiesVerifier.verifyProperties(revValData, ctx); + QualifyingProperty revVal = props.iterator().next().getProperty(); + + return ((RevocationValuesProperty) revVal).getCrls(); + } + private static void doCoreVerification( XMLSignature signature, SignatureSpecificVerificationOptions verificationOptions, @@ -403,7 +480,8 @@ public void addProps( Collection usp, XAdESVerificationResult res) { - PropertiesUtils.addXadesXLProperties(usp, res.getValidationData()); + PropertiesUtils.addXadesXLProperties(usp, res.getValidationData(), + res.getAttributeValidationData()); PropertiesUtils.addXadesXProperties(usp); } }; diff --git a/src/main/java/xades4j/xml/bind/Base64XmlAdapter.java b/src/main/java/xades4j/xml/bind/Base64XmlAdapter.java index ac15a65f..0314627e 100644 --- a/src/main/java/xades4j/xml/bind/Base64XmlAdapter.java +++ b/src/main/java/xades4j/xml/bind/Base64XmlAdapter.java @@ -23,7 +23,7 @@ public byte[] unmarshal(String value) throws Exception { @Override public String marshal(byte[] value) throws IOException { - return (xades4j.utils.Base64.encodeBytes(value, xades4j.utils.Base64.DO_BREAK_LINES)); + return (xades4j.utils.Base64.encodeBytes(value/*, xades4j.utils.Base64.DO_BREAK_LINES*/)); } } diff --git a/src/main/java/xades4j/xml/bind/xades/ObjectFactory.java b/src/main/java/xades4j/xml/bind/xades/ObjectFactory.java index b19a976e..24ab6f98 100644 --- a/src/main/java/xades4j/xml/bind/xades/ObjectFactory.java +++ b/src/main/java/xades4j/xml/bind/xades/ObjectFactory.java @@ -59,7 +59,7 @@ public class ObjectFactory { private final static QName _AllDataObjectsTimeStamp_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "AllDataObjectsTimeStamp"); private final static QName _SignerRole_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "SignerRole"); private final static QName _RevocationValues_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "RevocationValues"); - private final static QName _ArchiveTimeStampV2_QNAME = new QName("http://uri.etsi.org/01903/v1.4.1#", "ArchiveTimeStampV2"); + private final static QName _ArchiveTimeStampV1_4_1_QNAME = new QName("http://uri.etsi.org/01903/v1.4.1#", "ArchiveTimeStamp"); private final static QName _QualifyingPropertiesReference_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "QualifyingPropertiesReference"); private final static QName _CertificateValues_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "CertificateValues"); private final static QName _Any_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "Any"); @@ -69,7 +69,7 @@ public class ObjectFactory { private final static QName _SigAndRefsTimeStamp_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "SigAndRefsTimeStamp"); private final static QName _DataObjectFormat_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "DataObjectFormat"); private final static QName _AttributeCertificateRefs_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "AttributeCertificateRefs"); - private final static QName _TimeStampValidationData_QNAME = new QName("http://uri.etsi.org/01903/v1.4.1#", "TimeStampValidationData"); + private final static QName _TimeStampValidationDataV1_4_1_QNAME = new QName("http://uri.etsi.org/01903/v1.4.1#", "TimeStampValidationData"); private final static QName _SignedProperties_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties"); private final static QName _CompleteCertificateRefs_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "CompleteCertificateRefs"); private final static QName _AttributeRevocationRefs_QNAME = new QName("http://uri.etsi.org/01903/v1.3.2#", "AttributeRevocationRefs"); @@ -728,9 +728,9 @@ public JAXBElement createRevocationValues(XmlRevocation * Create an instance of {@link JAXBElement }{@code <}{@link XmlXAdESTimeStampType }{@code >}} * */ - @XmlElementDecl(namespace = "http://uri.etsi.org/01903/v1.4.1#", name = "ArchiveTimeStampV2") - public JAXBElement createArchiveTimeStampV2(XmlXAdESTimeStampType value) { - return new JAXBElement(_ArchiveTimeStampV2_QNAME, XmlXAdESTimeStampType.class, null, value); + @XmlElementDecl(namespace = "http://uri.etsi.org/01903/v1.4.1#", name = "ArchiveTimeStamp") + public JAXBElement createArchiveTimeStampV1_4_1(XmlXAdESTimeStampType value) { + return new JAXBElement(_ArchiveTimeStampV1_4_1_QNAME, XmlXAdESTimeStampType.class, null, value); } /** @@ -819,8 +819,8 @@ public JAXBElement createAttributeCertificateRef * */ @XmlElementDecl(namespace = "http://uri.etsi.org/01903/v1.4.1#", name = "TimeStampValidationData") - public JAXBElement createTimeStampValidationData(XmlValidationDataType value) { - return new JAXBElement(_TimeStampValidationData_QNAME, XmlValidationDataType.class, null, value); + public JAXBElement createTimeStampValidationDataV1_4_1(XmlValidationDataType value) { + return new JAXBElement(_TimeStampValidationDataV1_4_1_QNAME, XmlValidationDataType.class, null, value); } /** diff --git a/src/main/java/xades4j/xml/bind/xades/XmlUnsignedSignaturePropertiesType.java b/src/main/java/xades4j/xml/bind/xades/XmlUnsignedSignaturePropertiesType.java index 5c7408bf..4888d12b 100644 --- a/src/main/java/xades4j/xml/bind/xades/XmlUnsignedSignaturePropertiesType.java +++ b/src/main/java/xades4j/xml/bind/xades/XmlUnsignedSignaturePropertiesType.java @@ -41,8 +41,9 @@ * <element name="RevocationValues" type="{http://uri.etsi.org/01903/v1.3.2#}RevocationValuesType" minOccurs="0"/> * <element name="AttrAuthoritiesCertValues" type="{http://uri.etsi.org/01903/v1.3.2#}CertificateValuesType" minOccurs="0"/> * <element name="AttributeRevocationValues" type="{http://uri.etsi.org/01903/v1.3.2#}RevocationValuesType" minOccurs="0"/> - * <element name="ArchiveTimeStamp" type="{http://uri.etsi.org/01903/v1.3.2#}XAdESTimeStampType" maxOccurs="unbounded" minOccurs="0"/> - * <any namespace='##other' maxOccurs="unbounded" minOccurs="0"/> + * <element name="ArchiveTimeStamp" type="{http://uri.etsi.org/01903/v1.4.1#}XAdESTimeStampType" maxOccurs="unbounded" minOccurs="0"/> + * <element name="TimeStampValidationData" type="{http://uri.etsi.org/01903/v1.4.1#}ValidationDataType" maxOccurs="unbounded" minOccurs="0"/> + * <any namespace='##other' maxOccurs="unbounded" minOccurs="0"/> * </sequence> * <attribute name="Id" type="{http://www.w3.org/2001/XMLSchema}ID" /> * </restriction> @@ -68,6 +69,7 @@ "attrAuthoritiesCertValues", "attributeRevocationValues", "archiveTimeStamp", + "timeStampValidationData", "any" }) public class XmlUnsignedSignaturePropertiesType @@ -98,6 +100,8 @@ public class XmlUnsignedSignaturePropertiesType protected XmlRevocationValuesType attributeRevocationValues; @XmlElement(name = "ArchiveTimeStamp") protected List archiveTimeStamp; + @XmlElement(name = "TimeStampValidationData") + protected List timeStampValidationData; @XmlAnyElement(lax = true) protected List any; @XmlAttribute(name = "Id") @@ -459,6 +463,35 @@ public List getArchiveTimeStamp() return this.archiveTimeStamp; } + /** + * Gets the value of the timeStampValidationData property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the archiveTimeStamp property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getTimeStampValidationData().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link XmlValidationDataType } + * + * + */ + public List getTimeStampValidationData() + { + if (timeStampValidationData == null) + timeStampValidationData = new ArrayList(); + return this.timeStampValidationData; + } + /** * Gets the value of the any property. * diff --git a/src/main/java/xades4j/xml/bind/xades/XmlValidationDataType.java b/src/main/java/xades4j/xml/bind/xades/XmlValidationDataType.java index c584376f..6000390f 100644 --- a/src/main/java/xades4j/xml/bind/xades/XmlValidationDataType.java +++ b/src/main/java/xades4j/xml/bind/xades/XmlValidationDataType.java @@ -33,7 +33,7 @@ * <element ref="{http://uri.etsi.org/01903/v1.3.2#}RevocationValues" minOccurs="0"/> * </sequence> * <attribute name="Id" type="{http://www.w3.org/2001/XMLSchema}ID" /> - * <attribute name="UR" type="{http://www.w3.org/2001/XMLSchema}anyURI" /> + * <attribute name="URI" type="{http://www.w3.org/2001/XMLSchema}anyURI" /> * </restriction> * </complexContent> * </complexType> @@ -57,9 +57,9 @@ public class XmlValidationDataType { @XmlID @XmlSchemaType(name = "ID") protected String id; - @XmlAttribute(name = "UR") + @XmlAttribute(name = "URI") @XmlSchemaType(name = "anyURI") - protected String ur; + protected String uri; /** * Gets the value of the certificateValues property. @@ -134,27 +134,27 @@ public void setId(String value) { } /** - * Gets the value of the ur property. + * Gets the value of the uri property. * * @return * possible object is * {@link String } * */ - public String getUR() { - return ur; + public String getURI() { + return uri; } /** - * Sets the value of the ur property. + * Sets the value of the uri property. * * @param value * allowed object is * {@link String } * */ - public void setUR(String value) { - this.ur = value; + public void setURI(String value) { + this.uri = value; } } diff --git a/src/main/java/xades4j/xml/marshalling/DefaultUnsignedPropertiesMarshaller.java b/src/main/java/xades4j/xml/marshalling/DefaultUnsignedPropertiesMarshaller.java index 77553d93..bd2fcbf8 100644 --- a/src/main/java/xades4j/xml/marshalling/DefaultUnsignedPropertiesMarshaller.java +++ b/src/main/java/xades4j/xml/marshalling/DefaultUnsignedPropertiesMarshaller.java @@ -21,12 +21,15 @@ import org.w3c.dom.Node; import xades4j.properties.QualifyingProperty; import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.AttributeRevocationValuesData; import xades4j.properties.data.CertificateValuesData; import xades4j.properties.data.CompleteCertificateRefsData; import xades4j.properties.data.CompleteRevocationRefsData; import xades4j.properties.data.RevocationValuesData; import xades4j.properties.data.SigAndRefsTimeStampData; import xades4j.properties.data.SignatureTimeStampData; +import xades4j.properties.data.TimeStampValidationDataData; import xades4j.xml.bind.xades.ObjectFactory; import xades4j.xml.bind.xades.XmlUnsignedDataObjectPropertiesType; import xades4j.xml.bind.xades.XmlUnsignedPropertiesType; @@ -55,8 +58,11 @@ class DefaultUnsignedPropertiesMarshaller super.putConverter(CompleteRevocationRefsData.class, new ToXmlCompleteRevocRefsConverter()); super.putConverter(SigAndRefsTimeStampData.class, new ToXmlSigAndRefsTimeStampConverter(algorithmsParametersMarshallingProvider)); super.putConverter(CertificateValuesData.class, new ToXmlCertificateValuesConverter()); + super.putConverter(AttrAuthoritiesCertValuesData.class, new ToXmlAttrAuthoritiesCertValuesConverter()); super.putConverter(RevocationValuesData.class, new ToXmlRevocationValuesConverter()); + super.putConverter(AttributeRevocationValuesData.class, new ToXmlAttributeRevocationValuesConverter()); super.putConverter(ArchiveTimeStampData.class, new ToXmlArchiveTimeStampConverter(algorithmsParametersMarshallingProvider)); + super.putConverter(TimeStampValidationDataData.class, new ToXmlTimeStampValidationDataConverter()); /* The CounterSignature property is marshalled directly using DOM because * it is easier that way (it is represented by a GenericDOMData instance). */ diff --git a/src/main/java/xades4j/xml/marshalling/ToXmlArchiveTimeStampConverter.java b/src/main/java/xades4j/xml/marshalling/ToXmlArchiveTimeStampConverter.java index 1936819b..568fdefe 100644 --- a/src/main/java/xades4j/xml/marshalling/ToXmlArchiveTimeStampConverter.java +++ b/src/main/java/xades4j/xml/marshalling/ToXmlArchiveTimeStampConverter.java @@ -39,7 +39,7 @@ protected void insertIntoObjectTree( XmlUnsignedPropertiesType xmlProps, ArchiveTimeStampData propData) { - JAXBElement xmlArchTS = new ObjectFactory().createArchiveTimeStampV2(xmlTimeStamp); + JAXBElement xmlArchTS = new ObjectFactory().createArchiveTimeStampV1_4_1(xmlTimeStamp); xmlProps.getUnsignedSignatureProperties().getAny().add(xmlArchTS); } } diff --git a/src/main/java/xades4j/xml/marshalling/ToXmlAttrAuthoritiesCertValuesConverter.java b/src/main/java/xades4j/xml/marshalling/ToXmlAttrAuthoritiesCertValuesConverter.java new file mode 100644 index 00000000..6219e60a --- /dev/null +++ b/src/main/java/xades4j/xml/marshalling/ToXmlAttrAuthoritiesCertValuesConverter.java @@ -0,0 +1,53 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.marshalling; + +import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Document; + +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.PropertyDataObject; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlUnsignedPropertiesType; + +public class ToXmlAttrAuthoritiesCertValuesConverter implements + UnsignedPropertyDataToXmlConverter +{ + + @Override + public void convertIntoObjectTree(PropertyDataObject propData, + XmlUnsignedPropertiesType xmlProps, Document doc) + { + Collection certValues = + ((AttrAuthoritiesCertValuesData)propData).getData(); + + XmlCertificateValuesType xmlCertValues = new XmlCertificateValuesType(); + List xmlCerts = xmlCertValues.getEncapsulatedX509CertificateOrOtherCertificate(); + + for (byte[] encodedCer : certValues) + { + XmlEncapsulatedPKIDataType xmlEncodedCert = new XmlEncapsulatedPKIDataType(); + xmlEncodedCert.setValue(encodedCer); + xmlCerts.add(xmlEncodedCert); + } + + xmlProps.getUnsignedSignatureProperties().setAttrAuthoritiesCertValues(xmlCertValues); + } +} diff --git a/src/main/java/xades4j/xml/marshalling/ToXmlAttributeRevocationValuesConverter.java b/src/main/java/xades4j/xml/marshalling/ToXmlAttributeRevocationValuesConverter.java new file mode 100644 index 00000000..5564dc1f --- /dev/null +++ b/src/main/java/xades4j/xml/marshalling/ToXmlAttributeRevocationValuesConverter.java @@ -0,0 +1,56 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.marshalling; + +import java.util.Collection; +import java.util.List; + +import org.w3c.dom.Document; + +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.PropertyDataObject; +import xades4j.xml.bind.xades.XmlCRLValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlUnsignedPropertiesType; + +public class ToXmlAttributeRevocationValuesConverter + implements UnsignedPropertyDataToXmlConverter +{ + @Override + public void convertIntoObjectTree(PropertyDataObject propData, + XmlUnsignedPropertiesType xmlProps, Document doc) + { + Collection crlValues = ((AttributeRevocationValuesData)propData).getData(); + + XmlRevocationValuesType xmlAttrRevocValues = new XmlRevocationValuesType(); + XmlCRLValuesType xmlCRLValues = new XmlCRLValuesType(); + xmlAttrRevocValues.setCRLValues(xmlCRLValues); + + List xmlCRLs = xmlCRLValues.getEncapsulatedCRLValue(); + + for (byte[] encodCrl : crlValues) + { + XmlEncapsulatedPKIDataType xmlEncodedCrl = new XmlEncapsulatedPKIDataType(); + xmlEncodedCrl.setValue(encodCrl); + xmlCRLs.add(xmlEncodedCrl); + } + + xmlProps.getUnsignedSignatureProperties() + .setAttributeRevocationValues(xmlAttrRevocValues); + } +} diff --git a/src/main/java/xades4j/xml/marshalling/ToXmlTimeStampValidationDataConverter.java b/src/main/java/xades4j/xml/marshalling/ToXmlTimeStampValidationDataConverter.java new file mode 100644 index 00000000..f14fcf36 --- /dev/null +++ b/src/main/java/xades4j/xml/marshalling/ToXmlTimeStampValidationDataConverter.java @@ -0,0 +1,83 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.marshalling; + +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.JAXBElement; + +import org.w3c.dom.Document; + +import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.TimeStampValidationDataData; +import xades4j.xml.bind.xades.ObjectFactory; +import xades4j.xml.bind.xades.XmlCRLValuesType; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlUnsignedPropertiesType; +import xades4j.xml.bind.xades.XmlValidationDataType; + +public class ToXmlTimeStampValidationDataConverter implements + UnsignedPropertyDataToXmlConverter +{ + + @Override + public void convertIntoObjectTree(PropertyDataObject propData, + XmlUnsignedPropertiesType xmlProps, Document doc) + { + Collection certValues = ((TimeStampValidationDataData) propData).getCertificateData(); + Collection crlValues = ((TimeStampValidationDataData) propData).getCRLData(); + + ObjectFactory objectFactory = new ObjectFactory(); + + XmlValidationDataType xmlValidationDataType = objectFactory.createXmlValidationDataType(); + if (certValues != null && ! certValues.isEmpty()) + { + XmlCertificateValuesType xmlCertificateValues = new XmlCertificateValuesType(); + List certList = + xmlCertificateValues.getEncapsulatedX509CertificateOrOtherCertificate(); + for (byte[] cert : certValues) + { + XmlEncapsulatedPKIDataType xmlEncodedCert = new XmlEncapsulatedPKIDataType(); + xmlEncodedCert.setValue(cert); + certList.add(xmlEncodedCert); + } + xmlValidationDataType.setCertificateValues(xmlCertificateValues); + } + + if (crlValues != null && ! crlValues.isEmpty()) + { + XmlRevocationValuesType xmlRevocationValuesType = new XmlRevocationValuesType(); + XmlCRLValuesType xmlCRLValuesType = new XmlCRLValuesType(); + List crlList = xmlCRLValuesType.getEncapsulatedCRLValue(); + for (byte[] crl : crlValues) + { + XmlEncapsulatedPKIDataType xmlEncodedCrl = new XmlEncapsulatedPKIDataType(); + xmlEncodedCrl.setValue(crl); + crlList.add(xmlEncodedCrl); + } + xmlRevocationValuesType.setCRLValues(xmlCRLValuesType); + xmlValidationDataType.setRevocationValues(xmlRevocationValuesType); + } + + JAXBElement xmlTimeStampValidationData = + objectFactory.createTimeStampValidationDataV1_4_1(xmlValidationDataType); + xmlProps.getUnsignedSignatureProperties().getAny().add(xmlTimeStampValidationData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlArchiveTimeStampConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlArchiveTimeStampConverter.java new file mode 100644 index 00000000..1c7b0346 --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlArchiveTimeStampConverter.java @@ -0,0 +1,57 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import xades4j.algorithms.Algorithm; +import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlArchiveTimeStampConverter extends + FromXmlBaseTimeStampConverter + implements UnsignedSigPropFromXmlConv +{ + + public FromXmlArchiveTimeStampConverter() + { + super(ArchiveTimeStampProperty.PROP_NAME); + } + + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + super.convertTimeStamps(xmlProps.getArchiveTimeStamp(), + propertyDataCollector); + } + + @Override + protected ArchiveTimeStampData createTSData(Algorithm c14n) + { + return new ArchiveTimeStampData(c14n); + } + + @Override + protected void setTSData(ArchiveTimeStampData tsData, + QualifyingPropertiesDataCollector propertyDataCollector) + { + propertyDataCollector.addArchiveTimeStamp(tsData); + } + +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlAttrAuthoritiesCertValuesConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlAttrAuthoritiesCertValuesConverter.java new file mode 100644 index 00000000..b77a71bf --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlAttrAuthoritiesCertValuesConverter.java @@ -0,0 +1,64 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.util.List; + +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.xml.bind.xades.XmlAnyType; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlAttrAuthoritiesCertValuesConverter implements + UnsignedSigPropFromXmlConv +{ + + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + XmlCertificateValuesType xmlCertificateValues = xmlProps.getAttrAuthoritiesCertValues(); + convertFromObject(xmlCertificateValues, propertyDataCollector); + } + + public void convertFromObject(XmlCertificateValuesType xmlCertificateValues, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + if (null == xmlCertificateValues) + return; + + AttrAuthoritiesCertValuesData attrAuthoritiesCertValuesData = + new AttrAuthoritiesCertValuesData(); + List values = xmlCertificateValues.getEncapsulatedX509CertificateOrOtherCertificate(); + for (Object item : values) + { + if (item instanceof XmlEncapsulatedPKIDataType) + { + XmlEncapsulatedPKIDataType cert = (XmlEncapsulatedPKIDataType) item; + attrAuthoritiesCertValuesData.addData(cert.getValue()); + } + if (item instanceof XmlAnyType) + throw new PropertyUnmarshalException("Property not supported", "OtherCertificate"); + } + + propertyDataCollector.setAttrAuthoritiesCertValues(attrAuthoritiesCertValuesData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlAttributeRevocationValuesConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlAttributeRevocationValuesConverter.java new file mode 100644 index 00000000..755f1e45 --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlAttributeRevocationValuesConverter.java @@ -0,0 +1,66 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.util.List; + +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.xml.bind.xades.XmlCRLValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlAttributeRevocationValuesConverter + implements UnsignedSigPropFromXmlConv +{ + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + XmlRevocationValuesType xmlRevocationValues = xmlProps.getAttributeRevocationValues(); + convertFromObject(xmlRevocationValues, propertyDataCollector); + } + + public void convertFromObject(XmlRevocationValuesType xmlRevocationValues, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + if (null == xmlRevocationValues) + return; + + AttributeRevocationValuesData attrRevocationValuesData = + new AttributeRevocationValuesData(); + XmlCRLValuesType values = xmlRevocationValues.getCRLValues(); + List crls = values.getEncapsulatedCRLValue(); + for (XmlEncapsulatedPKIDataType crl : crls) + { + attrRevocationValuesData.addData(crl.getValue()); + } + + // handle unsupported data + if (xmlRevocationValues.getOCSPValues() != null) + throw new PropertyUnmarshalException("OCSP responses are unsupported", + "AttributeRevocationValues"); + if (xmlRevocationValues.getOtherValues() != null) + throw new PropertyUnmarshalException("Other (not CRL and not OCSP) " + + "certificate revocation values unsupported", "AttributeRevocationValues"); + + propertyDataCollector.setAttributeRevocationValues(attrRevocationValuesData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlCertificateValuesConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlCertificateValuesConverter.java new file mode 100644 index 00000000..a814ef64 --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlCertificateValuesConverter.java @@ -0,0 +1,62 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.util.List; + +import xades4j.properties.data.CertificateValuesData; +import xades4j.xml.bind.xades.XmlAnyType; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlCertificateValuesConverter implements UnsignedSigPropFromXmlConv +{ + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + XmlCertificateValuesType xmlCertificateValues = xmlProps.getCertificateValues(); + + convertFromObject(xmlCertificateValues, propertyDataCollector); + } + + public void convertFromObject(XmlCertificateValuesType xmlCertificateValues, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + if (null == xmlCertificateValues) + return; + + CertificateValuesData certificateValuesData = new CertificateValuesData(); + List values = xmlCertificateValues.getEncapsulatedX509CertificateOrOtherCertificate(); + for (Object item : values) + { + if (item instanceof XmlEncapsulatedPKIDataType) + { + XmlEncapsulatedPKIDataType cert = (XmlEncapsulatedPKIDataType) item; + certificateValuesData.addData(cert.getValue()); + } + if (item instanceof XmlAnyType) + throw new PropertyUnmarshalException("Property not supported", "OtherCertificate"); + } + + propertyDataCollector.setCertificateValues(certificateValuesData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteCertRefsConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteCertRefsConverter.java index f856dc66..a1ef88cb 100644 --- a/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteCertRefsConverter.java +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteCertRefsConverter.java @@ -35,6 +35,12 @@ public void convertFromObjectTree( if (null == xmlCompleteCertRefs) return; + convertFromObject(xmlCompleteCertRefs, propertyDataCollector); + } + + public void convertFromObject(XmlCompleteCertificateRefsType xmlCompleteCertRefs, + QualifyingPropertiesDataCollector propertyDataCollector) + { CompleteCertificateRefsData completeCertRefsData = new CompleteCertificateRefsData(); FromXmlUtils.createAndCertificateRefs(xmlCompleteCertRefs.getCertRefs(), completeCertRefsData); diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteRevocRefsConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteRevocRefsConverter.java index 587eba36..2b2a7e1a 100644 --- a/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteRevocRefsConverter.java +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlCompleteRevocRefsConverter.java @@ -40,12 +40,21 @@ public void convertFromObjectTree( if (null == xmlCompleteRevocRefs) return; + convertFromObject(xmlCompleteRevocRefs, propertyDataCollector); + } + + public void convertFromObject(XmlCompleteRevocationRefsType xmlCompleteRevocRefs, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { if (xmlCompleteRevocRefs.getOCSPRefs() != null || xmlCompleteRevocRefs.getOtherRefs() != null) - throw new PropertyUnmarshalException("Only CRL references are supported", CompleteRevocationRefsProperty.PROP_NAME); + throw new PropertyUnmarshalException("Only CRL references are supported", + CompleteRevocationRefsProperty.PROP_NAME); XmlCRLRefsType xmlCRLRefs = xmlCompleteRevocRefs.getCRLRefs(); if (null == xmlCRLRefs) - throw new PropertyUnmarshalException("CRL references not present", CompleteRevocationRefsProperty.PROP_NAME); + throw new PropertyUnmarshalException("CRL references not present", + CompleteRevocationRefsProperty.PROP_NAME); CompleteRevocationRefsData complRevocRefsData = new CompleteRevocationRefsData(); diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlRevocationValuesConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlRevocationValuesConverter.java new file mode 100644 index 00000000..5cebf93d --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlRevocationValuesConverter.java @@ -0,0 +1,64 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.util.List; + +import xades4j.properties.data.RevocationValuesData; +import xades4j.xml.bind.xades.XmlCRLValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlRevocationValuesConverter implements UnsignedSigPropFromXmlConv +{ + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + XmlRevocationValuesType xmlRevocationValues = xmlProps.getRevocationValues(); + convertFromObject(xmlRevocationValues, propertyDataCollector); + } + + public void convertFromObject(XmlRevocationValuesType xmlRevocationValues, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + if (null == xmlRevocationValues) + return; + + RevocationValuesData revocationValuesData = new RevocationValuesData(); + XmlCRLValuesType values = xmlRevocationValues.getCRLValues(); + List crls = values.getEncapsulatedCRLValue(); + for (XmlEncapsulatedPKIDataType crl : crls) + { + revocationValuesData.addData(crl.getValue()); + } + + // handle unsupported data + if (xmlRevocationValues.getOCSPValues() != null) + throw new PropertyUnmarshalException("OCSP responses are unsupported", + "RevocationValues"); + if (xmlRevocationValues.getOtherValues() != null) + throw new PropertyUnmarshalException("Other (not CRL and not OCSP) " + + "certificate revocation values unsupported", "RevocationValues"); + + propertyDataCollector.setRevocationValues(revocationValuesData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlSigAndRefsTimeStampConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlSigAndRefsTimeStampConverter.java new file mode 100644 index 00000000..bc71bd8f --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlSigAndRefsTimeStampConverter.java @@ -0,0 +1,56 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import xades4j.algorithms.Algorithm; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.data.SigAndRefsTimeStampData; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; + +public class FromXmlSigAndRefsTimeStampConverter + extends FromXmlBaseTimeStampConverter + implements UnsignedSigPropFromXmlConv +{ + + public FromXmlSigAndRefsTimeStampConverter() + { + super(SigAndRefsTimeStampProperty.PROP_NAME); + } + + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + super.convertTimeStamps(xmlProps.getSigAndRefsTimeStamp(), + propertyDataCollector); + } + + @Override + protected SigAndRefsTimeStampData createTSData(Algorithm c14n) + { + return new SigAndRefsTimeStampData(c14n); + } + + @Override + protected void setTSData(SigAndRefsTimeStampData tsData, + QualifyingPropertiesDataCollector propertyDataCollector) + { + propertyDataCollector.addSigAndRefsTimeStamp(tsData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlTimeStampValidationDataConverter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlTimeStampValidationDataConverter.java new file mode 100644 index 00000000..c80b90fa --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlTimeStampValidationDataConverter.java @@ -0,0 +1,98 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.util.List; + +import xades4j.properties.data.TimeStampValidationDataData; +import xades4j.xml.bind.xades.XmlAnyType; +import xades4j.xml.bind.xades.XmlCRLValuesType; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlEncapsulatedPKIDataType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlUnsignedSignaturePropertiesType; +import xades4j.xml.bind.xades.XmlValidationDataType; + +public class FromXmlTimeStampValidationDataConverter implements + UnsignedSigPropFromXmlConv +{ + + @Override + public void convertFromObjectTree( + XmlUnsignedSignaturePropertiesType xmlProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + List xmlTimeStampValidationData = + xmlProps.getTimeStampValidationData(); + convertFromObject(xmlTimeStampValidationData, propertyDataCollector); + } + + public void convertFromObject( + List xmlTimeStampValidationData, + QualifyingPropertiesDataCollector propertyDataCollector) + throws PropertyUnmarshalException + { + if (null == xmlTimeStampValidationData || xmlTimeStampValidationData.isEmpty()) + return; + + TimeStampValidationDataData timeStampValidationDataData = + new TimeStampValidationDataData(); + + for (XmlValidationDataType xmlVDT : xmlTimeStampValidationData) + { + if (xmlVDT.getCertificateValues() != null) + { + XmlCertificateValuesType xmlCertificateValues = xmlVDT.getCertificateValues(); + + List certValues = xmlCertificateValues.getEncapsulatedX509CertificateOrOtherCertificate(); + for (Object item : certValues) + { + if (item instanceof XmlEncapsulatedPKIDataType) + { + XmlEncapsulatedPKIDataType cert = (XmlEncapsulatedPKIDataType) item; + timeStampValidationDataData.addCertificateData(cert.getValue()); + } + if (item instanceof XmlAnyType) + throw new PropertyUnmarshalException("Property not supprted", "OtherCertificate"); + } + } + + XmlRevocationValuesType xmlRevocationValues = xmlVDT.getRevocationValues(); + if (xmlRevocationValues != null && xmlRevocationValues.getCRLValues() != null) + { + XmlCRLValuesType crlValues = xmlRevocationValues.getCRLValues(); + List crls = crlValues.getEncapsulatedCRLValue(); + for (XmlEncapsulatedPKIDataType crl : crls) + { + timeStampValidationDataData.addCRLData(crl.getValue()); + } + + // check for unsupported data + if (xmlRevocationValues.getOCSPValues() != null) + throw new PropertyUnmarshalException("OCSP responses are unsupported", + "TimeStampValidationData"); + if (xmlRevocationValues.getOtherValues() != null) + throw new PropertyUnmarshalException("Other (not CRL and not OCSP) " + + "certificate revocation values unsupported", + "TimeStampValidationData"); + } + } + + propertyDataCollector.addTimeStampValidationDataData(timeStampValidationDataData); + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/FromXmlUnsupportedUSPLimiter.java b/src/main/java/xades4j/xml/unmarshalling/FromXmlUnsupportedUSPLimiter.java index 716b0cde..ae7f34af 100644 --- a/src/main/java/xades4j/xml/unmarshalling/FromXmlUnsupportedUSPLimiter.java +++ b/src/main/java/xades4j/xml/unmarshalling/FromXmlUnsupportedUSPLimiter.java @@ -33,14 +33,8 @@ public void convertFromObjectTree( if (!ObjectUtils.allNull( xmlProps.getAttributeCertificateRefs(), xmlProps.getAttributeRevocationRefs(), - xmlProps.getCertificateValues(), - xmlProps.getRevocationValues(), - xmlProps.getAttrAuthoritiesCertValues(), - xmlProps.getAttributeRevocationValues(), xmlProps.getAttributeCertificateRefs()) || - !xmlProps.getSigAndRefsTimeStamp().isEmpty() || - !xmlProps.getRefsOnlyTimeStamp().isEmpty() || - !xmlProps.getArchiveTimeStamp().isEmpty()) + !xmlProps.getRefsOnlyTimeStamp().isEmpty()) throw new PropertyUnmarshalException("Unsupported properties were found", "Unsupported"); } } diff --git a/src/main/java/xades4j/xml/unmarshalling/HybridQualifyingPropertiesUnmarshaller.java b/src/main/java/xades4j/xml/unmarshalling/HybridQualifyingPropertiesUnmarshaller.java new file mode 100644 index 00000000..80e5dc14 --- /dev/null +++ b/src/main/java/xades4j/xml/unmarshalling/HybridQualifyingPropertiesUnmarshaller.java @@ -0,0 +1,390 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2010 Luis Goncalves. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.xml.unmarshalling; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; +import xades4j.properties.CertificateValuesProperty; +import xades4j.properties.CompleteCertificateRefsProperty; +import xades4j.properties.CompleteRevocationRefsProperty; +import xades4j.properties.CounterSignatureProperty; +import xades4j.properties.QualifyingProperty; +import xades4j.properties.RevocationValuesProperty; +import xades4j.properties.SigAndRefsTimeStampProperty; +import xades4j.properties.SignatureTimeStampProperty; +import xades4j.properties.TimeStampValidationDataProperty; +import xades4j.properties.UnsignedSignatureProperty; +import xades4j.xml.bind.xades.XmlCertificateValuesType; +import xades4j.xml.bind.xades.XmlCompleteCertificateRefsType; +import xades4j.xml.bind.xades.XmlCompleteRevocationRefsType; +import xades4j.xml.bind.xades.XmlCounterSignatureType; +import xades4j.xml.bind.xades.XmlQualifyingPropertiesType; +import xades4j.xml.bind.xades.XmlRevocationValuesType; +import xades4j.xml.bind.xades.XmlValidationDataType; +import xades4j.xml.bind.xades.XmlXAdESTimeStampType; + +public final class HybridQualifyingPropertiesUnmarshaller implements + QualifyingPropertiesUnmarshaller +{ + private static final JAXBContext jaxbContext; + private final UnmarshallerModule[] modules; + private boolean acceptUnknown = false; + + static + { + try + { + jaxbContext = JAXBContext.newInstance(XmlQualifyingPropertiesType.class); + } + catch(JAXBException e) + { + throw new UnsupportedOperationException(e); + } + } + + public HybridQualifyingPropertiesUnmarshaller() + { + this.modules = new UnmarshallerModule[3]; + this.modules[0] = new SignedSigPropsModule(); + this.modules[1] = new SignedDataObjPropsModule(); + this.modules[2] = new UnsignedDataObjPropsModule(); + } + + @Override + public void setAcceptUnknownProperties(boolean accept) + { + for (UnmarshallerModule module : modules) + { + module.setAcceptUnknownProperties(accept); + } + acceptUnknown = accept; + } + + @Override + public void unmarshalProperties(Element qualifyingProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws UnmarshalException + { + /* + * Unmarshall SignedSignatureProperties, SignedDataObjectProperties and + * UnsignedDataObjectProperties. + */ + XmlQualifyingPropertiesType xmlQualifyingProps = null; + try + { + // Create the JAXB unmarshaller and unmarshalProperties the root JAXB element + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + JAXBElement qualifPropsElem = + (JAXBElement)unmarshaller.unmarshal(qualifyingProps, + XmlQualifyingPropertiesType.class); + xmlQualifyingProps = qualifPropsElem.getValue(); + } catch (javax.xml.bind.UnmarshalException ex) + { + throw new UnmarshalException("Cannot bind XML elements to Java classes", ex); + } catch (JAXBException ex) + { + throw new UnmarshalException("Cannot unmarshall properties. Error on JAXB unmarshalling.", ex); + } + + // Iterate the modules to convert the different types of properties. + for (UnmarshallerModule module : modules) + { + module.convertProperties(xmlQualifyingProps, qualifyingProps, propertyDataCollector); + } + + /* + * Unmarshall UnsignedSignatureProperties + */ + Element unsignedSignatureProperties = findUnsignedSignaturePropertiesNode(qualifyingProps); + if (unsignedSignatureProperties == null) + return; + + // because the order of properties is important + unmarshalUnsignedSignatureProperties(unsignedSignatureProperties, propertyDataCollector); + } + + private Element findUnsignedSignaturePropertiesNode(Element qualifyingProps) + throws UnmarshalException + { + // find UnsignedProperties element + NodeList qualifyingPropsNodes = qualifyingProps.getChildNodes(); + Element unsignedProperties = null; + for (int i=0; i < qualifyingPropsNodes.getLength(); i++) + { + Node item = qualifyingPropsNodes.item(i); + if (item.getNodeType() != Node.ELEMENT_NODE) + continue; + + Element elem = (Element) item; + if (elem.getLocalName().equalsIgnoreCase(QualifyingProperty.UNSIGNED_PROPS_TAG) + && elem.getNamespaceURI().equals(QualifyingProperty.XADES_XMLNS)) + if (unsignedProperties == null) + unsignedProperties = elem; + else + throw new UnmarshalException("Multiple UnsignedProperties nodes in signature!"); + } + if (unsignedProperties == null) + return null; // no unsigned properties, nothing to unmarshall + + // find UnsignedSignatureProperties element + NodeList unsignedPropsNodes = unsignedProperties.getChildNodes(); + Element unsignedSignatureProperties = null; + for (int i=0; i < unsignedPropsNodes.getLength(); i++) + { + Node item = unsignedPropsNodes.item(i); + if (item.getNodeType() != Node.ELEMENT_NODE) + continue; + + Element elem = (Element) item; + if (elem.getLocalName().equalsIgnoreCase(QualifyingProperty.UNSIGNED_SIGNATURE_PROPS_TAG) + && elem.getNamespaceURI().equals(QualifyingProperty.XADES_XMLNS)) + if (unsignedSignatureProperties == null) + unsignedSignatureProperties = elem; + else + throw new UnmarshalException("Multiple UnsignedSignatureProperties nodes in signature"); + } + if (unsignedSignatureProperties == null) + return null; // no unsigned signature properties, nothing to unmarshall + return unsignedSignatureProperties; + } + + private Node getNextElementNode(Node node) + { + while (node != null && node.getNodeType() != Node.ELEMENT_NODE) + node = node.getNextSibling(); + return node; + } + + private boolean isNodeTheProperty(Node node, Class clazz) + { + Field propNameField; + Field namespaceField; + try { + propNameField = clazz.getField("PROP_NAME"); + if (!clazz.equals(ArchiveTimeStampProperty.class) && + !clazz.equals(TimeStampValidationDataProperty.class)) + namespaceField = clazz.getField("XADES_XMLNS"); + else + namespaceField = clazz.getField("XADESV141_XMLNS"); + } catch (Exception e) + { + throw new RuntimeException("Wrong class passed to isNodeTheProperty metod", e); + } + String propName; + String namespace; + try { + propName = (String) propNameField.get(null); + namespace = (String) namespaceField.get(null); + } catch (Exception e) + { + throw new RuntimeException("Wrong class passed to isNodeTheProperty metod", e); + } + + if (node.getLocalName().equalsIgnoreCase(propName) && + node.getNamespaceURI().equalsIgnoreCase(namespace)) + return true; + + return false; + } + + + private JAXBElement unmarshallElement(Node node, Class clazz) + throws UnmarshalException + { + JAXBContext jaxbCont = null; + try + { + jaxbCont = JAXBContext.newInstance(clazz); + } catch (JAXBException e) + { + throw new UnmarshalException("JAXB initialization failure", e); + } + JAXBElement unmarshalledElement; + try + { + Unmarshaller unmarshaller = jaxbCont.createUnmarshaller(); + unmarshalledElement = unmarshaller.unmarshal(node, clazz); + } catch (JAXBException e) + { + throw new UnmarshalException( + "Property " + node.getLocalName() + " unmarshalling error", e); + } + return unmarshalledElement; + } + + private void unmarshalUnsignedSignatureProperties(Element unsignedSigProps, + QualifyingPropertiesDataCollector propertyDataCollector) + throws UnmarshalException + { + for(Node node = unsignedSigProps.getFirstChild(); node != null; node = node.getNextSibling()) + { + // go past text (line breaks), CDATA, comments, etc. + node = getNextElementNode(node); + if (node == null) + return; + + if (isNodeTheProperty(node, SignatureTimeStampProperty.class)) + { + JAXBElement sigTimeStampElem = + unmarshallElement(node, XmlXAdESTimeStampType.class); + + List xmlSigTimeStamp = new ArrayList(); + xmlSigTimeStamp.add(sigTimeStampElem.getValue()); + FromXmlSignatureTimeStampConverter sigTSConv = new FromXmlSignatureTimeStampConverter(); + + sigTSConv.convertTimeStamps(xmlSigTimeStamp, + propertyDataCollector); + + } else if (isNodeTheProperty(node, CompleteCertificateRefsProperty.class)) + { + JAXBElement completeCertRefsElem = + unmarshallElement(node, XmlCompleteCertificateRefsType.class); + + FromXmlCompleteCertRefsConverter compCertRefsConverter = + new FromXmlCompleteCertRefsConverter(); + + compCertRefsConverter.convertFromObject(completeCertRefsElem.getValue(), + propertyDataCollector); + + } else if (isNodeTheProperty(node, CompleteRevocationRefsProperty.class)) + { + JAXBElement completeRevocRefsElem = + unmarshallElement(node, XmlCompleteRevocationRefsType.class); + + FromXmlCompleteRevocRefsConverter compCertRefsConverter = + new FromXmlCompleteRevocRefsConverter(); + + compCertRefsConverter.convertFromObject(completeRevocRefsElem.getValue(), + propertyDataCollector); + + } else if (isNodeTheProperty(node, SigAndRefsTimeStampProperty.class)) + { + JAXBElement sigAndRefsTimeStampElem = + unmarshallElement(node, XmlXAdESTimeStampType.class); + + List xmlSigAndRefsTimeStamp = new ArrayList(); + xmlSigAndRefsTimeStamp.add(sigAndRefsTimeStampElem.getValue()); + FromXmlSigAndRefsTimeStampConverter sigTSConv = + new FromXmlSigAndRefsTimeStampConverter(); + + sigTSConv.convertTimeStamps(xmlSigAndRefsTimeStamp, + propertyDataCollector); + + } else if (isNodeTheProperty(node, CertificateValuesProperty.class)) + { + JAXBElement certificateValuesElem = + unmarshallElement(node, XmlCertificateValuesType.class); + + FromXmlCertificateValuesConverter certValuesConverter = + new FromXmlCertificateValuesConverter(); + + certValuesConverter.convertFromObject(certificateValuesElem.getValue(), + propertyDataCollector); + + } else if (isNodeTheProperty(node, RevocationValuesProperty.class)) + { + JAXBElement revocationValuesElem = + unmarshallElement(node, XmlRevocationValuesType.class); + + FromXmlRevocationValuesConverter revocValuesConverter = + new FromXmlRevocationValuesConverter(); + + revocValuesConverter.convertFromObject(revocationValuesElem.getValue(), + propertyDataCollector); + + } else if (isNodeTheProperty(node, AttrAuthoritiesCertValuesProperty.class)) + { + + JAXBElement attrAuthCertValElem = + unmarshallElement(node, XmlCertificateValuesType.class); + + FromXmlAttrAuthoritiesCertValuesConverter attrAuthCertValConverter = + new FromXmlAttrAuthoritiesCertValuesConverter(); + + attrAuthCertValConverter.convertFromObject(attrAuthCertValElem.getValue(), + propertyDataCollector); + } else if (isNodeTheProperty(node, AttributeRevocationValuesProperty.class)) + { + JAXBElement revocationValuesElem = + unmarshallElement(node, XmlRevocationValuesType.class); + + FromXmlAttributeRevocationValuesConverter revocValuesConverter = + new FromXmlAttributeRevocationValuesConverter(); + + revocValuesConverter.convertFromObject(revocationValuesElem.getValue(), + propertyDataCollector); + + } else if (isNodeTheProperty(node, CounterSignatureProperty.class)) + { + JAXBElement counterSignatureElem = + unmarshallElement(node, XmlCounterSignatureType.class); + + // TODO counter signature not supported + continue; + } else if (isNodeTheProperty(node, ArchiveTimeStampProperty.class)) + { + JAXBElement xmlArchiveTimeStampElem = + unmarshallElement(node, XmlXAdESTimeStampType.class); + + List archivalTimeStamps = + new ArrayList(); + archivalTimeStamps.add(xmlArchiveTimeStampElem.getValue()); + + FromXmlArchiveTimeStampConverter archTSConv = + new FromXmlArchiveTimeStampConverter(); + + archTSConv.convertTimeStamps(archivalTimeStamps , propertyDataCollector); + + } else if (isNodeTheProperty(node, TimeStampValidationDataProperty.class)) + { + JAXBElement xmlTimeStampValidationDataElem = + unmarshallElement(node, XmlValidationDataType.class); + + FromXmlTimeStampValidationDataConverter tsValidationDataConverter = + new FromXmlTimeStampValidationDataConverter(); + + List xmlTimeStampValidationData = + new ArrayList(); + xmlTimeStampValidationData.add(xmlTimeStampValidationDataElem.getValue()); + tsValidationDataConverter.convertFromObject(xmlTimeStampValidationData , + propertyDataCollector); + } else if (!acceptUnknown) // not recognized property + throw new UnmarshalException("Unknown unsigned signature property: " + + node.getLocalName()); + else if (acceptUnknown) + continue; + + propertyDataCollector.linkPropertyToElem((Element)node); + + } + } +} diff --git a/src/main/java/xades4j/xml/unmarshalling/QualifyingPropertiesDataCollector.java b/src/main/java/xades4j/xml/unmarshalling/QualifyingPropertiesDataCollector.java index 67836355..79eddf74 100644 --- a/src/main/java/xades4j/xml/unmarshalling/QualifyingPropertiesDataCollector.java +++ b/src/main/java/xades4j/xml/unmarshalling/QualifyingPropertiesDataCollector.java @@ -16,7 +16,19 @@ */ package xades4j.xml.unmarshalling; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + import xades4j.properties.data.AllDataObjsTimeStampData; +import xades4j.properties.data.ArchiveTimeStampData; +import xades4j.properties.data.AttrAuthoritiesCertValuesData; +import xades4j.properties.data.AttributeRevocationValuesData; +import xades4j.properties.data.CertificateValuesData; +import xades4j.properties.data.PropertyDataObject; +import xades4j.properties.data.RevocationValuesData; +import xades4j.properties.data.SigAndRefsTimeStampData; import xades4j.properties.data.SigningTimeData; import xades4j.properties.data.SigningCertificateData; import xades4j.properties.data.SignatureProdPlaceData; @@ -30,10 +42,11 @@ import xades4j.properties.data.SignaturePolicyData; import xades4j.properties.data.SignatureTimeStampData; import xades4j.properties.data.SignerRoleData; +import xades4j.properties.data.TimeStampValidationDataData; /** * Passed to a {@link QualifyingPropertiesUnmarshaller} to collect the property - * data obejcts. This is used instead of a collection that is returned by the unmarshaller + * data objects. This is used instead of a collection that is returned by the unmarshaller * because it allows controlling the number of occurrences of each property. *

* All the methods will throw {@code PropertyTargetException} if an attempt is made @@ -74,4 +87,42 @@ public void addIndividualDataObjsTimeStamp( public void addGenericDOMData(GenericDOMData domData); public void addOther(OtherPropertyData otherData); + + public void addSigAndRefsTimeStamp(SigAndRefsTimeStampData tsData); + + public void setCertificateValues(CertificateValuesData certificateValuesData); + + public void setRevocationValues(RevocationValuesData revocationValuesData); + + public void setAttrAuthoritiesCertValues( + AttrAuthoritiesCertValuesData attrAuthoritiesCertValuesData); + + public void setAttributeRevocationValues( + AttributeRevocationValuesData attrRevocValData); + + public void addArchiveTimeStamp(ArchiveTimeStampData tsData); + + public void addTimeStampValidationDataData( + TimeStampValidationDataData timeStampValidationDataData); + + public void linkPropertyToElem(Element node); + + /** + * return all properties in Signature in following order: + * SignedSignatureProperties, SignedDataObjectProperties, UnsignedSignatureProperties + * and UnsignedDataObjectProperties. + *

+ * Properties in each of the groups are returned in order in which they are present + * in Signature + * @return + */ + public List getPropertiesData(); + + /** + * Returns XML DOM Node that was parsed to from provided PropertyDataObject + * @param pdo + * @return {@code null} if property is not part of UnsignedSignaturePoperties or was + * not present in unmarshalled Signature + */ + public Node getPropertyNode(PropertyDataObject pdo); } diff --git a/src/main/java/xades4j/xml/unmarshalling/UnmarshallingBindingsModule.java b/src/main/java/xades4j/xml/unmarshalling/UnmarshallingBindingsModule.java index 38619cf9..207457ba 100644 --- a/src/main/java/xades4j/xml/unmarshalling/UnmarshallingBindingsModule.java +++ b/src/main/java/xades4j/xml/unmarshalling/UnmarshallingBindingsModule.java @@ -27,6 +27,6 @@ public final class UnmarshallingBindingsModule extends AbstractModule @Override protected void configure() { - bind(QualifyingPropertiesUnmarshaller.class).to(DefaultQualifyingPropertiesUnmarshaller.class); + bind(QualifyingPropertiesUnmarshaller.class).to(HybridQualifyingPropertiesUnmarshaller.class); } } diff --git a/src/main/java/xades4j/xml/unmarshalling/UnsignedSigPropsModule.java b/src/main/java/xades4j/xml/unmarshalling/UnsignedSigPropsModule.java index 7b349aad..cfb3cfd8 100644 --- a/src/main/java/xades4j/xml/unmarshalling/UnsignedSigPropsModule.java +++ b/src/main/java/xades4j/xml/unmarshalling/UnsignedSigPropsModule.java @@ -32,10 +32,17 @@ class UnsignedSigPropsModule extends UnmarshallerModule - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/src/test/cert/csrc.nist/readme.txt b/src/test/cert/csrc.nist/readme.txt index 8c37478f..ee06642d 100644 --- a/src/test/cert/csrc.nist/readme.txt +++ b/src/test/cert/csrc.nist/readme.txt @@ -19,4 +19,4 @@ More info on the test certificates: http://csrc.nist.gov/groups/ST/crypto_apps_i keytool -importcert -alias TACP0101 -file "Trust Anchor CP.01.01.crt" -keystore trustAnchor -storepass password -keytool -importcert -alias accvCA -file "..\gva\accvroot1.cer" -keystore trustAnchor -storepass password \ No newline at end of file +keytool -importcert -alias accvCA -file "..\gva\accvroot1.cer" -keystore trustAnchor -storepass password diff --git a/src/test/cert/gva/ACCVRAIZ1.crt b/src/test/cert/gva/ACCVRAIZ1.crt new file mode 100644 index 00000000..f036381f --- /dev/null +++ b/src/test/cert/gva/ACCVRAIZ1.crt @@ -0,0 +1,44 @@ +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- diff --git a/src/test/cert/gva/raizaccv1_der.crl b/src/test/cert/gva/raizaccv1_der.crl new file mode 100644 index 00000000..4cb9fa18 Binary files /dev/null and b/src/test/cert/gva/raizaccv1_der.crl differ diff --git a/src/test/cert/gva/raizaccv1_der_29_11_2016.crl b/src/test/cert/gva/raizaccv1_der_29_11_2016.crl new file mode 100644 index 00000000..623a4e31 Binary files /dev/null and b/src/test/cert/gva/raizaccv1_der_29_11_2016.crl differ diff --git a/src/test/cert/gva/readme.txt b/src/test/cert/gva/readme.txt index a58d861c..195ccd63 100644 --- a/src/test/cert/gva/readme.txt +++ b/src/test/cert/gva/readme.txt @@ -9,4 +9,4 @@ The default TSA is from http://www.accv.es/ and its certificate is issued by the ======== Trust-anchors keystore ========= -keytool -importcert -alias accvCA -file "accvroot1.cer" -keystore trustAnchor -storepass password \ No newline at end of file +keytool -importcert -alias accvCA -file "accvroot1.cer" -keystore trustAnchor -storepass password diff --git a/src/test/cert/gva/rootgva_der_07_04_2016.crl b/src/test/cert/gva/rootgva_der_07_04_2016.crl new file mode 100644 index 00000000..3f7af0bb Binary files /dev/null and b/src/test/cert/gva/rootgva_der_07_04_2016.crl differ diff --git a/src/test/cert/gva/trustAnchor b/src/test/cert/gva/trustAnchor index 68d02c74..9ebd3ff0 100644 Binary files a/src/test/cert/gva/trustAnchor and b/src/test/cert/gva/trustAnchor differ diff --git a/src/test/cert/my/readme.txt b/src/test/cert/my/readme.txt index 96c1b994..4ae0679b 100644 --- a/src/test/cert/my/readme.txt +++ b/src/test/cert/my/readme.txt @@ -34,4 +34,4 @@ pvk2pfx.exe -pvk LG.pvk -pi mykeypass -spc LG.cer -pfx LG.pfx --> Starfield Class 2 Certification Authority - keytool -importcert -alias sfCA -file "..\starfield\sf-class2-root.cer" -keystore myStore -storepass mystorepass \ No newline at end of file + keytool -importcert -alias sfCA -file "..\starfield\sf-class2-root.cer" -keystore myStore -storepass mystorepass diff --git a/src/test/java/xades4j/production/SignerBESTest.java b/src/test/java/xades4j/production/SignerBESTest.java index 46449b6d..db3bc276 100644 --- a/src/test/java/xades4j/production/SignerBESTest.java +++ b/src/test/java/xades4j/production/SignerBESTest.java @@ -17,6 +17,13 @@ package xades4j.production; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import org.apache.xml.security.signature.XMLSignatureInput; +import org.apache.xml.security.utils.resolver.ResourceResolver; +import org.apache.xml.security.utils.resolver.ResourceResolverContext; +import org.apache.xml.security.utils.resolver.ResourceResolverException; +import org.apache.xml.security.utils.resolver.ResourceResolverSpi; import xades4j.algorithms.EnvelopedSignatureTransform; import xades4j.properties.DataObjectDesc; import xades4j.properties.AllDataObjsCommitmentTypeProperty; @@ -146,4 +153,5 @@ public void testSignBESDetachedWithXPathAndNamespaces() throws Exception outputDocument(doc, "detached.bes.xml"); } + } diff --git a/src/test/java/xades4j/production/TestResolverSpi.java b/src/test/java/xades4j/production/TestResolverSpi.java new file mode 100644 index 00000000..6bdfd763 --- /dev/null +++ b/src/test/java/xades4j/production/TestResolverSpi.java @@ -0,0 +1,45 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2010 Luis Goncalves. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.production; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import org.apache.xml.security.signature.XMLSignatureInput; +import org.apache.xml.security.utils.resolver.ResourceResolverContext; +import org.apache.xml.security.utils.resolver.ResourceResolverException; +import org.apache.xml.security.utils.resolver.ResourceResolverSpi; + +public class TestResolverSpi extends ResourceResolverSpi { + + @Override + public XMLSignatureInput engineResolveURI(ResourceResolverContext context) throws ResourceResolverException { + try { + String fileName = context.uriToResolve.replace("xades4j://", ""); + return new XMLSignatureInput(new FileInputStream(new File("src/test/xml/" + fileName))); + } catch (FileNotFoundException ex) { + throw new ResourceResolverException(ex.getMessage(), context.uriToResolve, "src/test/xml/"); + } + } + + @Override + public boolean engineCanResolveURI(ResourceResolverContext context) { + return context.attr.getValue().startsWith("xades4j:"); + } + +} diff --git a/src/test/java/xades4j/providers/impl/DefaultTimeStampVerificationProviderTest.java b/src/test/java/xades4j/providers/impl/DefaultTimeStampVerificationProviderTest.java index 46a88e15..f7503396 100644 --- a/src/test/java/xades4j/providers/impl/DefaultTimeStampVerificationProviderTest.java +++ b/src/test/java/xades4j/providers/impl/DefaultTimeStampVerificationProviderTest.java @@ -20,6 +20,7 @@ import java.io.FileInputStream; import java.security.KeyStore; import org.junit.Test; + import xades4j.providers.TimeStampTokenDigestException; import xades4j.providers.TimeStampTokenVerificationException; import xades4j.utils.StreamUtils; @@ -83,12 +84,12 @@ private byte[] getTestToken() throws Exception private void doVerifyToken(byte[] tsDigestInput, byte[] tsToken) throws Exception { KeyStore ks = createAndLoadJKSKeyStore("gva/trustAnchor", "password"); - PKIXCertificateValidationProvider certificateValidationProvider = new PKIXCertificateValidationProvider(ks, false); + PKIXTSACertificateValidationProvider certificateValidationProvider = new PKIXTSACertificateValidationProvider(ks, false); DefaultTimeStampVerificationProvider timeStampVerificationProvider = new DefaultTimeStampVerificationProvider( certificateValidationProvider, new DefaultMessageDigestProvider()); - timeStampVerificationProvider.verifyToken(tsToken, tsDigestInput); + timeStampVerificationProvider.verifyToken(tsToken, tsDigestInput, null); } } diff --git a/src/test/java/xades4j/providers/impl/HttpTimeStampTokenProviderTest.java b/src/test/java/xades4j/providers/impl/HttpTimeStampTokenProviderTest.java index 103f39e8..aed35cc8 100644 --- a/src/test/java/xades4j/providers/impl/HttpTimeStampTokenProviderTest.java +++ b/src/test/java/xades4j/providers/impl/HttpTimeStampTokenProviderTest.java @@ -16,6 +16,10 @@ */ package xades4j.providers.impl; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import org.apache.xml.security.algorithms.MessageDigestAlgorithm; import static org.junit.Assert.assertNotNull; import org.junit.Test; @@ -39,6 +43,19 @@ public void testGetTimeStampToken() throws Exception assertNotNull(result); assertNotNull(result.encodedTimeStampToken); + //updateTestToken(result.encodedTimeStampToken); assertNotNull(result.timeStampTime); } + + private void updateTestToken(byte[] encodedTimeStampToken) throws FileNotFoundException, IOException { + String tokenPath = "./src/test/java/" + this.getClass().getPackage().getName().replace('.', '/') + "/tstoken"; + File file = new File(tokenPath); + FileOutputStream is = new FileOutputStream(file); + try { + is.write(encodedTimeStampToken); + } finally { + is.close(); + } + + } } diff --git a/src/test/java/xades4j/providers/impl/tstoken b/src/test/java/xades4j/providers/impl/tstoken index f09c0d76..47adbeef 100644 Binary files a/src/test/java/xades4j/providers/impl/tstoken and b/src/test/java/xades4j/providers/impl/tstoken differ diff --git a/src/test/java/xades4j/utils/SignatureServicesTestBase.java b/src/test/java/xades4j/utils/SignatureServicesTestBase.java index b82ffcfa..c14213b1 100644 --- a/src/test/java/xades4j/utils/SignatureServicesTestBase.java +++ b/src/test/java/xades4j/utils/SignatureServicesTestBase.java @@ -19,6 +19,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.security.Security; + import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -41,6 +43,7 @@ public class SignatureServicesTestBase { try { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); db = dbf.newDocumentBuilder(); diff --git a/src/test/java/xades4j/verification/AgedTimeStampTest.java b/src/test/java/xades4j/verification/AgedTimeStampTest.java new file mode 100644 index 00000000..d869e788 --- /dev/null +++ b/src/test/java/xades4j/verification/AgedTimeStampTest.java @@ -0,0 +1,3191 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStore.Entry; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.KeyStore.TrustedCertificateEntry; +import java.security.cert.CRLException; +import java.security.cert.CertStore; +import java.security.cert.CertStoreParameters; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.xml.security.utils.Constants; +import org.bouncycastle.asn1.x509.CRLReason; +import org.junit.FixMethodOrder; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import xades4j.XAdES4jException; +import xades4j.production.Enveloped; +import xades4j.production.XadesFormatExtenderProfile; +import xades4j.production.XadesSignatureFormatExtender; +import xades4j.production.XadesSigningProfile; +import xades4j.production.XadesTSigningProfile; +import xades4j.providers.CannotBuildCertificationPathException; +import xades4j.providers.CertificateValidationProvider; +import xades4j.providers.KeyingDataProvider; +import xades4j.providers.TSACertificateValidationProvider; +import xades4j.providers.impl.DirectKeyingDataProvider; +import xades4j.providers.impl.PKIXCertificateValidationProvider; +import xades4j.providers.impl.PKIXTSACertificateValidationProvider; +import xades4j.utils.DOMHelper; +import static xades4j.utils.SignatureServicesTestBase.toPlatformSpecificFilePath; +import static xades4j.utils.SignatureServicesTestBase.toPlatformSpecificXMLDirFilePath; +import xades4j.utils.XadesProfileResolutionException; +import xades4j.verification.FullCert.CRLEntries; + +/** + * Extensive tests of signer and verifiers with special emphasis on testing time stamp + * related corner cases. + * + * @author Hubert Kario + * + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class AgedTimeStampTest +{ + private static final CertStore emptyCertStore; + /* + * first test uses only XAdES-T signatures, Signature is from one CA and TimeStamp is + * signed by TSA from another CA, CRLs are generated on demand in tests + */ + private static FullCert test01_T_userCaCert; + private static FullCert test01_T_tsaCaCert; + private static FullCert test01_T_userCert; + private static FullCert test01_T_tsaCert; + private static X509CRL test01_T_tsaCRL_1; + private static X509CRL test01_T_userCRL_2; + private static X509CRL test01_T_tsaCRL_2; + private static KeyStore test01_T_userTrustAnchors; + private static KeyStore test01_T_tsaTrustAnchors; + + /** + * cryptographic data for signature creation + */ + private static KeyingDataProvider test01_keyingDataProviderNow; + /** + * cryptographic data from 30min ago for validation of certificates + */ + private static CertificateValidationProvider test01_userCertValidationDataProviderPast30m; + /** + * validation data provider that has revocation information from TSA from 30min ago + */ + private static TSACertificateValidationProvider test01_tsaValidationDataProviderPast30m; + /** + * validation data provider that has current revocation information + */ + private static CertificateValidationProvider test01_userCertValidationDataProviderNow; + /** + * validation data provider that has current revocation information for TSA + */ + private static TSACertificateValidationProvider test01_tsaValidationDataProviderNow; + + /* + * data for XAdES-X (SigAndRefsTimeStamp) tests + */ + private static FullCert test02_X_userCaCert; + private static FullCert test02_X_userCert; + private static FullCert test02_X_tsaCaCert; + private static FullCert test02_X_tsa1Cert; + private static FullCert test02_X_tsa2Cert; + private static X509CRL test02_X_tsaCRL_1; + private static X509CRL test02_X_tsaCRL_2; + private static KeyStore test02_X_userTrustAnchors; + private static KeyStore test02_X_tsaTrustAnchors; + + private static KeyingDataProvider test02_keyingDataproviderNow; + private static CertificateValidationProvider test02_userCertValidationDataProviderXCreation; + private static TSACertificateValidationProvider test02_tsaCertValidationDataProviderXCreation; + private static TSACertificateValidationProvider test02_tsaCertValidationDataProviderNow; + private static CertificateValidationProvider test02_userCertMinimalValidationDataProvider; + private static TSACertificateValidationProvider test02_tsaCertMinimalValidationDataProvider; + + /* + * data for XAdES-A (ArchiveTimeStamp) tests + */ + private static FullCert test03_userCaCert; + private static FullCert test03_userCert; + private static FullCert test03_X_tsaCaCert; + private static FullCert test03_A_tsaCaCert; + private static FullCert test03_T_tsa1Cert; + private static FullCert test03_X_tsa2Cert; + private static FullCert test03_A_tsa3Cert; + private static X509CRL test03_X_tsaCRL_1; + private static X509CRL test03_X_tsaCRL_2; + private static X509CRL test03_A_tsaCRL_3; + private static KeyStore test03_userTrustAnchors; + private static KeyStore test03_X_tsaTrustAnchors; + private static KeyStore test03_A_tsaTrustAnchors; + + private static KeyingDataProvider test03_signatureCreationKeyingDataprovider; + private static CertificateValidationProvider test03_userCertValidationDataProviderXCreation; + private static TSACertificateValidationProvider test03_tsaCertValidationDataProviderXCreation; + private static TSACertificateValidationProvider test03_tsaCertValidationDataProviderACreation; + private static TSACertificateValidationProvider test03_tsaCertValidationDataProviderAnow; + /** validation data with only trust anchors (no CRLs or certificates) */ + private static CertificateValidationProvider test03_userCertMinimalValidationDataProvider; + /** validation data with only trust anchors and current CRLs */ + private static TSACertificateValidationProvider test03_tsaCertMinimalValidationDataProvider; + + /* + * data for XAdES-A timestamp tests with multiple time stamps + */ + private static FullCert test04_acmeCA; + private static FullCert test04_acmePersonalCA; + private static FullCert test04_willECoyote; + private static FullCert test04_consterCA; + private static FullCert test04_consterTSA17ya; + private static FullCert test04_ascendeusCA; + private static FullCert test04_ascendeusIssuingCA; + private static FullCert test04_ascendeusTSA17ya; + private static FullCert test04_ascendeusTSA13ya; + private static FullCert test04_carpamaCA; + private static FullCert test04_carpamaTSA13ya; + private static FullCert test04_carpamaTSA9ya; + private static FullCert test04_premoxCA; + private static FullCert test04_premoxTSA9ya; + private static FullCert test04_premoxTSA5ya; + private static FullCert test04_gescapeCA; + private static FullCert test04_gescapeTSA5ya; + private static FullCert test04_unibimCA; + private static FullCert test04_unibimTSA1ya; + private static FullCert test04_astronCA; + private static FullCert test04_astronTSA1ya; + private static CertificateValidationProvider test04_certValidationDataProviderCCreation; + private static CertificateValidationProvider test04_certValidationDataProviderOnlyTrustAnchors; + + private static final long ONE_HOUR_IN_MS = 60 * 60 * 1000; + private static final long ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS; + private static final long ONE_WEEK_IN_MS = 7 * ONE_DAY_IN_MS; + private static final long ONE_YEAR_IN_MS = 52 * ONE_WEEK_IN_MS; + + private static final Date realNow = new Date(); + + static + { + try { + + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + System.out.println("AgedTimeStampTest start, current time is " + realNow); + + CertStoreParameters emptyParams = new CollectionCertStoreParameters(); + emptyCertStore = CertStore.getInstance("Collection", emptyParams ); + + /* ****************************************************************************** + * + * Create cryptographic data for XAdES-T tests + * + * *****************************************************************************/ + createXadesTCerts(realNow); + + /* ****************************************************************************** + * + * Create certs and CRLs for XAdES-X tests + * + * *****************************************************************************/ + + createXadesXCerts(realNow); + + /* ****************************************************************************** + * + * Create certs and CRLs for XAdES-A tests + * + * *****************************************************************************/ + + createXadesACerts(realNow); + + /* ****************************************************************************** + * + * Create certs for XAdES-A with multiple time stamps + * + * *****************************************************************************/ + + createXadesAmultTSACerts(realNow); + + } catch (Exception ex) + { + throw new RuntimeException("static initialization failed", ex); + } + } + + private static void createXadesTCerts(Date now) throws Exception, + CertificateEncodingException, IOException, CRLException, + KeyStoreException, NoSuchAlgorithmException, CertificateException, + InvalidAlgorithmParameterException, NoSuchProviderException + { + X509CRL crl; + test01_T_userCaCert = FullCert.getCACert("RSA", 1024, "CN=XAdES4j Testing CA", + new Date(now.getTime() - ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + "SHA256withRSA"); /* cert will have serial number: 1 */ + saveCertificate("ca.cer", test01_T_userCaCert.getCertificate()); + System.out.println("CA validity from " + + test01_T_userCaCert.getCertificate().getNotBefore() + + " to " + test01_T_userCaCert.getCertificate().getNotAfter()); + + test01_T_tsaCaCert = FullCert.getCACert("RSA", 1024, "CN=XAdES4j TSA Testing CA", + new Date(now.getTime() - ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + "SHA256withRSA"); /* cert will have serial number: 1 */ + saveCertificate("tsaCA.cer", test01_T_userCaCert.getCertificate()); + System.out.println("TSA CA validity from " + + test01_T_userCaCert.getCertificate().getNotBefore() + + " to " + test01_T_userCaCert.getCertificate().getNotAfter()); + + test01_T_userCert = test01_T_userCaCert.createUserCert("RSA", 1024, + "CN=User Certificate", + new Date(now.getTime() - ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS/2), + new BigInteger("2"), "SHA256withRSA"); + saveCertificate("user.cer", test01_T_userCert.getCertificate()); + System.out.println("user validity from " + + test01_T_userCert.getCertificate().getNotBefore() + + " to " + test01_T_userCert.getCertificate().getNotAfter()); + + test01_T_tsaCert = test01_T_tsaCaCert.createTSACert("RSA", 1024, + "CN=XAdES4j Testing TSA", + new Date(now.getTime() - ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS/2), + new BigInteger("3"), "SHA256withRSA"); + saveCertificate("tsa.cer", test01_T_tsaCert.getCertificate()); + System.out.println("TSA validity from " + + test01_T_tsaCert.getCertificate().getNotBefore() + + " to " + test01_T_tsaCert.getCertificate().getNotAfter()); + + CRLEntries entries = test01_T_userCaCert.new CRLEntries(); + // add fictional entry + entries.addEntry(new BigInteger("134"), realNow, CRLReason.keyCompromise); + + crl = test01_T_userCaCert.createCRL("SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS/2), + new Date(now.getTime() + ONE_HOUR_IN_MS/4), + new BigInteger("2"), + entries); + saveCRL("ca.crl", crl); + + test01_T_tsaCRL_1 = test01_T_tsaCaCert.createCRL("SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS/2), + new Date(now.getTime() + ONE_HOUR_IN_MS/4), + new BigInteger("2"), + entries); + saveCRL("tsaCA.crl", test01_T_tsaCRL_1); + + test01_T_userCRL_2 = test01_T_userCaCert.createCRL("SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS/60), + new Date(now.getTime() + ONE_HOUR_IN_MS/3), + new BigInteger("3"), + entries); + saveCRL("ca-3.crl", test01_T_userCRL_2); + + test01_T_tsaCRL_2 = test01_T_tsaCaCert.createCRL("SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS/60), + new Date(now.getTime() + ONE_HOUR_IN_MS/3), + new BigInteger("3"), + entries); + saveCRL("tsaCA-3.crl", test01_T_tsaCRL_2); + + test01_keyingDataProviderNow = + new DirectKeyingDataProvider(test01_T_userCert.getCertificate(), + test01_T_userCert.getPrivateKey()); + + /* + * Create validation data providers for past + */ + // create current trust anchors + test01_T_userTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test01_T_userTrustAnchors.load(null); + TrustedCertificateEntry ca = + new TrustedCertificateEntry(test01_T_userCaCert.getCertificate()); + test01_T_userTrustAnchors.setEntry("ca", ca, null); + // create store with additional certificates and CRLs + Collection content = new ArrayList(); + content.add(crl); + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + // create validation provider with revocation information from 30m ago + test01_userCertValidationDataProviderPast30m = + new PKIXCertificateValidationProvider(test01_T_userTrustAnchors, + true, intermCertsAndCrls); + + // create current trust anchors + test01_T_tsaTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test01_T_tsaTrustAnchors.load(null); + TrustedCertificateEntry tsaCA = + new TrustedCertificateEntry(test01_T_tsaCaCert.getCertificate()); + test01_T_tsaTrustAnchors.setEntry("ca", tsaCA, null); + // create store with additional certificates and CRLs + content = new ArrayList(); + content.add(test01_T_tsaCRL_1); + content.add(test01_T_tsaCert.getCertificate()); // tsa cert is not added to token + intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + // create validation provider with revocation information from 30m ago + test01_tsaValidationDataProviderPast30m = new PKIXTSACertificateValidationProvider( + test01_T_tsaTrustAnchors, true, intermCertsAndCrls); + + /* + * create validation data providers for now + */ + // create store with additional certificates and CRLs + content = new ArrayList(); + content.add(test01_T_userCRL_2); + intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + // create validation provider with revocation information from now + test01_userCertValidationDataProviderNow = + new PKIXCertificateValidationProvider(test01_T_userTrustAnchors, + true, intermCertsAndCrls); + + // create store with additional certificates and CRLs + content = new ArrayList(); + content.add(test01_T_tsaCRL_2); + content.add(test01_T_tsaCert.getCertificate()); // tsa cert is not added to token + CertStore tsaIntermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + // create validation provider with revocation information for TSA + test01_tsaValidationDataProviderNow = new PKIXTSACertificateValidationProvider( + test01_T_tsaTrustAnchors, true, tsaIntermCertsAndCrls); + } + + private static void createXadesAmultTSACerts(Date now) + throws Exception + { + test04_acmeCA = FullCert.getCACert( + "RSA", + 1024, + "CN=ACME Certification Services CA", + new Date(now.getTime() - 20 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 10 * ONE_YEAR_IN_MS), + "SHA1withRSA"); + + test04_acmePersonalCA = test04_acmeCA.createSubCACert( + "RSA", + 1024, + "CN=ACME Personal Certificates CA", + new Date(now.getTime() - 18 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 13 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA1withRSA"); + + test04_willECoyote = test04_acmePersonalCA.createUserCert( + "RSA", + 1024, + "CN=Will E. Coyote", + new Date(now.getTime() - 18 * ONE_YEAR_IN_MS + 45 * ONE_WEEK_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("2"), + "SHA1withRSA"); + + test04_consterCA = FullCert.getCACert( + "RSA", + 1024, + "CN=Conster CA", + new Date(now.getTime() - 18 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 8 * ONE_YEAR_IN_MS), + "SHA1withRSA"); + + test04_consterTSA17ya = test04_consterCA.createTSACert( + "RSA", + 1024, + "CN=Conster Time Server", + new Date(now.getTime() - 17 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 12 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA1withRSA"); + + test04_ascendeusCA = FullCert.getCACert( + "RSA", + 1024, + "CN=Ascendeus Root CA", + new Date(now.getTime() - 17 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 7 * ONE_YEAR_IN_MS), + "SHA1withRSA"); + + test04_ascendeusIssuingCA = test04_ascendeusCA.createSubCACert( + "RSA", + 1024, + "CN=Ascendeus Issuing CA", + new Date(now.getTime() - 17 * ONE_YEAR_IN_MS - 3 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 7 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA1withRSA"); + + test04_ascendeusTSA17ya = test04_ascendeusIssuingCA.createTSACert( + "RSA", + 1024, + "CN=Ascendeus Time Services", + new Date(now.getTime() - 17 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 12 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA1withRSA"); + + test04_ascendeusTSA13ya = test04_ascendeusIssuingCA.createTSACert( + "RSA", + 1024, + "CN=Ascendeus Time Services", + new Date(now.getTime() - 13 * ONE_YEAR_IN_MS), + new Date(now.getTime() - 8 * ONE_YEAR_IN_MS), + new BigInteger("3"), + "SHA1withRSA"); + + test04_carpamaCA = FullCert.getCACert( + "RSA", + 1024, + "CN=Carpama Certificate Authority", + new Date(now.getTime() - 13 * ONE_YEAR_IN_MS - 12 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 3 * ONE_YEAR_IN_MS), + "SHA1withRSA"); + + test04_carpamaTSA13ya = test04_carpamaCA.createTSACert( + "RSA", + 1024, + "CN=Carpama Time Server", + new Date(now.getTime() - 13 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 8 * ONE_YEAR_IN_MS), + new BigInteger("3"), + "SHA1withRSA"); + + test04_carpamaTSA9ya = test04_carpamaCA.createTSACert( + "RSA", + 1024, + "CN=Carpama Time Server", + new Date(now.getTime() - 9 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 4 * ONE_YEAR_IN_MS), + new BigInteger("4"), + "SHA1withRSA"); + + test04_premoxCA = FullCert.getCACert( + "RSA", + 1024, + "CN=Premox CA", + new Date(now.getTime() - 9 * ONE_YEAR_IN_MS - 12 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 1 * ONE_YEAR_IN_MS), + "SHA256withRSA"); + + test04_premoxTSA9ya = test04_premoxCA.createTSACert( + "RSA", + 1024, + "CN=Premox TSA", + new Date(now.getTime() - 9 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 4 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + test04_premoxTSA5ya = test04_premoxCA.createTSACert( + "RSA", + 1024, + "CN=Premox TSA", + new Date(now.getTime() - 5 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() - 6 * ONE_WEEK_IN_MS), + new BigInteger("3"), + "SHA256withRSA"); + + test04_gescapeCA = FullCert.getCACert( + "RSA", + 1024, + "CN=Gescape Certificate Authority", + new Date(now.getTime() - 5 * ONE_YEAR_IN_MS - 12 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 6 * ONE_WEEK_IN_MS), + "SHA256withRSA"); + + test04_gescapeTSA5ya = test04_gescapeCA.createTSACert( + "RSA", + 1024, + "CN=Gescape Time Stamping Authority", + new Date(now.getTime() - 5 * ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 6 * ONE_WEEK_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + test04_unibimCA = FullCert.getCACert( + "RSA", + 2048, + "CN=Unibim CA", + new Date(now.getTime() - ONE_YEAR_IN_MS - 12 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 9 * ONE_YEAR_IN_MS), + "SHA256withRSA"); + + test04_unibimTSA1ya = test04_unibimCA.createTSACert( + "RSA", + 2048, + "CN=Unibim TSA", + new Date(now.getTime() - ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 4 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + test04_astronCA = FullCert.getCACert( + "RSA", + 2048, + "CN=Astron CA", + new Date(now.getTime() - ONE_YEAR_IN_MS - 12 * ONE_WEEK_IN_MS), + new Date(now.getTime() + 9 * ONE_YEAR_IN_MS), + "SHA256withRSA"); + + test04_astronTSA1ya = test04_astronCA.createTSACert( + "RSA", + 2048, + "CN=Astron TSA", + new Date(now.getTime() - ONE_YEAR_IN_MS - 6 * ONE_WEEK_IN_MS - 1 * ONE_DAY_IN_MS), + new Date(now.getTime() + 4 * ONE_YEAR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + Date cCreatTime = new Date(now.getTime() - 17 * ONE_YEAR_IN_MS + + 16 * ONE_DAY_IN_MS); + CRLEntries emptyEntries = test04_acmeCA.new CRLEntries(); + KeyStore trustAnchors = keyStoreForCerts(test04_acmeCA); + X509CRL acmeCRL = test04_acmeCA.createCRL( + "SHA1withRSA", + new Date(cCreatTime.getTime() - 3 * ONE_DAY_IN_MS), + new Date(now.getTime() + 4 * ONE_DAY_IN_MS), + new BigInteger("3"), + emptyEntries); + X509CRL acmePersonalCRL = test04_acmePersonalCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 8 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 16 * ONE_HOUR_IN_MS), + new BigInteger("3"), + emptyEntries); + Collection crls = createCRLCollection(acmeCRL, acmePersonalCRL); + CertStore intermCertsAndCrls = certStoreForCertsAndCrls(crls, + test04_acmeCA.getCertificate(), + test04_acmePersonalCA.getCertificate()); + test04_certValidationDataProviderCCreation = + new PKIXCertificateValidationProvider(trustAnchors, true, intermCertsAndCrls); + + test04_certValidationDataProviderOnlyTrustAnchors = + new PKIXCertificateValidationProvider(trustAnchors, true, emptyCertStore); + } + + private static void createXadesXCerts(Date now) throws Exception, + CertificateEncodingException, KeyStoreException, IOException, + NoSuchAlgorithmException, CertificateException, + InvalidAlgorithmParameterException, NoSuchProviderException + { + X509CRL crl; + TrustedCertificateEntry ca; + Collection content; + CertStore intermCertsAndCrls; + TrustedCertificateEntry tsaCA; + test02_X_userCaCert = FullCert.getCACert( + "RSA", + 1024, + "CN=XAdES4j XAdES-X User CA", + new Date(now.getTime() - ONE_HOUR_IN_MS * 24), + new Date(now.getTime() - ONE_HOUR_IN_MS * 16), + "SHA256withRSA"); + System.out.println("\"" + test02_X_userCaCert.getCertificate().getSubjectDN() + + "\" validity from " + test02_X_userCaCert.getCertificate().getNotBefore() + + " to " + test02_X_userCaCert.getCertificate().getNotAfter()); + + test02_X_tsaCaCert = FullCert.getCACert( + "RSA", + 1024, + "CN=XAdES4j XAdES-X TSA Testing CA", + new Date(now.getTime() - ONE_HOUR_IN_MS * 23), + new Date(now.getTime() + ONE_HOUR_IN_MS * 2), + "SHA256withRSA"); + System.out.println("\"" + test02_X_tsaCaCert.getCertificate().getSubjectDN() + + "\" validity from " + test02_X_tsaCaCert.getCertificate().getNotBefore() + + " to " + test02_X_tsaCaCert.getCertificate().getNotAfter()); + + test02_X_userCert = test02_X_userCaCert.createUserCert( + "RSA", + 1024, + "CN=XAdES4j test user", + new Date(now.getTime() - ONE_HOUR_IN_MS * 22), + // should be -16h, temporary fix for problems in xades4j when creating + // signature with past times, won't matter as the CA is valid to -16h + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + System.out.println("user certificate \"" + + test02_X_userCert.getCertificate().getSubjectDN() + + "\" validity from " + test02_X_userCert.getCertificate().getNotBefore() + + " to " + test02_X_userCert.getCertificate().getNotAfter()); + + + test02_X_tsa1Cert = test02_X_tsaCaCert.createTSACert( + "RSA", + 1024, + "CN=XAdES4j XAdES-X TSA 1", + new Date(now.getTime() - ONE_HOUR_IN_MS * 21), + new Date(now.getTime() - ONE_HOUR_IN_MS * 12), + new BigInteger("2"), + "SHA256withRSA"); + System.out.println("TSA certificate " + + test02_X_tsa1Cert.getCertificate().getSubjectDN().toString() + + " is valid from " + + test02_X_tsa1Cert.getCertificate().getNotBefore() + + " to " + + test02_X_tsa1Cert.getCertificate().getNotAfter()); + + CRLEntries entries_test02 = test02_X_userCaCert.new CRLEntries(); + entries_test02.addEntry( + test02_X_userCert.getCertificate().getSerialNumber(), + new Date(now.getTime() - ONE_HOUR_IN_MS * 18), + CRLReason.affiliationChanged); + + crl = test02_X_userCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS * 18), + new Date(now.getTime() - ONE_HOUR_IN_MS * 17), + new BigInteger("1"), + entries_test02); + + test02_X_tsa2Cert = test02_X_tsaCaCert.createTSACert( + "RSA", + 1024, + "CN=XAdES4j XAdES-X TSA 2", + new Date(now.getTime() - ONE_HOUR_IN_MS * 15), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("3"), + "SHA256withRSA"); + System.out.println("Certificate " + + test02_X_tsa2Cert.getCertificate().getSubjectDN().toString() + + " is valid from " + + test02_X_tsa2Cert.getCertificate().getNotBefore() + + " to " + + test02_X_tsa2Cert.getCertificate().getNotAfter()); + + entries_test02 = test02_X_tsaCaCert.new CRLEntries(); + + test02_X_tsaCRL_1 = test02_X_tsaCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS * 19), + new Date(now.getTime() - ONE_HOUR_IN_MS * 13), + new BigInteger("4"), + entries_test02); + + entries_test02.addEntry( + test02_X_tsa1Cert.getCertificate().getSerialNumber(), + new Date(now.getTime() - ONE_HOUR_IN_MS * 13), + CRLReason.cessationOfOperation); + + test02_X_tsaCRL_2 = test02_X_tsaCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - ONE_HOUR_IN_MS * 13), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("5"), + entries_test02); + + test02_X_userTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test02_X_userTrustAnchors.load(null); + ca = new TrustedCertificateEntry(test02_X_userCaCert.getCertificate()); + test02_X_userTrustAnchors.setEntry("ca", ca, null); + + test02_X_tsaTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test02_X_tsaTrustAnchors.load(null); + tsaCA = new TrustedCertificateEntry(test02_X_tsaCaCert.getCertificate()); + test02_X_tsaTrustAnchors.setEntry("tsaCA", tsaCA, null); + + test02_keyingDataproviderNow = new DirectKeyingDataProvider( + test02_X_userCert.getCertificate(), + test02_X_userCert.getPrivateKey()); + + content = new ArrayList(); + content.add(crl); + + intermCertsAndCrls = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test02_userCertValidationDataProviderXCreation = + new PKIXCertificateValidationProvider( + test02_X_userTrustAnchors, + true, + intermCertsAndCrls); + + content = new ArrayList(); + content.add(test02_X_tsaCRL_1); + content.add(test02_X_tsaCRL_2); + content.add(test02_X_tsa1Cert.getCertificate()); + + intermCertsAndCrls = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test02_tsaCertValidationDataProviderXCreation = + new PKIXTSACertificateValidationProvider( + test02_X_tsaTrustAnchors, + true, + intermCertsAndCrls); + + content = new ArrayList(); + content.add(test02_X_tsa1Cert.getCertificate()); + content.add(test02_X_tsa2Cert.getCertificate()); + content.add(test02_X_tsaCRL_1); + content.add(test02_X_tsaCRL_2); + + intermCertsAndCrls = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test02_tsaCertValidationDataProviderNow = + new PKIXTSACertificateValidationProvider( + test02_X_tsaTrustAnchors, + true, + intermCertsAndCrls); + + test02_userCertMinimalValidationDataProvider = + new PKIXCertificateValidationProvider(test02_X_userTrustAnchors, + true, + emptyCertStore); + content = new ArrayList(); + content.add(test02_X_tsaCRL_2); + + intermCertsAndCrls = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + test02_tsaCertMinimalValidationDataProvider = + new PKIXTSACertificateValidationProvider(test02_X_tsaTrustAnchors, + true, + emptyCertStore); + } + + private static void createXadesACerts(Date now) + throws Exception, CertificateEncodingException, KeyStoreException, + IOException, NoSuchAlgorithmException, CertificateException, + InvalidAlgorithmParameterException, NoSuchProviderException + { + test03_userCaCert = FullCert.getCACert( + "RSA", + 1024, + "CN=XAdES-A testing User CA", + new Date(now.getTime() - 24 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 17 * ONE_HOUR_IN_MS), + "SHA256withRSA"); + + test03_X_tsaCaCert = FullCert.getCACert( + "RSA", + 1024, + "CN=XAdES-A testing X form TSA CA", + new Date(now.getTime() - 23 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 9 * ONE_HOUR_IN_MS), + "SHA256withRSA"); + + test03_userCert = test03_userCaCert.createUserCert( + "RSA", + 1024, + "CN=XAdes-A testing User cert", + new Date(now.getTime() - 22 * ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + test03_T_tsa1Cert = test03_X_tsaCaCert.createTSACert( + "RSA", + 1024, + "CN=XAdES-A testing T form TSA", + new Date(now.getTime() - 21 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + CRLEntries tsaCaCrlEntries = test03_X_tsaCaCert.new CRLEntries(); + test03_X_tsaCRL_1 = test03_X_tsaCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 19 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 13 * ONE_HOUR_IN_MS), + new BigInteger("1"), + tsaCaCrlEntries); + + CRLEntries userCaCrlEntries = test03_userCaCert.new CRLEntries(); + userCaCrlEntries.addEntry( + test03_userCert.getCertificate().getSerialNumber(), + new Date(now.getTime() - 18 * ONE_HOUR_IN_MS), + CRLReason.affiliationChanged); + X509CRL userCaCrl = test03_userCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 18 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 17 * ONE_HOUR_IN_MS), + new BigInteger("1"), + userCaCrlEntries); + + test03_X_tsa2Cert = test03_X_tsaCaCert.createTSACert( + "RSA", + 1024, + "CN=XAdES-A testing X form TSA", + new Date(now.getTime() - 15 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 9 * ONE_HOUR_IN_MS), + new BigInteger("3"), + "SHA256withRSA"); + + tsaCaCrlEntries.addEntry( + test03_T_tsa1Cert.getCertificate().getSerialNumber(), + new Date(now.getTime() - 13 * ONE_HOUR_IN_MS), + CRLReason.affiliationChanged); + test03_X_tsaCRL_2 = test03_X_tsaCaCert.createCRL("SHA256withRSA", + new Date(now.getTime() - 13 * ONE_HOUR_IN_MS), + new Date(now.getTime() - 9 * ONE_HOUR_IN_MS), + new BigInteger("3"), + tsaCaCrlEntries); + + test03_A_tsaCaCert = FullCert.getCACert( + "RSA", + 1024, + "CN=XAdES-A testing A form TSA CA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + "SHA256withRSA"); + + test03_A_tsa3Cert = test03_A_tsaCaCert.createTSACert( + "RSA", + 1024, + "CN=XAdES-A testing A form TSA", + new Date(now.getTime() - 11 * ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("2"), + "SHA256withRSA"); + + tsaCaCrlEntries = test03_A_tsaCaCert.new CRLEntries(); + test03_A_tsaCRL_3 = test03_A_tsaCaCert.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 1 * ONE_HOUR_IN_MS), + new Date(now.getTime() + ONE_HOUR_IN_MS), + new BigInteger("1"), + tsaCaCrlEntries); + + test03_signatureCreationKeyingDataprovider = new DirectKeyingDataProvider( + test03_userCert.getCertificate(), + test03_userCert.getPrivateKey()); + + test03_userTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test03_userTrustAnchors.load(null); + TrustedCertificateEntry ca = new TrustedCertificateEntry(test03_userCaCert.getCertificate()); + test03_userTrustAnchors.setEntry("ca", ca, null); + + List content = new ArrayList(); + content.add(userCaCrl); + CertStore userIntermCertsAndCrlsXCreation = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test03_userCertValidationDataProviderXCreation = + new PKIXCertificateValidationProvider(test03_userTrustAnchors, + true, + userIntermCertsAndCrlsXCreation); + + test03_X_tsaTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test03_X_tsaTrustAnchors.load(null); + ca = new TrustedCertificateEntry(test03_X_tsaCaCert.getCertificate()); + test03_X_tsaTrustAnchors.setEntry("ca", ca, null); + + content = new ArrayList(); + content.add(test03_T_tsa1Cert.getCertificate()); + content.add(test03_X_tsaCRL_1); + CertStore xTsaIntermCertsAndCrlsXCreation = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test03_tsaCertValidationDataProviderXCreation = + new PKIXTSACertificateValidationProvider( + test03_X_tsaTrustAnchors, + true, + xTsaIntermCertsAndCrlsXCreation); + + test03_A_tsaTrustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + test03_A_tsaTrustAnchors.load(null); + ca = new TrustedCertificateEntry(test03_X_tsaCaCert.getCertificate()); + test03_A_tsaTrustAnchors.setEntry("ca", ca, null); + ca = new TrustedCertificateEntry(test03_A_tsaCaCert.getCertificate()); + test03_A_tsaTrustAnchors.setEntry("newCA", ca, null); + + content = new ArrayList(); + content.add(test03_T_tsa1Cert.getCertificate()); + content.add(test03_X_tsa2Cert.getCertificate()); + content.add(test03_X_tsaCRL_1); + content.add(test03_X_tsaCRL_2); + CertStore aTsaIntermCertsAndCrlsACreation = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + test03_tsaCertValidationDataProviderACreation = + new PKIXTSACertificateValidationProvider( + test03_A_tsaTrustAnchors, + true, + aTsaIntermCertsAndCrlsACreation); + + content = new ArrayList(); + content.add(test03_A_tsa3Cert.getCertificate()); + content.add(test03_A_tsaCRL_3); + + CertStore aValidationCertsAndCrls = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + + test03_tsaCertValidationDataProviderAnow = + new PKIXTSACertificateValidationProvider( + test03_A_tsaTrustAnchors, + true, + aValidationCertsAndCrls); + + test03_userCertMinimalValidationDataProvider = + new PKIXCertificateValidationProvider( + test03_userTrustAnchors, + true, + emptyCertStore); + + content = new ArrayList(); + content.add(test03_A_tsaCRL_3); + CertStore aValidationCrlsOnly = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(content)); + test03_tsaCertMinimalValidationDataProvider = + new PKIXTSACertificateValidationProvider( + test03_A_tsaTrustAnchors, + true, + aValidationCrlsOnly); + } + + @Test + public void init() throws Exception + { + // cause the static initializer to run + return; + } + + + /* + * Generator creates signature with certificates and time stamps created in the past + * + * ^ + * | <-- in 1h: + * | caCert validity end + * | + * | <-- in 30 min: + * | userCert validity end + * | tsaCert validity end + * | CRL (3) validity end + * | + * | <-- in 15 min: + * | CRL (2) validity end + * | + * | <-- *now* (signature validation) + * | + * | <- 1m ago: + * | CRL (3) creation + * | + * | <- 30 min ago: + * | CRL (2) creation + * | time stamp token creation + * | + * | <-- 1h ago: + * | caCert creation + * | userCert creation + * | tsaCert creation + */ + + // test creation of document with time stamp in the past + @Test + public void test01_01_T_sig1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // test signing + SurrogateTimeStampTokenProvider.setTSACert(test01_T_tsaCert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + new Date(realNow.getTime() - ONE_HOUR_IN_MS/2), + new BigInteger("3")); + System.out.println("SignatureTimeStamp creation date in " + + "\"document.aged.testT_1s\" is " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS/2)); + + Document doc = getDocument("document.xml"); + Element elemToSign = doc.getDocumentElement(); + XadesSigningProfile signer = new XadesTSigningProfile(test01_keyingDataProviderNow); + signer.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + new Enveloped(signer.newSigner()).sign(elemToSign); + + outputDocument(doc, "document.aged.testT_1s.xml"); + } + + // test if document can be validated using revocation information published before + // time stamp generation (TODO should fail if we support grace period) + @Test + public void test01_02_T_ver1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // test verification + XAdESForm f = verifySignature("document.aged.testT_1s.xml", + new XadesVerificationProfile(test01_userCertValidationDataProviderPast30m, + test01_tsaValidationDataProviderPast30m)); + assertEquals(XAdESForm.T, f); + } + + // test if document can be validated using current revocation information + @Test + public void test01_03_T_ver2() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // test verification + XAdESForm f = verifySignature("document.aged.testT_1s.xml", + new XadesVerificationProfile(test01_userCertValidationDataProviderNow, + test01_tsaValidationDataProviderNow)); + assertEquals(XAdESForm.T, f); + } + + // test if document can be validated if user certificate was revoked after + // signature was time stamped + // XXX fails if the CertPathBuilder considers certificate to be revoked before their + // revocation date + // BC provider has such behavior for revocation reason unspecified, keyCompromise, + // aACompromise and few others + @Test + @Ignore + public void test01_04_T_ver3() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + CRLEntries entries = test01_T_userCaCert.new CRLEntries(); + + // revoke user certificate 15 min ago + entries.addEntry(test01_T_userCert.getCertificate().getSerialNumber(), + new Date(realNow.getTime() - ONE_HOUR_IN_MS/4), CRLReason.unspecified); + System.out.println("User certificate revoked at " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS/4)); + + // create CRL + X509CRL revokedCerts = test01_T_userCaCert.createCRL("SHA1withRSA", + new Date(realNow.getTime() - ONE_HOUR_IN_MS/60), // generated 1 min ago + new Date(realNow.getTime() + ONE_HOUR_IN_MS/2), // nextUpdate in 30 min + new BigInteger("4"), + entries); + + // create validation provider with CRL with revoked user cert + Collection content = new ArrayList(); + content.add(revokedCerts); + content.add(test01_T_tsaCert.getCertificate()); // tsa cert is not added to token + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + CertificateValidationProvider cvp = new PKIXCertificateValidationProvider( + test01_T_userTrustAnchors, true, intermCertsAndCrls); + + XAdESForm f = verifySignature("document.aged.testT_1s.xml", + new XadesVerificationProfile(cvp, + test01_tsaValidationDataProviderNow)); + assertEquals(XAdESForm.T, f); + } + + // test if document can be validated if user certificate was revoked after + // signature was time stamped + // exact same test as testT_3v, with the change to a more "harmless" revocation + // reason than unspecified: affiliation changed. + @Test + public void test01_05_T_ver3_1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + CRLEntries entries = test01_T_userCaCert.new CRLEntries(); + + // revoke user certificate 15 min ago + entries.addEntry(test01_T_userCert.getCertificate().getSerialNumber(), + new Date(realNow.getTime() - ONE_HOUR_IN_MS/4), CRLReason.affiliationChanged); + System.out.println("User certificate revoked at " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS/4)); + + // create CRL + X509CRL revokedCerts = test01_T_userCaCert.createCRL("SHA1withRSA", + new Date(realNow.getTime() - ONE_HOUR_IN_MS/60), // generated 1 min ago + new Date(realNow.getTime() + ONE_HOUR_IN_MS/2), // nextUpdate in 30 min + new BigInteger("4"), + entries); + + // create validation provider with CRL with revoked user cert + Collection content = new ArrayList(); + content.add(revokedCerts); + content.add(test01_T_tsaCert.getCertificate()); // tsa cert is not added to token + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + CertificateValidationProvider cvp = new PKIXCertificateValidationProvider( + test01_T_userTrustAnchors, true, intermCertsAndCrls); + + XAdESForm f = verifySignature("document.aged.testT_1s.xml", + new XadesVerificationProfile(cvp, + test01_tsaValidationDataProviderNow)); + assertEquals(XAdESForm.T, f); + } + + + // test if document can be validated if user certificate was revoked before + // signature was time stamped + @Test(expected = CannotBuildCertificationPathException.class) + public void test01_06_T_ver4() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + CRLEntries entries = test01_T_userCaCert.new CRLEntries(); + + // revoke user certificate 45 min ago + entries.addEntry(test01_T_userCert.getCertificate().getSerialNumber(), + new Date(realNow.getTime() - 1000*60*45), CRLReason.unspecified); + System.out.println("User certificate revoked at " + + new Date(realNow.getTime() - 1000*60*45)); + + // create CRL + X509CRL revokedCerts = test01_T_userCaCert.createCRL("SHA1withRSA", + new Date(realNow.getTime() - 1000*60), // generated 1 min ago + new Date(realNow.getTime() + 1000*60*30), // nextUpdate in 30 min + new BigInteger("4"), + entries); + + // create validation provider with CRL with revoked user cert + Collection content = new ArrayList(); + content.add(revokedCerts); + content.add(test01_T_tsaCert.getCertificate()); // tsa cert is not added to token + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + CertificateValidationProvider cvp = new PKIXCertificateValidationProvider( + test01_T_userTrustAnchors, true, intermCertsAndCrls); + + XAdESForm f = verifySignature("document.aged.testT_1s.xml", + new XadesVerificationProfile(cvp, test01_tsaValidationDataProviderNow)); + assertEquals(XAdESForm.T, f); + } + + /* SigAndRefsTimeStamp tests + * + * worst case scenario for validation that still should validate successfully + */ + /* t + * -24 .User CA validity + * -23 | .TSA CA validity + * -22 | | .User cert validity + * -21 | | | .TSA1 cert validity + * | | | | + * -20 | | | | <---- XAdES-T time stamp (TSA1) + * | | | | + * -19 | | | | .TSA CA 1st CRL + * -18 | | | | | .User CA 1st CRL (user cert revocation) + * | | | | | | + * -17 ' | ' | | ' + * -15 | | | .TSA2 cert validity + * | | | | + * -14 | | | | <---- XAdES-X time stamp (TSA2) + * | | | | + * -13 | | ' | .TSA CA 2nd CRL (TSA1 cert revocation) + * -12 | ' | | + * 0 | | | <---- (now) validation + * 1 | ' ' + * 2 ' + */ + + // create basic XAdES-T signed document + @Test + public void test02_01_X_sig1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test02_X_sig1.xml"); + removeFile(outFileName); + + SurrogateTimeStampTokenProvider.setTSACert(test02_X_tsa1Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 20), + new BigInteger("1")); + System.out.println("SignatureTimeStamp creation date is " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 20)); + + Document doc = getDocument("document.xml"); + Element elemToSign = doc.getDocumentElement(); + XadesSigningProfile signer = new XadesTSigningProfile(test02_keyingDataproviderNow); + signer.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + new Enveloped(signer.newSigner()).sign(elemToSign); + + outputDocument(doc, outFileName); + } + + // extend T form to X form + @Test + public void test02_02_X_sig2() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test02_X_sig2.xml"); + removeFile(outFileName); + + SurrogateTimeStampTokenProvider.setTSACert(test02_X_tsa2Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 14), + new BigInteger("2")); + + System.out.println("SigAndRefsTimeStamp creation date is " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 14)); + + Document doc = getOutputDocument("document.aged.test02_X_sig1.xml"); + Element signatureNode = getSigElement(doc); + + /* + * extension of signature to X form must be performed in two steps, first we have + * to create the XML with needed Properties (CompleteRevocationRefs and + * CompleteCertificateRefs, that is, C form) and only after that we can add the + * X form time stamp + */ + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test02_userCertValidationDataProviderXCreation, + test02_tsaCertValidationDataProviderXCreation); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + Date now = new Date(realNow.getTime() - ONE_HOUR_IN_MS * 14); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + // extend T to C + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.C); + + assertEquals(res.getSignatureForm(), XAdESForm.T); + + // extend C to X + res = verifier.verify(signatureNode, options, formExt, XAdESForm.X); + + assertEquals(res.getSignatureForm(), XAdESForm.C); + + outputDocument(doc, outFileName); + } + + // extend X to X-L form + @Test + public void test02_03_X_sig3() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test02_X_sig3.xml"); + removeFile(outFileName); + + Document doc = getOutputDocument("document.aged.test02_X_sig2.xml"); + Element signatureNode = getSigElement(doc); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test02_userCertValidationDataProviderXCreation, + test02_tsaCertValidationDataProviderNow); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + // extend X to X-L + XAdESVerificationResult res = verifier.verify(signatureNode, null, formExt, + XAdESForm.X_L); + + assertEquals(res.getSignatureForm(), XAdESForm.X); + + outputDocument(doc, outFileName); + } + + // verify if the X form was properly created + @Test + public void test02_04_X_ver1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // test verification + XAdESForm f = verifySignature("document.aged.test02_X_sig2.xml", + new XadesVerificationProfile(test02_userCertValidationDataProviderXCreation, + test02_tsaCertValidationDataProviderNow)); + + assertEquals(XAdESForm.X, f); + } + + // verify if the X-L form was properly created by using validators with just CA + // certificates and current CRLs for TSA + @Test + public void test02_05_X_ver2() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // verify using minimal data (just CA certificates) + XAdESForm f = verifySignature("document.aged.test02_X_sig3.xml", + new XadesVerificationProfile(test02_userCertMinimalValidationDataProvider, + test02_tsaCertMinimalValidationDataProvider)); + + assertEquals(XAdESForm.X_L, f); + } + + /* t + * -24 .User CA validity + * -23 | .TSA CA validity + * -22 | | .User cert validity + * -21 | | | .TSA1 cert validity + * | | | | + * -20 | | | | <---- XAdES-T time stamp (TSA1) + * | | | | + * -19 | | | | .TSA CA 1st CRL + * -18 | | | | | .User CA 1st CRL (user cert revocation) + * | | | | | | + * -17 ' | ' | | ' + * -15 | | | .TSA2 cert validity + * | | | | + * -14 | | | | <---- XAdES-X time stamp (TSA2) + * | | | | + * -13 | | ' | .TSA CA 2nd CRL (TSA1 cert revocation) + * -12 | ' | | .Arch-TSA CA validity + * -11 | | | | .Arch-TSA3 validity + * | | | | | + * -10 | | | | | <---- first XAdES-A time stamp (Arch-TSA1) + * -9 ' ' ' | | + * -1 | | .Arch-TSA CA 1st CRL + * | | | + * 0 | | | <--- (now) validaiton + * | | | + * 1 ' ' ' + */ + + // create basic XAdES-T signed document + @Test + public void test03_01_T_sig1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_T_sig1.xml"); + removeFile(outFileName); + + SurrogateTimeStampTokenProvider.setTSACert(test03_T_tsa1Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 20), + new BigInteger("1")); + System.out.println("SignatureTimeStamp creation date is " + + new Date(realNow.getTime() - ONE_HOUR_IN_MS * 20)); + + Document doc = getDocument("document.xml"); + Element elemToSign = doc.getDocumentElement(); + XadesSigningProfile signer = new XadesTSigningProfile(test03_signatureCreationKeyingDataprovider); + signer.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + new Enveloped(signer.newSigner()).sign(elemToSign); + + outputDocument(doc, outFileName); + } + + // extend T form to X form + @Test + public void test03_02_X_sig2() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_X_sig2.xml"); + removeFile(outFileName); + + Date now = new Date(realNow.getTime() - ONE_HOUR_IN_MS * 14); + SurrogateTimeStampTokenProvider.setTSACert(test03_X_tsa2Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + now, + new BigInteger("2")); + + System.out.println("SigAndRefsTimeStamp creation date is " + + now); + + Document doc = getOutputDocument("document.aged.test03_T_sig1.xml"); + Element signatureNode = getSigElement(doc); + + /* + * extension of signature to X form must be performed in two steps, first we have + * to create the XML with needed Properties (CompleteRevocationRefs and + * CompleteCertificateRefs, that is, C form) and only after that we can add the + * X form time stamp + */ + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderXCreation); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + // extend T to C + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.C); + + assertEquals(res.getSignatureForm(), XAdESForm.T); + + // extend C to X + res = verifier.verify(signatureNode, options, formExt, XAdESForm.X); + + assertEquals(res.getSignatureForm(), XAdESForm.C); + + outputDocument(doc, outFileName); + } + + // extend X to X-L form + @Test + public void test03_03_X_sig3() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_X_sig3.xml"); + removeFile(outFileName); + + Document doc = getOutputDocument("document.aged.test03_X_sig2.xml"); + Element signatureNode = getSigElement(doc); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderACreation); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + Date now = new Date(realNow.getTime() - 10 * ONE_HOUR_IN_MS); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + // extend X to X-L + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.X_L); + + assertEquals(res.getSignatureForm(), XAdESForm.X); + + outputDocument(doc, outFileName); + } + + // extend X-L form to A form + @Test + public void test03_04_A_sig4() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_A_sig4.xml"); + removeFile(outFileName); + + Date now = new Date(realNow.getTime() - ONE_HOUR_IN_MS * 10); + SurrogateTimeStampTokenProvider.setTSACert(test03_A_tsa3Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + now, + new BigInteger("2")); + + System.out.println("ArchiveTimeStamp creation date is " + + now); + + Document doc = getOutputDocument("document.aged.test03_X_sig3.xml"); + Element signatureNode = getSigElement(doc); + + /* + * extension of signature to X form must be performed in two steps, first we have + * to create the XML with needed Properties (CompleteRevocationRefs and + * CompleteCertificateRefs, that is, C form) and only after that we can add the + * X form time stamp + */ + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderAnow); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(new Date(realNow.getTime() - ONE_HOUR_IN_MS * 14)); + + // extend X-L to A + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.X_L); + + outputDocument(doc, outFileName); + } + + // verify A form + @Test + public void test03_05_A_ver1() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // test verification + XAdESForm f = verifySignature("document.aged.test03_A_sig4.xml", + new XadesVerificationProfile(test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderAnow)); + + assertEquals(XAdESForm.A, f); + } + + // add validation info to A form + @Test + public void test03_06_A_sig5() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_A_sig5.xml"); + removeFile(outFileName); + + Document doc = getOutputDocument("document.aged.test03_A_sig4.xml"); + Element signatureNode = getSigElement(doc); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderAnow); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + // extend A to A-VD + XAdESVerificationResult res = verifier.verify(signatureNode, null, formExt, + XAdESForm.A_VD); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, outFileName); + } + + // verify A form using minimal validators + @Test + public void test03_07_A_ver2() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + + // verify using minimal data (just CA certificates) + XAdESForm f = verifySignature("document.aged.test03_A_sig5.xml", + new XadesVerificationProfile(test03_userCertMinimalValidationDataProvider, + test03_tsaCertMinimalValidationDataProvider)); + + assertEquals(XAdESForm.A, f); + } + + // time stamp A(VD) form again + @Test + public void test03_08_A_sig6() throws Exception + { + System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); + String outFileName = new String("document.aged.test03_A_sig6.xml"); + removeFile(outFileName); + + SurrogateTimeStampTokenProvider.setTSACert(test03_A_tsa3Cert); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + realNow, + new BigInteger("3")); + + System.out.println("ArchiveTimeStamp creation date is " + + realNow); + + Document doc = getOutputDocument("document.aged.test03_A_sig5.xml"); + Element signatureNode = getSigElement(doc); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test03_userCertValidationDataProviderXCreation, + test03_tsaCertValidationDataProviderAnow); + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + // extend A to A (add ArchiveTimeStamp) + XAdESVerificationResult res = verifier.verify(signatureNode, null, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, outFileName); + } + + /* Tests with multiple Time Stamping Authorities and realistic time periods + * + * quick overview: + * t + * -17y -- Signature creation + * -17y ̣ -- XAdES-T form creation (2 time stamps) (consterTSA and ascendeusTSA) + * -17y+16d-- XAdES-C form creation + * -13y -- XAdES-X form creation (2 time stamps) (ascendeusTSA and carpamaTSA) + * -13y+2w -- XAdES-X-L form creation + * -9y -- XAdES-A form creation (2 time stamps, 1st set) (carpamaTSA and premoxTSA) + * -9y+2w -- XAdES-A with validation data + * -5y -- extending XAdES-A with new set of timestamps (2 timestamps, 2nd set) (premoxTSA and gescapeTSA) + * -5y+2w -- adding validation data + * -1y -- extending XAdES-A with new set of timestamps (2 timestamps, 3rd set) (unibimTSA and astronTSA) + * -1y+2w -- adding validation data + * now -- validation with currently available revocation data + * + * Certificate authorities hierarchy: + * + * ACME Certification Services CA + * | + * + Will E. Coyote + * + * Conster CA + * | + * + Conster Time Server (5 years) + * + * Ascendeus Root CA (10 years) + * | + * + Ascendeus Issuing CA (8 years) + * | + * + Ascendeus Time Services (5 years) + * | + * + Ascendeus Time Services (2nd) (5 years) + * + * Carpama Certificate Authority (10 years) + * | + * + Carpama Time Server (5 years) + * | + * + Carpama Time Server (2nd) (5 years) + * + * Premox CA (10 years) + * | + * + Premox TSA (5 years) + * | + * + Premox TSA (2nd) (5 years) + * + * Gescape CA (10 years) + * | + * + Gescape TSA (5 years) + * + * Unibim CA (10 years) + * | + * + Unibim TSA (5 years) + * + * Astron CA (10 years) + * | + * + Astron TSA (5 years) + */ + + // create XAdES-T form + @Test + public void test04_01_T_sig1() throws Exception + { + System.out.println("test04_T_sig1"); + + Date now = new Date(realNow.getTime() - 17 * ONE_YEAR_IN_MS); + + KeyingDataProvider keyingDataProvider = new DirectKeyingDataProvider( + test04_willECoyote.getCertificate(), + test04_willECoyote.getPrivateKey()); + + SurrogateTimeStampTokenProvider.setTSACert(test04_consterTSA17ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("3")); + + Document doc = getDocument("document.xml"); + Element elemToSign = doc.getDocumentElement(); + XadesSigningProfile signer = new XadesTSigningProfile(keyingDataProvider); + signer.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + new Enveloped(signer.newSigner()).sign(elemToSign); + + outputDocument(doc, "document.aged.test04.T.xml"); + } + + // add second XAdES-T time stamp + @Test + public void test04_02_T_sig2() throws Exception + { + System.out.println("test04_T_sig2"); + + Date now = new Date(realNow.getTime() - 17 * ONE_YEAR_IN_MS + + 2 * ONE_HOUR_IN_MS); + + SurrogateTimeStampTokenProvider.setTSACert(test04_ascendeusTSA17ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("34")); + + Document doc = getOutputDocument("document.aged.test04.T.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore trustAnchors = keyStoreForCerts(test04_acmeCA); + final CRLEntries emptyEntries = test04_acmeCA.new CRLEntries(); + X509CRL acmeCRL = test04_acmeCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 6 * ONE_DAY_IN_MS), + new Date(now.getTime() + ONE_DAY_IN_MS), + new BigInteger("2"), + emptyEntries); + X509CRL acmePcCRL = test04_acmePersonalCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("2"), + emptyEntries); + Collection crls = createCRLCollection(acmeCRL, acmePcCRL); + CertStore intermCertsAndCrls = certStoreForCertsAndCrls( + crls, + test04_acmeCA.getCertificate(), + test04_acmePersonalCA.getCertificate(), + test04_willECoyote.getCertificate()); + CertificateValidationProvider certValidationDataProvider = + new PKIXCertificateValidationProvider(trustAnchors, + true, + intermCertsAndCrls); + + KeyStore tsaTrustAnchors = keyStoreForCerts(test04_consterCA); + X509CRL consterCRL = test04_consterCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("2"), + emptyEntries); + X509CRL ascendusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("2"), + emptyEntries); + X509CRL ascendusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 18 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 6 * ONE_HOUR_IN_MS), + new BigInteger("2"), + emptyEntries); + Collection tsaCRLs = createCRLCollection(consterCRL, + ascendusCRL, + ascendusIssuingCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls( + tsaCRLs, + test04_consterCA.getCertificate(), + test04_consterTSA17ya.getCertificate(), + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA17ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + certValidationDataProvider, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + // add second T time stamp + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.T); + + assertEquals(res.getSignatureForm(), XAdESForm.T); + + outputDocument(doc, "document.aged.test04.T2.xml"); + } + + // add references to signature data + @Test + public void test04_03_C_sig3() throws Exception + { + System.out.println("test04_C_sig3"); + + final Date now = new Date(realNow.getTime() - 17 * ONE_YEAR_IN_MS + + 16 * ONE_DAY_IN_MS); + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.T2.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_ascendeusCA, + test04_consterCA); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 1 * ONE_DAY_IN_MS), + new Date(now.getTime() + 6 * ONE_DAY_IN_MS), + new BigInteger("3"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("3"), + emptyEntries); + X509CRL consterCRL = test04_consterCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 20 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 1 * ONE_HOUR_IN_MS), + new BigInteger("3"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + ascendeusCRL, + ascendeusIssuingCRL, + consterCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA17ya.getCertificate(), + test04_consterCA.getCertificate(), + test04_consterTSA17ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + // it shouldn't be used now, as it is not configured, it will throw an exception + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderCCreation, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.C); + + assertEquals(res.getSignatureForm(), XAdESForm.T); + + outputDocument(doc, "document.aged.test04_C.xml"); + } + + // add second type of time stamp to signature (SigAndRefsTimeStamp) + @Test + public void test04_04_X_sig4() throws Exception + { + System.out.println("test04_X_sig4"); + Date now = new Date(realNow.getTime() - 13 * ONE_YEAR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_ascendeusTSA13ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("35")); + + Document doc = getOutputDocument("document.aged.test04_C.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL consterCRL = test04_consterCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 7 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 17 * ONE_HOUR_IN_MS), + new BigInteger("4"), + emptyEntries ); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("4"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("4"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("4"), + emptyEntries); + Collection tsaCRLs =createCRLCollection( + consterCRL, + ascendeusCRL, + ascendeusIssuingCRL, + carpamaCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_consterCA.getCertificate(), + test04_consterTSA17ya.getCertificate(), + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA17ya.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderCCreation, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.X); + + assertEquals(res.getSignatureForm(), XAdESForm.C); + + outputDocument(doc, "document.aged.test04.X.xml"); + } + + // add second SigAndRefsTimeStamp + @Test + public void test04_05_X_sig5() throws Exception + { + System.out.println("test04_X_sig5"); + Date now = new Date(realNow.getTime() - 13 * ONE_YEAR_IN_MS + ONE_HOUR_IN_MS / 2); + SurrogateTimeStampTokenProvider.setTSACert(test04_carpamaTSA13ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("36")); + + Document doc = getOutputDocument("document.aged.test04.X.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL consterCRL = test04_consterCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 7 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 17 * ONE_HOUR_IN_MS), + new BigInteger("5"), + emptyEntries ); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("5"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("5"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("5"), + emptyEntries); + Collection tsaCRLs =createCRLCollection( + consterCRL, + ascendeusCRL, + ascendeusIssuingCRL, + carpamaCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_consterCA.getCertificate(), + test04_consterTSA17ya.getCertificate(), + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA17ya.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderCCreation, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.X); + + assertEquals(res.getSignatureForm(), XAdESForm.X); + + outputDocument(doc, "document.aged.test04.X2.xml"); + } + + // add certificates to signature (X-L form) + @Test + public void test04_06_XL_sig6() throws Exception + { + System.out.println("test04_XL_sig6"); + + final Date now = new Date(realNow.getTime() - 13 * ONE_YEAR_IN_MS + + 14 * ONE_DAY_IN_MS); + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.X2.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_ascendeusCA, + test04_consterCA, + test04_carpamaCA); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 1 * ONE_DAY_IN_MS), + new Date(now.getTime() + 6 * ONE_DAY_IN_MS), + new BigInteger("6"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("6"), + emptyEntries); + X509CRL consterCRL = test04_consterCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 20 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 1 * ONE_HOUR_IN_MS), + new BigInteger("6"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 20 * ONE_HOUR_IN_MS), + new BigInteger("6"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + ascendeusCRL, + ascendeusIssuingCRL, + consterCRL, + carpamaCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA17ya.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_consterCA.getCertificate(), + test04_consterTSA17ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + // it shouldn't be used now, as it is not configured, it will throw an exception + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderCCreation, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.X_L); + + assertEquals(res.getSignatureForm(), XAdESForm.X); + + outputDocument(doc, "document.aged.test04.XL.xml"); + } + + // add first Archival Time Stamp + @Test + public void test04_07_A_sig7() throws Exception + { + System.out.println("test04_A_sig7"); + Date now = new Date(realNow.getTime() - 9 * ONE_YEAR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_carpamaTSA9ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("37")); + + Document doc = getOutputDocument("document.aged.test04.XL.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("7"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("7"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("7"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("7"), + emptyEntries); + Collection tsaCRLs =createCRLCollection( + ascendeusCRL, + ascendeusIssuingCRL, + carpamaCRL, + premoxCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.X_L); + + outputDocument(doc, "document.aged.test04.A.xml"); + } + + // add second Archival time stamp + @Test + public void test04_08_A_sig8() throws Exception + { + System.out.println("test04_A_sig8"); + Date now = new Date(realNow.getTime() - 9 * ONE_YEAR_IN_MS + ONE_HOUR_IN_MS / 60); + SurrogateTimeStampTokenProvider.setTSACert(test04_premoxTSA9ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial( + now, + new BigInteger("38")); + + Document doc = getOutputDocument("document.aged.test04.A.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("8"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("8"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("8"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("8"), + emptyEntries); + Collection tsaCRLs =createCRLCollection( + ascendeusCRL, + ascendeusIssuingCRL, + carpamaCRL, + premoxCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(XAdESForm.A, res.getSignatureForm()); + + outputDocument(doc, "document.aged.test04.A2.xml"); + } + + @Test + public void test04_09_AVD_sig9() throws Exception + { + System.out.println("test04_AVD_sig9"); + + final Date now = new Date(realNow.getTime() - 9 * ONE_YEAR_IN_MS + + 14 * ONE_DAY_IN_MS); + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.A2.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA); + X509CRL ascendeusCRL = test04_ascendeusCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 1 * ONE_DAY_IN_MS), + new Date(now.getTime() + 6 * ONE_DAY_IN_MS), + new BigInteger("9"), + emptyEntries); + X509CRL ascendeusIssuingCRL = test04_ascendeusIssuingCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("9"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 20 * ONE_HOUR_IN_MS), + new BigInteger("9"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("9"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + ascendeusCRL, + ascendeusIssuingCRL, + premoxCRL, + carpamaCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_ascendeusCA.getCertificate(), + test04_ascendeusIssuingCA.getCertificate(), + test04_ascendeusTSA13ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA13ya.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + // it shouldn't be used now, as it is not configured, it will throw an exception + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A_VD); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.AVD.xml"); + } + + // add second group of A time stamps + @Test + public void test04_10_2A_sig10() throws Exception + { + System.out.println("test04_2A_sig10"); + Date now = new Date(realNow.getTime() - 5 * ONE_YEAR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_premoxTSA5ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("38")); + + Document doc = getOutputDocument("document.aged.test04.AVD.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("10"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("10"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("10"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + carpamaCRL, + premoxCRL, + gescapeCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate(), + test04_premoxTSA5ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.2A.xml"); + } + + // add second time stamp in second group of timestamps + @Test + public void test04_11_2A_sig11() throws Exception + { + System.out.println("test04_2A_sig11"); + Date now = new Date(realNow.getTime() - 5 * ONE_YEAR_IN_MS + ONE_HOUR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_gescapeTSA5ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("39")); + + Document doc = getOutputDocument("document.aged.test04.2A.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("11"), + emptyEntries); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 4 * ONE_HOUR_IN_MS), + new BigInteger("11"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("11"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + carpamaCRL, + premoxCRL, + gescapeCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_carpamaCA.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate(), + test04_premoxTSA5ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.2A2.xml"); + } + + // add revocation information about first group of archive time stamps + @Test + public void test04_12_2AVD_sig12() throws Exception + { + System.out.println("test04_2AVD_sig12"); + + final Date now = new Date(realNow.getTime() - 5 * ONE_YEAR_IN_MS + + 14 * ONE_DAY_IN_MS); + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.2A2.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA); + X509CRL carpamaCRL = test04_carpamaCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 4 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 20 * ONE_HOUR_IN_MS), + new BigInteger("12"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("12"), + emptyEntries); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("12"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + premoxCRL, + carpamaCRL, + gescapeCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_carpamaCA.getCertificate(), + test04_carpamaTSA9ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA9ya.getCertificate(), + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_premoxTSA5ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + // it shouldn't be used now, as it is not configured, it will throw an exception + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A_VD); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.2AVD.xml"); + } + + // add first timestamp to third group of timestamps + @Test + public void test04_13_3A_sig13() throws Exception + { + System.out.println("test04_3A_sig13"); + Date now = new Date(realNow.getTime() - 1 * ONE_YEAR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_unibimTSA1ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("39")); + + Document doc = getOutputDocument("document.aged.test04.2AVD.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA, + test04_unibimCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("13"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("13"), + emptyEntries); + X509CRL unibimCRL = test04_unibimCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("13"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + premoxCRL, + gescapeCRL, + unibimCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA5ya.getCertificate(), + test04_unibimCA.getCertificate(), + test04_unibimTSA1ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.3A.xml"); + } + + // add second timestamp to third group of timestamps + @Test + public void test04_14_3A_sig14() throws Exception + { + System.out.println("test04_3A_sig14"); + Date now = new Date(realNow.getTime() - 1 * ONE_YEAR_IN_MS + ONE_HOUR_IN_MS); + SurrogateTimeStampTokenProvider.setTSACert(test04_astronTSA1ya); + SurrogateTimeStampTokenProvider.setTimeAndSerial(now, new BigInteger("40")); + + Document doc = getOutputDocument("document.aged.test04.3A.xml"); + Element signatureNode = getSigElement(doc); + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA, + test04_unibimCA, + test04_astronCA); + + final CRLEntries emptyEntries = test04_consterCA.new CRLEntries(); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA1withRSA", + new Date(now.getTime() - 2 * ONE_DAY_IN_MS), + new Date(now.getTime() + 5 * ONE_DAY_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL unibimCRL = test04_unibimCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL astronCRL = test04_astronCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + premoxCRL, + gescapeCRL, + unibimCRL, + astronCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_premoxCA.getCertificate(), + test04_premoxTSA5ya.getCertificate(), + test04_unibimCA.getCertificate(), + test04_unibimTSA1ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider( + tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.3A2.xml"); + } + + // add revocation information about second group of archive time stamps + @Test + public void test04_15_3AVD_sig15() throws Exception + { + System.out.println("test04_3AVD_sig14"); + + final Date now = new Date(realNow.getTime() - 1 * ONE_YEAR_IN_MS + + 14 * ONE_DAY_IN_MS); + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.3A2.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA, + test04_unibimCA, + test04_astronCA); + X509CRL premoxCRL = test04_premoxCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL unibimCRL = test04_unibimCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL astronCRL = test04_astronCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + premoxCRL, + gescapeCRL, + unibimCRL, + astronCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_premoxCA.getCertificate(), + test04_premoxTSA5ya.getCertificate(), + test04_gescapeCA.getCertificate(), + test04_gescapeTSA5ya.getCertificate(), + test04_unibimCA.getCertificate(), + test04_unibimTSA1ya.getCertificate(), + test04_astronCA.getCertificate(), + test04_astronTSA1ya.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesFormatExtenderProfile formExtProfile = new XadesFormatExtenderProfile(); + // it shouldn't be used now, as it is not configured, it will throw an exception + formExtProfile.withTimeStampTokenProvider(SurrogateTimeStampTokenProvider.class); + XadesSignatureFormatExtender formExt = formExtProfile.getFormatExtender(); + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + SignatureSpecificVerificationOptions options = new SignatureSpecificVerificationOptions().setDefaultVerificationDate(now); + + XAdESVerificationResult res = verifier.verify(signatureNode, options, formExt, + XAdESForm.A_VD); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + + outputDocument(doc, "document.aged.test04.3AVD.xml"); + } + + // test with minimal revocation information and all trust anchors + @Test + public void test04_16_3AVD_ver1() throws Exception + { + System.out.println("test04_3AVD_ver1"); + + final Date now = realNow; + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.3AVD.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_ascendeusCA, + test04_carpamaCA, + test04_premoxCA, + test04_gescapeCA, + test04_unibimCA, + test04_astronCA); + X509CRL unibimCRL = test04_unibimCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + X509CRL astronCRL = test04_astronCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + unibimCRL, + astronCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_unibimCA.getCertificate(), + test04_astronCA.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + XAdESVerificationResult res = verifier.verify(signatureNode, null); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + } + + // test with minimal revocation information and only some TSA trust anchors (minimal amount + // that will still allow for successful history traversal) + @Test + public void test04_17_3AVD_ver2() throws Exception + { + System.out.println("test04_3AVD_ver2"); + + final Date now = realNow; + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.3AVD.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_carpamaCA, + test04_gescapeCA, + test04_astronCA); + X509CRL astronCRL = test04_astronCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + astronCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_astronCA.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + XAdESVerificationResult res = verifier.verify(signatureNode, null); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + } + + // test with minimal revocation information and only some TSA trust anchors + // removal of trust for all newest TSAs (gescape TSA is valid but don't provide CRL + // for it) + @Test(expected = CannotBuildCertificationPathException.class) + public void test04_18_3AVD_ver3() throws Exception + { + System.out.println("test04_3AVD_ver3"); + + final Date now = realNow; + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.3AVD.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_carpamaCA, + test04_gescapeCA); + X509CRL astronCRL = test04_astronCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + astronCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_astronCA.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + XAdESVerificationResult res = verifier.verify(signatureNode, null); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + } + + // test with minimal revocation information and only some TSA trust anchors + // removal of trust for all newest TSAs, but provide CRL for gescape + @Test + public void test04_19_3AVD_ver4() throws Exception + { + System.out.println("test04_3AVD_ver3"); + + final Date now = realNow; + final CRLEntries emptyEntries = test04_ascendeusCA.new CRLEntries(); + + Document doc = getOutputDocument("document.aged.test04.3AVD.xml"); + Element signatureNode = getSigElement(doc); + + KeyStore tsaTrustAnchors = keyStoreForCerts( + test04_consterCA, + test04_carpamaCA, + test04_gescapeCA); + X509CRL gescapeCRL = test04_gescapeCA.createCRL( + "SHA256withRSA", + new Date(now.getTime() - 12 * ONE_HOUR_IN_MS), + new Date(now.getTime() + 12 * ONE_HOUR_IN_MS), + new BigInteger("14"), + emptyEntries); + Collection tsaCRLs = createCRLCollection( + gescapeCRL); + CertStore tsaIntermCertsAndCrls = certStoreForCertsAndCrls(tsaCRLs, + test04_gescapeCA.getCertificate()); + TSACertificateValidationProvider tsaValidationDataProvider = + new PKIXTSACertificateValidationProvider(tsaTrustAnchors, + true, + tsaIntermCertsAndCrls); + + XadesVerificationProfile verProfile = new XadesVerificationProfile( + test04_certValidationDataProviderOnlyTrustAnchors, + tsaValidationDataProvider); + + XadesHybridVerifierImpl verifier = (XadesHybridVerifierImpl) verProfile.newVerifier(); + + XAdESVerificationResult res = verifier.verify(signatureNode, null); + + assertEquals(res.getSignatureForm(), XAdESForm.A); + } + + /* + * TODO missing grace period support + * The library should support enforcing grace period, but what's more important, it + * should add only necessary CRLs when creating revocation information related + * properties. If that's not performed, then CRLs that don't help in later + * validation will be added causing the file size to grow needlessly. + + time + . + | <- Signature creation time + | + | <- SignatureTimeStamp creation time (XAdES-T) + | + + | | + | | grace period for Signature + | | + | + + | <- earliest time a CRL will make the Signature non-repudiable + | + | <- publishing of CRL for Signature certificate + | <- earliest time a usable XAdES-C can be created + / + / + | <- SigAndRefsTimeStamp creation time (XAdES-X) + | + + | | + | | grace period for SignatureTimeStamp + | | + | + + | <- earliest time a CRL will make SignatureTimeStamp non-repudiable + | + | <- publishing of CRL for SignatureTimeStamp certificate + | <- earliest time a usable XAdES-X-L can be created + / + / + | <- 1st ArchiveTimeStamp creation time (XAdES-A) + | + + | | + | | grace period for SigAndRefsTimeSamp + | | + | + + | <- earliest time a CRL will make SigAndRefsTimeStamp non-repudiable + | + | <- publishing of CRL for SigAndRefsTimeStamp + | <- earliest time TimeStampValidationData can be created (XAdES-A-VD) + / + / + | <- 2nd ArchiveTimeStamp creation time (XAdES-A) + | + + | | + | | grace period for 1st ArchiveTimeStamp + | | + | + + | <- earliest time a CRL will make 1st ArchiveTimeStamp non-repudiable + | + | <- publishing of CRL for 1st ArchiveTimeStamp + | <- earliest time TimeStampValidationData for 1st ArchiveTimeStamp can be created + ' + + */ + /* + * end of tests + */ + + // helper method + private XAdESForm verifySignature(String path, + XadesVerificationProfile p) + throws FileNotFoundException, ParserConfigurationException, + SAXException, IOException, XadesProfileResolutionException, XAdES4jException + { + Element signatureNode = getSigElement(getOutputDocument(path)); + XAdESVerificationResult res = p.newVerifier().verify(signatureNode, null); + return res.getSignatureForm(); + } + + // helper method + private Element getSigElement(Document document) + { + return (Element)document.getElementsByTagNameNS(Constants.SignatureSpecNS, + Constants._TAG_SIGNATURE).item(0); + } + + private void removeFile(String path) + { + File outDir = ensureOutputDir("xml/"); + File file = new File(outDir, path); + file.delete(); + } + + // helper method + private void outputDocument(Document doc, String fileName) throws Exception { + TransformerFactory tf = TransformerFactory.newInstance(); + File outDir = ensureOutputDir("xml"); + FileOutputStream out = new FileOutputStream(new File(outDir, fileName)); + tf.newTransformer().transform( + new DOMSource(doc), + new StreamResult(out)); + out.flush(); + out.getFD().sync(); + out.close(); + } + + private static File ensureOutputDir(String subdir) { + File dir = new File(toPlatformSpecificFilePath("./target/out/" + subdir)); + dir.mkdirs(); + return dir; + } + + /*private void outputDocument(Document doc, String path) + throws TransformerConfigurationException, + TransformerException, IOException + { + path = toDocumentDirFilePath(path); + TransformerFactory tf = TransformerFactory.newInstance(); + FileOutputStream out = new FileOutputStream(path); + tf.newTransformer().transform( + new DOMSource(doc), + new StreamResult(out)); + out.flush(); + out.getFD().sync(); + out.close(); + }*/ + + // helper method + private static void saveCRL(String fileName, X509CRL crl) + throws CRLException, IOException + { + File directory = ensureOutputDir("cert/aged"); + FileOutputStream fos = new FileOutputStream(new File(directory, fileName)); + fos.write(crl.getEncoded()); + fos.close(); + return; + } + + // helper method + /*private String toDocumentDirFilePath(String path) + { + return "./src/test/xml/" + path; + }*/ + + // helper method + private static void saveCertificate(String fileName, X509Certificate cert) + throws CertificateEncodingException, IOException + { + File outDir = ensureOutputDir("cert/aged"); + FileOutputStream fos = new FileOutputStream(new File(outDir, fileName)); + fos.write(cert.getEncoded()); + fos.close(); + return; + } + + // helper method + private Document getDocument(String fileName) throws ParserConfigurationException, + FileNotFoundException, SAXException, IOException + { + String path = toPlatformSpecificXMLDirFilePath(fileName); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new FileInputStream(path)); + Element elem = doc.getDocumentElement(); + DOMHelper.useIdAsXmlId(elem); + return doc; + } + + private Document getOutputDocument(String fileName) throws ParserConfigurationException, + FileNotFoundException, SAXException, IOException { + File outDir = ensureOutputDir("xml"); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new FileInputStream(new File(outDir, fileName))); + Element elem = doc.getDocumentElement(); + DOMHelper.useIdAsXmlId(elem); + return doc; + } + + private static Collection createCRLCollection(X509CRL... crls) + { + Collection list = new ArrayList(crls.length); + for (X509CRL c : crls) + { + list.add(c); + } + return list; + } + + private static CertStore certStoreForCertsAndCrls(Collection crls, + X509Certificate... certificates) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + { + Collection certs = new ArrayList(crls.size() + certificates.length); + for (X509Certificate cert : certificates) + { + certs.add(cert); + } + certs.addAll(crls); + CertStore certStore = CertStore.getInstance( + "Collection", + new CollectionCertStoreParameters(certs)); + return certStore; + } + + private static KeyStore keyStoreForCerts(FullCert... certs) throws KeyStoreException, + IOException, NoSuchAlgorithmException, CertificateException + { + KeyStore trustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + trustAnchors.load(null); + int i = 1; + for(FullCert cert: certs) + { + Entry entry = new TrustedCertificateEntry(cert.getCertificate()); + trustAnchors.setEntry("ca" + i, entry , null); + i++; + } + return trustAnchors; + } +} diff --git a/src/test/java/xades4j/verification/CertPathBuilderTest.java b/src/test/java/xades4j/verification/CertPathBuilderTest.java new file mode 100644 index 00000000..fcdf366f --- /dev/null +++ b/src/test/java/xades4j/verification/CertPathBuilderTest.java @@ -0,0 +1,241 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyStore; +import java.security.KeyStore.Entry; +import java.security.Security; +import java.security.KeyStore.TrustedCertificateEntry; +import java.security.cert.CRLException; +import java.security.cert.CertPath; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertStore; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.X509CRL; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.x509.ExtendedPKIXParameters; +import org.junit.Test; +import static xades4j.utils.SignatureServicesTestBase.toPlatformSpecificFilePath; + +import xades4j.verification.FullCert.CRLEntries; + +public class CertPathBuilderTest +{ + private static FullCert caCert; + private static FullCert userCert; + private static FullCert user2Cert; + private static KeyStore trustAnchors; + static + { + try { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + + caCert = FullCert.getCACert("RSA", 1024, "CN=XAdES4j Testing CA", + new Date(new Date().getTime() - 1000*60*60), + new Date(new Date().getTime() + 1000*60*60), + "SHA256withRSA"); /* cert will have serial number: 1 */ + saveCertificate("ca.cer", caCert.getCertificate()); + + userCert = caCert.createUserCert("RSA", 1024, "CN=User Certificate", + new Date(new Date().getTime() - 1000*60*60), + new Date(new Date().getTime() + 1000*60*30), + new BigInteger("2"), "SHA256withRSA"); + saveCertificate("user.cer", userCert.getCertificate()); + + user2Cert = caCert.createTSACert("RSA", 1024, "CN=User 2 Certificate", + new Date(new Date().getTime() - 1000*60*60), + new Date(new Date().getTime() - 1000*60*30), + new BigInteger("3"), "SHA256withRSA"); + saveCertificate("user2.cer", user2Cert.getCertificate()); + + trustAnchors = KeyStore.getInstance(KeyStore.getDefaultType()); + trustAnchors.load(null, null); + Entry caEntry = new TrustedCertificateEntry(caCert.getCertificate()); + trustAnchors.setEntry("ca", caEntry, null); + + } catch (Exception ex) + { + throw new RuntimeException("static initialization failed", ex); + } + } + + /* + * Generator creates certificates with validity periods in the past: + * + * ^ + * | <-- in 1h: + * | caCert validity end + * | + * | <-- in 30 min: + * | userCert validity end + * | + * | <-- in 20 min: + * | CRL validity end (test1, test2) + * | + * | <-- *now* + * | + * | <- 1m ago: + * | CRL creation (test1, test2) + * | + * | <- 30 min ago: + * | user2Cert validity end + * | + * | <-- 45 min ago: + * | user2Cert validation + * | + * | <-- 1h ago: + * | caCert creation + * | userCert creation + * | user2Cert creation + */ + + /* + * revoke certificate 1min ago, try to validate it before that (15min ago) + * XXX fails with BC provider + */ + @Test + public void test1() throws Exception + { + System.out.println("test1"); + + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"/*, "BC"*/); + + X509CertSelector userCertSelector = new X509CertSelector(); + userCertSelector.setCertificate(userCert.getCertificate()); + + PKIXBuilderParameters buildParams = new PKIXBuilderParameters(trustAnchors, userCertSelector); + + // create CRL with user cert revoked after the time we will verify it at + CRLEntries entries = caCert.new CRLEntries(); + entries.addEntry(userCert.getCertificate().getSerialNumber(), + new Date(new Date().getTime() - 1000*60), CRLReason.unspecified); + X509CRL crl = caCert.createCRL("SHA256withRSA", + new Date(new Date().getTime() - 1000*60), + new Date(new Date().getTime() + 1000*60*20), + new BigInteger("3"), + entries); + saveCRL("test1.crl", crl); + + Collection content = new ArrayList(); + content.add(crl); + content.add(userCert.getCertificate()); + //content.add(tsaCert.getCertificate()); + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + + buildParams.addCertStore(intermCertsAndCrls); + buildParams.setRevocationEnabled(true); + buildParams.setDate(new Date(new Date().getTime() - 1000*60*15)); + + //ExtendedPKIXParameters extBuildParams = ExtendedPKIXParameters.getInstance(buildParams); + //extBuildParams.setValidityModel(ExtendedPKIXParameters.PKIX_VALIDITY_MODEL); + PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(buildParams); + CertPath certPath = result.getCertPath(); + + System.out.println(certPath.getCertificates().get(0).getType()); + } + + /* + * Try to validate certificate using CRL published after certificate validity end + */ + @Test(expected = CertPathBuilderException.class) + public void test2() throws Exception + { + System.out.println("test2"); + // test verification + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); + + X509CertSelector userCertSelector = new X509CertSelector(); + userCertSelector.setCertificate(user2Cert.getCertificate()); + + PKIXBuilderParameters buildParams = new PKIXBuilderParameters(trustAnchors, userCertSelector); + + // create empty CRL + CRLEntries entries = caCert.new CRLEntries(); + X509CRL crl = caCert.createCRL("SHA256withRSA", + new Date(new Date().getTime() - 1000*60), + new Date(new Date().getTime() + 1000*60*20), + new BigInteger("3"), + entries); + saveCRL("test2.crl", crl); + + Collection content = new ArrayList(); + content.add(crl); + content.add(user2Cert.getCertificate()); + //content.add(tsaCert.getCertificate()); + CertStore intermCertsAndCrls = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(content)); + + buildParams.addCertStore(intermCertsAndCrls); + buildParams.setRevocationEnabled(true); + buildParams.setDate(new Date(new Date().getTime() - 1000*60*45)); // 45 minutes ago + + ExtendedPKIXParameters extBuildParams = ExtendedPKIXParameters.getInstance(buildParams); + extBuildParams.setValidityModel(ExtendedPKIXParameters.PKIX_VALIDITY_MODEL); + + PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(buildParams); + CertPath certPath = result.getCertPath(); + + System.out.println(certPath.getCertificates().get(0).getType()); + + } + + /* + * end of tests + */ + private static File ensureOutputDir() { + File dir = new File(toPlatformSpecificFilePath("./target/out/cert/certpath")); + dir.mkdirs(); + return dir; + } + + // helper method + private static void saveCRL(String fileName, X509CRL crl) + throws CRLException, IOException + { + File outDir = ensureOutputDir(); + FileOutputStream fos = new FileOutputStream(new File(outDir, fileName)); + fos.write(crl.getEncoded()); + fos.close(); + return; + } + + // helper method + private static void saveCertificate(String fileName, X509Certificate cert) + throws CertificateEncodingException, IOException + { + File outDir = ensureOutputDir(); + FileOutputStream fos = new FileOutputStream(new File(outDir, fileName)); + fos.write(cert.getEncoded()); + fos.close(); + return; + } +} diff --git a/src/test/java/xades4j/verification/FullCert.java b/src/test/java/xades4j/verification/FullCert.java new file mode 100644 index 00000000..289f862e --- /dev/null +++ b/src/test/java/xades4j/verification/FullCert.java @@ -0,0 +1,513 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.math.BigInteger; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.naming.directory.InvalidAttributesException; +import javax.security.auth.x500.X500Principal; + +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.RSAPublicKey; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CRLConverter; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +/** + * Class wrapping Certificate and PrivateKey in a easy-to-use package, oriented to testing + * + * @author Hubert Kario + * + */ +public class FullCert +{ + private X509Certificate certificate; + private PrivateKey privateKey; + + public X509Certificate getCertificate() + { + return certificate; + } + + public PrivateKey getPrivateKey() + { + return privateKey; + } + + /** + * @param algorithm key generation algorithm: RSA, DSA, ECDSA (currently only RSA is + * supported) + * @param keySize size of generated keys, for example 2048 for RSA and 521 for ECDSA + * @param distinguishedName DN of the new certificate + * @param notBefore time from which certificate is valid + * @param notAfter time after which certificate is not valid + * @param serialNumber this should be an unique serial number of the certificate + * @param sigAlgorithm name of signature algorithm (SHA1withDSA, SHA256withRSA, etc.) + * @param isCA should the new certificate be for a Certificate Authority + * @param keyUsage valid key uses (bitwise OR on values from {@link KeyUsage} + * @param extendedAttrCritical are extended key usage attributes critical + * @param extendedAttr list of extended attributes, if null then no extended key + * attributes will be added to certificate + * @param signer certificate that has to sign new certificate, can be null, + * then it will create a self-signed certificate + * @return ready to use FullCert (certificate + private key) + * @throws Exception on any error + */ + private static FullCert signCert( + String algorithm, + int keySize, + String distinguishedName, + Date notBefore, + Date notAfter, + BigInteger serialNumber, + String sigAlgorithm, + boolean isCA, + int keyUsage, + boolean extendedAttrCritical, + KeyPurposeId[] extendedAttr, + FullCert signer) throws Exception + { + if (!algorithm.equalsIgnoreCase("RSA")) + throw new InvalidAttributesException("Only RSA keys supported for the moment"); + + // prepare parameters + X500Principal subjectName = new X500Principal(distinguishedName); + // generate key pair + KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm, "BC"); + kpg.initialize(keySize); + KeyPair keyPair = kpg.generateKeyPair(); + // extract public key as RSA public key + java.security.interfaces.RSAPublicKey rsaPublicKey = + (java.security.interfaces.RSAPublicKey) keyPair.getPublic(); + java.security.interfaces.RSAPublicKey caRSAPublicKey; + if (signer != null) + { + caRSAPublicKey = + (java.security.interfaces.RSAPublicKey) signer.certificate.getPublicKey(); + } else { + caRSAPublicKey = + (java.security.interfaces.RSAPublicKey) keyPair.getPublic(); + } + + // set basic information + X509v3CertificateBuilder certBuilder; + if (signer != null) + { + certBuilder = new JcaX509v3CertificateBuilder( + signer.certificate.getSubjectX500Principal(), + serialNumber, + notBefore, + notAfter, + subjectName, + keyPair.getPublic()); + } else { + certBuilder = new JcaX509v3CertificateBuilder( + subjectName, + serialNumber, + notBefore, + notAfter, + subjectName, + keyPair.getPublic()); + } + // used for creating extensions in certificate, depends on algorithm (RSA, DSA, + // ECDSA) + SubjectPublicKeyInfo subjectKeyInfo = + new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, + DERNull.INSTANCE), + new RSAPublicKey(rsaPublicKey.getModulus(), + rsaPublicKey.getPublicExponent())); + SubjectPublicKeyInfo issuerKeyInfo = + new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, + DERNull.INSTANCE), + new RSAPublicKey(caRSAPublicKey.getModulus(), + caRSAPublicKey.getPublicExponent())); + + /* + * Additional constraints are defaults from EJBCA + */ + JcaX509ExtensionUtils utils = new JcaX509ExtensionUtils(); + + certBuilder.addExtension(Extension.subjectKeyIdentifier, + false, /* extension should always be non critical */ + utils.createSubjectKeyIdentifier(subjectKeyInfo)); + + certBuilder.addExtension(Extension.basicConstraints, + true, /* extension is critical */ + new BasicConstraints(isCA)); + + certBuilder.addExtension(Extension.authorityKeyIdentifier, + false, /* extension should always be non critical */ + utils.createAuthorityKeyIdentifier(issuerKeyInfo)); + + certBuilder.addExtension(Extension.keyUsage, + true, /* is critical */ + new KeyUsage(keyUsage)); + + if (extendedAttr != null) + { + certBuilder.addExtension(Extension.extendedKeyUsage, + extendedAttrCritical, + new DERSequence(extendedAttr)); + //new ExtendedKeyUsage(new DERSequence(extendedAttr))); + } + + // signature generator + ContentSigner sigGen; + if (signer != null) + { + sigGen = new JcaContentSignerBuilder(sigAlgorithm) + .setProvider("BC").build(signer.privateKey); + } else { + sigGen = new JcaContentSignerBuilder(sigAlgorithm) + .setProvider("BC").build(keyPair.getPrivate()); + } + + // export private key and signed certificate + FullCert cert = new FullCert(); + cert.privateKey = keyPair.getPrivate(); + cert.certificate = new JcaX509CertificateConverter() + .setProvider("BC").getCertificate(certBuilder.build(sigGen)); + + return cert; + } + + /** + * Generates Certificate with extensions useful for CA with serial number set at 1 + * + * @param algorithm key generation algorithm: RSA, DSA, ECDSA, etc. + * (currently only RSA is supported) + * @param keySize size of generated keys, for example 2048 for RSA and 521 for ECDSA + * @param distinguishedName DN of the CA + * @param notBefore time from which certificate is valid + * @param notAfter time after which certificate is not valid + * @param sigAlgorithm name of signature algorithm (SHAwithDSA, SHA256withRSA, etc.) + * @return Certificate together with private key + * @throws Exception on any error + */ + public static FullCert getCACert( + String algorithm, + int keySize, + String distinguishedName, + Date notBefore, + Date notAfter, + String sigAlgorithm) throws Exception + { + BigInteger serialNumber = new BigInteger("1"); + boolean isCA = true; + int keyUsage = KeyUsage.digitalSignature | KeyUsage.keyCertSign + | KeyUsage.cRLSign; + boolean extendedAttrCritical = false; + KeyPurposeId extendedAttr[] = null; + FullCert signer = null; + + return signCert(algorithm, + keySize, + distinguishedName, + notBefore, + notAfter, + serialNumber , + sigAlgorithm, + isCA, + keyUsage, + extendedAttrCritical, + extendedAttr, + signer); + } + + /** + * Uses current certificate as CA cert to create new user certificate with sane + * default key usages and extended key usages. Does not set distribution points for + * CRLs or OCSP + *

+ * Does not perform any sanity checks + * @param algorithm key generation algorithm: RSA, DSA, ECDSA + * (currently only RSA is supported) + * @param keySize size of generated keys, for example 2048 for RSA and 521 for ECDSA + * @param distinguishedName DN of the user + * @param notBefore time from which certificate is valid + * @param notAfter time after which certificate is not valid + * @param serialNumber this should be an unique serial number of the certificate + * @param sigAlgorithm name of signature algorithm (SHAwithDSA, SHA256withRSA, etc.) + * @return Certificate together with private key + * @throws Exception on any error + */ + public FullCert createUserCert( + String algorithm, + int keySize, + String distinguishedName, + Date notBefore, + Date notAfter, + BigInteger serialNumber, + String sigAlgorithm) throws Exception + { + boolean isCA = false; + int keyUsage = KeyUsage.digitalSignature | KeyUsage.keyEncipherment + | KeyUsage.dataEncipherment; + boolean extendedAttrCritical = false; + KeyPurposeId[] extendedAttr = new KeyPurposeId[3]; + extendedAttr[0] = KeyPurposeId.id_kp_clientAuth; + extendedAttr[1] = KeyPurposeId.id_kp_codeSigning; + extendedAttr[2] = KeyPurposeId.id_kp_emailProtection; + + return signCert(algorithm, + keySize, + distinguishedName, + notBefore, + notAfter, + serialNumber, + sigAlgorithm, + isCA, + keyUsage, + extendedAttrCritical, + extendedAttr, + this); + } + + /** + * Uses current certificate as CA cert to create new Time Stamping Authority (TSA) + * certificate with default key usages and extended key usages typical for a TSA. + * Does not set distribution points for CRLs or OCSP. + *

+ * Does not perform any sanity checks. Sets Extended Key Usage to critical. + * @param algorithm key generation algorithm: RSA, DSA, ECDSA + * (currently only RSA is supported) + * @param keySize size of generated keys, for example 2048 for RSA and 521 for ECDSA + * @param distinguishedName DN of the user + * @param notBefore time from which certificate is valid + * @param notAfter time after which certificate is not valid + * @param serialNumber this should be an unique serial number of the certificate + * @param sigAlgorithm name of signature algorithm (SHAwithDSA, SHA256withRSA, etc.) + * @return Certificate together with private key + * @throws Exception on any error + */ + public FullCert createTSACert( + String algorithm, + int keySize, + String distinguishedName, + Date notBefore, + Date notAfter, + BigInteger serialNumber, + String sigAlgorithm) throws Exception + { + boolean isCA = false; + int keyUsage = KeyUsage.digitalSignature | KeyUsage.nonRepudiation + | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment; + boolean extendedAttrCritical = true; + KeyPurposeId[] extendedAttr = new KeyPurposeId[1]; + extendedAttr[0] = KeyPurposeId.id_kp_timeStamping; + + return signCert(algorithm, + keySize, + distinguishedName, + notBefore, + notAfter, + serialNumber, + sigAlgorithm, + isCA, + keyUsage, + extendedAttrCritical, + extendedAttr, + this); + } + + /** + * Uses current certificate as CA cert to create new sub CA certificate with sane + * default key usages. Does not set distribution points for + * CRLs or OCSP + *

+ * Does not perform any sanity checks + * @param algorithm key generation algorithm: RSA, DSA, ECDSA + * (currently only RSA is supported) + * @param keySize size of generated keys, for example 2048 for RSA and 521 for ECDSA + * @param distinguishedName DN of the user + * @param notBefore time from which certificate is valid + * @param notAfter time after which certificate is not valid + * @param serialNumber this should be an unique serial number of the certificate + * @param sigAlgorithm name of signature algorithm (SHAwithDSA, SHA256withRSA, etc.) + * @return Certificate together with private key + * @throws Exception on any error + */ + public FullCert createSubCACert( + String algorithm, + int keySize, + String distinguishedName, + Date notBefore, + Date notAfter, + BigInteger serialNumber, + String sigAlgorithm) throws Exception + { + + boolean isCA = true; + int keyUsage = KeyUsage.digitalSignature | KeyUsage.keyEncipherment + | KeyUsage.dataEncipherment | KeyUsage.keyCertSign | KeyUsage.cRLSign; + boolean extendedAttrCritical = false; + KeyPurposeId[] extendedAttr = null; + + return signCert(algorithm, + keySize, + distinguishedName, + notBefore, + notAfter, + serialNumber, + sigAlgorithm, + isCA, + keyUsage, + extendedAttrCritical, + extendedAttr , + this); + } + + /** + * Class containing entries for conversion to CRL + * + * @author Hubert Kario + * + */ + public class CRLEntries + { + private List serialNumber; + private List revocationDate; + private List revocationReason; + + public CRLEntries() + { + serialNumber = new ArrayList(); + revocationDate = new ArrayList(); + revocationReason = new ArrayList(); + } + + public CRLEntries(CRLEntries old) + { + this.serialNumber = new ArrayList(old.serialNumber); + this.revocationDate = new ArrayList(old.revocationDate); + this.revocationReason = new ArrayList(old.revocationReason); + } + + public void addEntry(BigInteger serialNumber, Date revocationDate, + int revocationReason) + { + if (serialNumber == null || revocationDate == null) + throw new InvalidParameterException("Parameters can't be null"); + if (revocationReason < 0) + throw new InvalidParameterException("Revocation reason must be positive"); + + this.serialNumber.add(serialNumber); + this.revocationDate.add(revocationDate); + this.revocationReason.add(revocationReason); + } + + public int size() + { + return serialNumber.size(); + } + + public BigInteger getSerialNumber(int index) + { + return serialNumber.get(index); + } + + public Date getRevocaDate(int index) + { + return revocationDate.get(index); + } + + public int getRevocationReason(int index) + { + return revocationReason.get(index); + } + } + + /** + * Create new certificate revocation list using this certificate as CA + * @param sigAlgorithm CRL signature algorithm (for example SHA256withRSA) + * @param thisUpdate date when this CRL has been created + * @param nextUpdate when new CRL should have been created + * @param serial Serial number of the CRL + * @param entries list of revoked certificates + * @return + */ + public X509CRL createCRL(String sigAlgorithm, + Date thisUpdate, Date nextUpdate, BigInteger serial, CRLEntries entries) + throws Exception + { + java.security.interfaces.RSAPublicKey caRSAPublicKey = + (java.security.interfaces.RSAPublicKey) certificate.getPublicKey(); + + SubjectPublicKeyInfo issuerKeyInfo = + new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, + DERNull.INSTANCE), + new RSAPublicKey(caRSAPublicKey.getModulus(), + caRSAPublicKey.getPublicExponent())); + + X500Name issuer = new X500Name(this.certificate.getSubjectDN().toString()); + X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuer, thisUpdate); + + crlBuilder.setNextUpdate(nextUpdate); + + for (int i = 0; i < entries.size(); i++) + { + crlBuilder.addCRLEntry( + entries.getSerialNumber(i), + entries.getRevocaDate(i), + entries.getRevocationReason(i)); + } + + crlBuilder.addExtension(Extension.authorityKeyIdentifier, + false, /* not critical */ + new AuthorityKeyIdentifier(issuerKeyInfo)); + + crlBuilder.addExtension(Extension.cRLNumber, + false, /* not critical */ + new CRLNumber(serial)); + + // signature generator (use CA key) + ContentSigner sigGen = new JcaContentSignerBuilder(sigAlgorithm) + .setProvider("BC").build(this.privateKey); + + X509CRLHolder crlHolder = crlBuilder.build(sigGen); + return new JcaX509CRLConverter().setProvider("BC").getCRL(crlHolder); + } +} diff --git a/src/test/java/xades4j/verification/GenericDOMDataVerifierTest.java b/src/test/java/xades4j/verification/GenericDOMDataVerifierTest.java index f7da86ea..0c643b26 100644 --- a/src/test/java/xades4j/verification/GenericDOMDataVerifierTest.java +++ b/src/test/java/xades4j/verification/GenericDOMDataVerifierTest.java @@ -26,6 +26,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.w3c.dom.Document; +import org.w3c.dom.Element; + import static org.junit.Assert.*; import xades4j.properties.QualifyingProperty; import xades4j.properties.data.GenericDOMData; @@ -119,4 +121,13 @@ public String getName() } }; } + + @Override + public QualifyingProperty verify(GenericDOMData propData, + Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + return verify(propData, ctx); + } } diff --git a/src/test/java/xades4j/verification/OtherVerifierTests.java b/src/test/java/xades4j/verification/OtherVerifierTests.java index 2e755462..dd4872d0 100644 --- a/src/test/java/xades4j/verification/OtherVerifierTests.java +++ b/src/test/java/xades4j/verification/OtherVerifierTests.java @@ -22,6 +22,7 @@ import xades4j.properties.QualifyingProperty; import xades4j.properties.data.SigningTimeData; import org.junit.Test; +import org.w3c.dom.Element; class SigningTimeVerifierThatDependsOnBuiltInVerifier implements QualifyingPropertyVerifier { @@ -40,6 +41,15 @@ public QualifyingProperty verify(SigningTimeData propData, QualifyingPropertyVer builtInVerifier.verify(propData, ctx); throw new SigningTimeVerificationException(null, null); } + + @Override + public QualifyingProperty verify(SigningTimeData propData, Element elem, + QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + builtInVerifier.verify(propData, ctx); + throw new SigningTimeVerificationException(null, null); + } } /** @@ -53,7 +63,9 @@ public class OtherVerifierTests extends VerifierTestBase @Before public void initialize() { - mySigsVerificationProfile = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs); + mySigsVerificationProfile = + new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs); } @Test(expected = UnsupportedOperationException.class) @@ -70,6 +82,15 @@ public QualifyingProperty verify( { throw new UnsupportedOperationException("Yeah!"); } + + @Override + public QualifyingProperty verify(SigningTimeData propData, + Element elem, QualifyingPropertyVerificationContext ctx) + throws InvalidPropertyException + { + throw new UnsupportedOperationException("Yeah!"); + + } }); verifySignature("document.signed.bes.xml", mySigsVerificationProfile); } diff --git a/src/test/java/xades4j/verification/SurrogateTimeStampTokenProvider.java b/src/test/java/xades4j/verification/SurrogateTimeStampTokenProvider.java new file mode 100644 index 00000000..3ac056a3 --- /dev/null +++ b/src/test/java/xades4j/verification/SurrogateTimeStampTokenProvider.java @@ -0,0 +1,221 @@ +/* + * XAdES4j - A Java library for generation and verification of XAdES signatures. + * Copyright (C) 2012 Hubert Kario - QBS. + * + * 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 . + */ +package xades4j.verification; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.xml.security.algorithms.MessageDigestAlgorithm; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.cms.Time; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator; +import org.bouncycastle.cms.SignerInfoGenerator; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.tsp.TSPAlgorithms; +import org.bouncycastle.tsp.TimeStampRequest; +import org.bouncycastle.tsp.TimeStampRequestGenerator; +import org.bouncycastle.tsp.TimeStampResponse; +import org.bouncycastle.tsp.TimeStampResponseGenerator; +import org.bouncycastle.tsp.TimeStampToken; +import org.bouncycastle.tsp.TimeStampTokenGenerator; + +import com.google.inject.Inject; + +import xades4j.UnsupportedAlgorithmException; +import xades4j.providers.MessageDigestEngineProvider; +import xades4j.providers.TimeStampTokenGenerationException; +import xades4j.providers.TimeStampTokenProvider; + +/** + * TimeStampTokenProvider that uses its own internal time source and generator. + * In other words, an off-line time stamp token generator. To be used only for tests. + * @author Hubert Kario + * + */ +// only package visible, users shouldn't use this class +class SurrogateTimeStampTokenProvider implements TimeStampTokenProvider +{ + private static FullCert tsaCert; + private static String algorithm = "SHA1withRSA"; + private static Date now = null; + private static BigInteger serial; + + private static final Map digestUriToOidMappings; + static + { + digestUriToOidMappings = new HashMap(6); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_NOT_RECOMMENDED_MD5, TSPAlgorithms.MD5); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_RIPEMD160, TSPAlgorithms.RIPEMD160); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1, TSPAlgorithms.SHA1); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256, TSPAlgorithms.SHA256); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA384, TSPAlgorithms.SHA384); + digestUriToOidMappings.put(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA512, TSPAlgorithms.SHA512); + } + + private MessageDigestEngineProvider mdEngineProvider; + + /** + * change time stamp token signature algorithm from default SHA1withRSA + * @param alg + */ + public static void setSigAlgorithm(String alg) + { + algorithm = alg; + } + + public static void setTSACert(FullCert cert) + { + tsaCert = cert; + } + + public static void setTimeAndSerial(Date time, BigInteger serial) + { + now = time; + SurrogateTimeStampTokenProvider.serial = serial; + } + + @Inject + public SurrogateTimeStampTokenProvider(MessageDigestEngineProvider messageDigest) + { + mdEngineProvider = messageDigest; + } + + @Override + public TimeStampTokenRes getTimeStampToken(byte[] tsDigestInput, + String digestAlgUri) throws TimeStampTokenGenerationException + { + if (now == null) + throw new IllegalStateException("Double use or not initialised"); + + try + { + // calculate digest of data + MessageDigest md = mdEngineProvider.getEngine(digestAlgUri); + byte[] digest = md.digest(tsDigestInput); + + // create time stamp token generator + TimeStampTokenGenerator tokenGenerator; + + DigestCalculator sha1DigestCalculator = new DigestCalculator() + { + /* SHA1 is hardcoded in few places in the library itself so hardcoding it + * ourselves we don't make the situation as bad as it looks + */ + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + } + + public OutputStream getOutputStream() + { + return bOut; + } + + public byte[] getDigest() + { + try + { + return MessageDigest.getInstance("SHA-1").digest(bOut.toByteArray()); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalStateException("Cannot create sha-1 hash: "+ e.getMessage()); + } + } + }; + /* because Bouncy Castle adds *two* timestamps to a token (one from system + * time, in attributes of CMS signature, and one from Date passed to + * generate() method we have to override the first one. + * + * Otherwise we could simply run + * + * SignerInfoGenerator signerInfoGen = new JcaSimpleSignerInfoGeneratorBuilder() + * .build(algorithm, tsaCert.getPrivateKey(), tsaCert.getCertificate()); + */ + JcaSignerInfoGeneratorBuilder sigBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()); + + // create default signingTime CMS attribute to be added to + // signedAttributes + // in CMS (RFC 3161 token) signature + Hashtable signedAttr = + new Hashtable(); + Attribute attr = new Attribute(CMSAttributes.signingTime, + new DERSet(new Time(now))); + signedAttr.put(attr.getAttrType(), attr); + AttributeTable signedAttributeTable = new AttributeTable(signedAttr); + + sigBuilder.setSignedAttributeGenerator( + new DefaultSignedAttributeTableGenerator(signedAttributeTable)); + + SignerInfoGenerator signerInfoGen = sigBuilder.build( + new JcaContentSignerBuilder(algorithm).setProvider("BC") + .build(tsaCert.getPrivateKey()), tsaCert + .getCertificate()); + + // "1.2" is a "no policy" policy ID + tokenGenerator = new TimeStampTokenGenerator(signerInfoGen, + sha1DigestCalculator, new ASN1ObjectIdentifier("1.2")); + + //tokenGenerator.addCertificates(caCerts); + + // generate signing request + TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator(); + TimeStampRequest request = reqGen.generate(digestUriToOidMappings.get(digestAlgUri), digest); + + // create response signer + TimeStampResponseGenerator tsrg = new TimeStampResponseGenerator(tokenGenerator, + TSPAlgorithms.ALLOWED); + + // sign request + TimeStampResponse resp = tsrg.generate(request, serial, now); + + // extract token from response + TimeStampToken tsToken = resp.getTimeStampToken(); + + now = null; // reset, so that every use needs to set time it wants to see + + return new TimeStampTokenRes(tsToken.getEncoded(), + tsToken.getTimeStampInfo().getGenTime()); + + } catch (UnsupportedAlgorithmException e) + { + throw new TimeStampTokenGenerationException("Digest algorithm not supported", e); + } catch (Exception e) + { + throw new TimeStampTokenGenerationException("Something went wrong", e); + } + } +} diff --git a/src/test/java/xades4j/verification/VerifierTestBase.java b/src/test/java/xades4j/verification/VerifierTestBase.java index 543272cf..b78928c8 100644 --- a/src/test/java/xades4j/verification/VerifierTestBase.java +++ b/src/test/java/xades4j/verification/VerifierTestBase.java @@ -27,8 +27,10 @@ import org.w3c.dom.Element; import xades4j.properties.ObjectIdentifier; import xades4j.providers.CertificateValidationProvider; +import xades4j.providers.TSACertificateValidationProvider; import xades4j.utils.FileSystemDirectoryCertStore; import xades4j.providers.impl.PKIXCertificateValidationProvider; +import xades4j.providers.impl.PKIXTSACertificateValidationProvider; import xades4j.providers.SignaturePolicyDocumentProvider; import xades4j.utils.SignatureServicesTestBase; @@ -40,7 +42,9 @@ public class VerifierTestBase extends SignatureServicesTestBase { static SignaturePolicyDocumentProvider policyDocumentFinder; public static CertificateValidationProvider validationProviderMySigs; + public static TSACertificateValidationProvider tsaValidationProviderMySigs; public static CertificateValidationProvider validationProviderNist; + public static TSACertificateValidationProvider tsaValidationProviderNist; public static CertificateValidationProvider validationProviderPtCc; static @@ -61,14 +65,26 @@ public InputStream getSignaturePolicyDocumentStream( // signatures without revocation data. FileSystemDirectoryCertStore certStore = createDirectoryCertStore("my"); KeyStore ks = createAndLoadJKSKeyStore("my/myStore", "mystorepass"); - validationProviderMySigs = new PKIXCertificateValidationProvider(ks, false, certStore.getStore()); + validationProviderMySigs = new PKIXCertificateValidationProvider(ks, + false, + certStore.getStore()); + tsaValidationProviderMySigs = new PKIXTSACertificateValidationProvider(ks, + false, + certStore.getStore()); // Validation provider with certificates/CRL from "csrc.nist" folder // and TSA CRL. Used for signatures with complete validation data. certStore = createDirectoryCertStore("csrc.nist"); FileSystemDirectoryCertStore gvaCRLStore = createDirectoryCertStore("gva"); ks = createAndLoadJKSKeyStore("csrc.nist/trustAnchor", "password"); - validationProviderNist = new PKIXCertificateValidationProvider(ks, true, certStore.getStore(), gvaCRLStore.getStore()); + validationProviderNist = new PKIXCertificateValidationProvider(ks, + true, + certStore.getStore(), + gvaCRLStore.getStore()); + tsaValidationProviderNist = new PKIXTSACertificateValidationProvider(ks, + true, + certStore.getStore(), + gvaCRLStore.getStore()); // Validation provider for "pt" folder. Used for signatures produced // with the PT citizen card. @@ -90,7 +106,8 @@ public InputStream getSignaturePolicyDocumentStream( protected static XAdESForm verifySignature(String sigFileName) throws Exception { - return verifySignature(sigFileName, new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs), null); + return verifySignature(sigFileName, new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs)); } protected static XAdESForm verifySignature( @@ -104,7 +121,8 @@ protected static XAdESForm verifySignature( String sigFileName, SignatureSpecificVerificationOptions options) throws Exception { - return verifySignature(sigFileName, new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs), options); + return verifySignature(sigFileName, new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs), options); } private static XAdESForm verifySignature( diff --git a/src/test/java/xades4j/verification/XadesVerificationProfileTest.java b/src/test/java/xades4j/verification/XadesVerificationProfileTest.java index 7be842fa..af7992d5 100644 --- a/src/test/java/xades4j/verification/XadesVerificationProfileTest.java +++ b/src/test/java/xades4j/verification/XadesVerificationProfileTest.java @@ -30,7 +30,9 @@ public class XadesVerificationProfileTest public void testGetVerifier() throws XadesProfileResolutionException { System.out.println("getVerifier"); - XadesVerificationProfile instance = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs); + XadesVerificationProfile instance = + new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs); XadesVerifier result = instance.newVerifier(); assertNotNull(result); } diff --git a/src/test/java/xades4j/verification/XadesVerifierErrorsTest.java b/src/test/java/xades4j/verification/XadesVerifierErrorsTest.java index b850443b..3ab269d7 100644 --- a/src/test/java/xades4j/verification/XadesVerifierErrorsTest.java +++ b/src/test/java/xades4j/verification/XadesVerifierErrorsTest.java @@ -17,10 +17,12 @@ package xades4j.verification; import java.security.KeyStore; +import static org.junit.Assert.assertEquals; import org.junit.Test; import static org.junit.Assume.assumeTrue; import org.junit.Before; -import xades4j.providers.CannotSelectCertificateException; + +import xades4j.providers.CannotBuildCertificationPathException; import xades4j.providers.impl.PKIXCertificateValidationProvider; /** @@ -35,8 +37,12 @@ public class XadesVerifierErrorsTest extends VerifierTestBase @Before public void initialize() { - mySigsVerificationProfile = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs); - nistVerificationProfile = new XadesVerificationProfile(VerifierTestBase.validationProviderNist); + mySigsVerificationProfile = new XadesVerificationProfile( + VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs); + nistVerificationProfile = new XadesVerificationProfile( + VerifierTestBase.validationProviderNist, + VerifierTestBase.tsaValidationProviderMySigs); } @Test(expected = QualifyingPropertiesIncorporationException.class) @@ -53,7 +59,8 @@ public void testErrVerifySignedPropsIncorpNoRefType() throws Exception assumeTrue(onWindowsPlatform() && null != validationProviderPtCc); verifyBadSignature("document.signed.bes.signedpropsrefnotype.xml", - new XadesVerificationProfile(validationProviderPtCc)); + new XadesVerificationProfile(validationProviderPtCc, + tsaValidationProviderMySigs)); } @Test(expected = InvalidXAdESFormException.class) @@ -63,14 +70,15 @@ public void testErrVerifyIncorrectC() throws Exception verifyBadSignature("document.signed.c.bad.xml",nistVerificationProfile); } - @Test(expected = CannotSelectCertificateException.class) + @Test(expected = CannotBuildCertificationPathException.class) public void testErrVerifyNoSignCert() throws Exception { System.out.println("ErrVerifyNoSignCert"); KeyStore ks = createAndLoadJKSKeyStore("be/beStore", "bestorepass"); PKIXCertificateValidationProvider cvp = new PKIXCertificateValidationProvider(ks, false); - verifyBadSignature("TSL_BE.nocert.xml", new XadesVerificationProfile(cvp)); + verifyBadSignature("TSL_BE.nocert.xml", new XadesVerificationProfile(cvp, + tsaValidationProviderMySigs)); } @Test(expected = ReferenceValueException.class) @@ -87,14 +95,14 @@ public void testErrVerifyChangedSigValue() throws Exception verifyBadSignature("document.signed.bes.invalidsigvalue.xml", mySigsVerificationProfile); } - @Test(expected = CompleteCertRefsCertNotFoundException.class) + @Test(expected = InvalidXAdESFormException.class) public void testErrVerifyCMissingCertRef() throws Exception { System.out.println("errVerifyCMissingCertRef"); verifyBadSignature("document.signed.c.missingcertref.xml", nistVerificationProfile); } - @Test(expected = TimeStampDigestMismatchException.class) + @Test(expected = AssertionError.class) public void testErrVerifyUnmatchSigTSDigest() throws Exception { // DefaultTimeStampTokenProvider tsProv = new DefaultTimeStampTokenProvider(new DefaultMessageDigestProvider()); @@ -106,7 +114,8 @@ public void testErrVerifyUnmatchSigTSDigest() throws Exception // outputDocument(doc, "bad/document.signed.t.bes.badtsdigest.xml"); System.out.println("errVerifyUnmatchSigTSDigest"); - verifyBadSignature("document.signed.t.bes.badtsdigest.xml", mySigsVerificationProfile); + XAdESForm form = verifySignature("bad/" + "document.signed.t.bes.badtsdigest.xml", mySigsVerificationProfile); + assertEquals(XAdESForm.T, form); } private static void verifyBadSignature(String sigFileName, XadesVerificationProfile p) throws Exception diff --git a/src/test/java/xades4j/verification/XadesVerifierImplTest.java b/src/test/java/xades4j/verification/XadesVerifierImplTest.java index b15111ae..5dda680f 100644 --- a/src/test/java/xades4j/verification/XadesVerifierImplTest.java +++ b/src/test/java/xades4j/verification/XadesVerifierImplTest.java @@ -19,26 +19,27 @@ import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assume.assumeTrue; import java.io.FileInputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; - import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; - import xades4j.production.XadesFormatExtenderProfile; import xades4j.production.XadesSignatureFormatExtender; +import xades4j.properties.ArchiveTimeStampProperty; +import xades4j.properties.AttrAuthoritiesCertValuesProperty; +import xades4j.properties.AttributeRevocationValuesProperty; import xades4j.properties.CertificateValuesProperty; import xades4j.properties.QualifyingProperty; import xades4j.properties.RevocationValuesProperty; import xades4j.properties.SigAndRefsTimeStampProperty; import xades4j.providers.CannotBuildCertificationPathException; +import static org.junit.Assume.assumeTrue; /** * @@ -52,8 +53,12 @@ public class XadesVerifierImplTest extends VerifierTestBase @Before public void initialize() { - verificationProfile = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs); - nistVerificationProfile = new XadesVerificationProfile(VerifierTestBase.validationProviderNist); + verificationProfile = new XadesVerificationProfile( + VerifierTestBase.validationProviderMySigs, + VerifierTestBase.tsaValidationProviderMySigs); + nistVerificationProfile = new XadesVerificationProfile( + VerifierTestBase.validationProviderNist, + VerifierTestBase.tsaValidationProviderNist); } @Test @@ -74,7 +79,7 @@ public void testVerifyBESWithVerificationDate() throws Exception System.out.println("testVerifyBESWithVerificationDate"); String sigFilename = "document.signed.bes.xml"; Element signatureNode = getSigElement(getDocument(sigFilename)); - XadesVerificationProfile p = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs); + XadesVerificationProfile p = new XadesVerificationProfile(VerifierTestBase.validationProviderMySigs, VerifierTestBase.tsaValidationProviderMySigs); Date verificationDate = new SimpleDateFormat("YYYY").parse("2041"); p.newVerifier().verify(signatureNode, new SignatureSpecificVerificationOptions().setDefaultVerificationDate(verificationDate)); @@ -197,13 +202,13 @@ public void testVerifyTEPES() throws Exception } @Test - public void testVerifyTPTCC() throws Exception - { + public void testVerifyTPTCC() throws Exception { System.out.println("verifyTPtCC"); assumeTrue(onWindowsPlatform() && null != validationProviderPtCc); XAdESForm f = verifySignature("document.signed.t.bes.ptcc.xml", - new XadesVerificationProfile(validationProviderPtCc)); + new XadesVerificationProfile(validationProviderPtCc, + tsaValidationProviderMySigs)); assertEquals(XAdESForm.T, f); } @@ -236,6 +241,24 @@ public void testVerifyDetachedC() throws Exception assertEquals(XAdESForm.C, res.getSignatureForm()); } + @Test + public void testVerifyCEnrichX() throws Exception + { + System.out.println("verifyCEnrichX"); + + Document doc = getDocument("document.signed.c.xml"); + Element signatureNode = getSigElement(doc); + + XadesSignatureFormatExtender formExt = new XadesFormatExtenderProfile().getFormatExtender(); + XAdESVerificationResult res = nistVerificationProfile.newVerifier().verify(signatureNode, null, formExt, XAdESForm.X); + + assertEquals(XAdESForm.C, res.getSignatureForm()); + assertPropElementPresent(signatureNode, SigAndRefsTimeStampProperty.PROP_NAME); + + outputDocument(doc, "document.verified.c.x.xml"); + + } + @Test public void testVerifyCEnrichXL() throws Exception { @@ -251,10 +274,84 @@ public void testVerifyCEnrichXL() throws Exception assertPropElementPresent(signatureNode, SigAndRefsTimeStampProperty.PROP_NAME); assertPropElementPresent(signatureNode, CertificateValuesProperty.PROP_NAME); assertPropElementPresent(signatureNode, RevocationValuesProperty.PROP_NAME); + assertPropElementPresent(signatureNode, AttrAuthoritiesCertValuesProperty.PROP_NAME); + assertPropElementPresent(signatureNode, AttributeRevocationValuesProperty.PROP_NAME); outputDocument(doc, "document.verified.c.xl.xml"); } + @Test + public void testVerifyX() throws Exception + { + System.out.println("verifyX"); + XAdESForm f = verifySignature( + "document.verified.c.x.xml", + nistVerificationProfile); + assertEquals(XAdESForm.X, f); + } + + @Test + public void testVerifyXL() throws Exception + { + System.out.println("verifyXL"); + XAdESForm f = verifySignature( + "document.verified.c.xl.xml", + nistVerificationProfile); + assertEquals(XAdESForm.X_L, f); + } + + @Test + public void testVerifyXLEnrichA() throws Exception + { + System.out.println("testVerifyXLEnrichA"); + + Document doc = getDocument("document.verified.c.xl.xml"); + Element signatureNode = getSigElement(doc); + + XadesSignatureFormatExtender formExt = + new XadesFormatExtenderProfile().getFormatExtender(); + XAdESVerificationResult res = nistVerificationProfile.newVerifier().verify( + signatureNode, null, formExt, XAdESForm.A); + + assertEquals(XAdESForm.X_L, res.getSignatureForm()); + assertPropXAdES141ElementPresent(signatureNode, ArchiveTimeStampProperty.PROP_NAME); + + outputDocument(doc, "document.verified.c.xl.a.xml"); + } + + @Test + public void testVerifyA() throws Exception + { + System.out.println("testVerifyA"); + XAdESForm f = verifySignature( + "document.verified.c.xl.a.xml", + nistVerificationProfile); + assertEquals(XAdESForm.A, f); + } + + @Test + public void testVerifyAEnrichAVD() throws Exception + { + System.out.println("testVerifyXLEnrichA"); + + Document doc = getDocument("document.verified.c.xl.a.xml"); + Element signatureNode = getSigElement(doc); + + XadesSignatureFormatExtender formExt = + new XadesFormatExtenderProfile().getFormatExtender(); + XAdESVerificationResult res = nistVerificationProfile.newVerifier().verify( + signatureNode, null, formExt, XAdESForm.A_VD); + + assertEquals(XAdESForm.A, res.getSignatureForm()); + // TODO recent changes to verifier made the property generated only when needed, + // as all information that would be stored in it is already present in other + // properties, the property won't be created + //assertPropXAdES141ElementPresent(signatureNode, TimeStampValidationDataProperty.PROP_NAME); + + outputDocument(doc, "document.verified.c.xl.avd.xml"); + } + + private static void assertPropElementPresent( Element sigElem, String elemName) @@ -262,4 +359,13 @@ private static void assertPropElementPresent( NodeList props = sigElem.getElementsByTagNameNS(QualifyingProperty.XADES_XMLNS, elemName); assertFalse(props.getLength() == 0); } + + private static void assertPropXAdES141ElementPresent( + Element sigElem, + String elemName) + { + NodeList props = sigElem.getElementsByTagNameNS( + QualifyingProperty.XADESV141_XMLNS, elemName); + assertFalse(props.getLength() == 0); + } } diff --git a/src/test/xml/detached.bes.xml b/src/test/xml/detached.bes.xml index 10816220..6ee7d55e 100644 --- a/src/test/xml/detached.bes.xml +++ b/src/test/xml/detached.bes.xml @@ -59,4 +59,4 @@ Ahu4PzJ6hggAlWWMy245JwIYuV0s1Oi39GVTxVNOBIX//AONZlGWO4S2Psb1mqdZ99b/MugsaA== 2017-05-20T19:16:05.649+01:004btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869text/xml - \ No newline at end of file + diff --git a/src/test/xml/detached.c.xml b/src/test/xml/detached.c.xml index 6f75353f..9aa46e5f 100644 --- a/src/test/xml/detached.c.xml +++ b/src/test/xml/detached.c.xml @@ -129,4 +129,4 @@ t270x65y/F778AmXQZQflwTPC9pUf9S0ET0Oncmc3vzyrCXcdoQUMGpoupZANQ1HGyT61IQounKq DE2UdrY5ECL7GvIvsIXZVu6Jbe6DMlvvPZTltBr6HEterdgNkBy5ipEM61Ob+GTRnry2RLDb3oel jZsD3fwprSBBYtwZQxuljF8+R3MwkC2NdamZFxkg1uBzenTaVBccdyoJzr4CCHbDBps6QKaVGS/W VkuhSCERQoMkn9hbhH3I/0pT9FgEH0JqayIj4ywHC7fXD+/fK8XiAAAAAA==6F5s4TtqSCTt+/C6kxSWxGiyQ/2uBQ/tQ2IFjNjna3U=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1 - \ No newline at end of file + diff --git a/src/test/xml/document.signed.bes.cs.xml b/src/test/xml/document.signed.bes.cs.xml index 72758057..e39d5450 100644 --- a/src/test/xml/document.signed.bes.cs.xml +++ b/src/test/xml/document.signed.bes.cs.xml @@ -26,26 +26,26 @@ - + - + rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ= - + -pfrrRjkGNPiRF2Bjuj3IJTGSEFYc2PDs6FCNAUJOu8E= +xZEYSpzc7i/0nQ0s7P4N9sOzFpLUvP4/RXstelh9VSs= - -Vb/2ay4rX9cytiRCmBkgRkE4d0E05nkM412CwOHvTagCXPZZ2B2ifbmrQznTEU6Jz0dF65j5kU7C -bYK+9fMKB0lmqx2kUdWeBa30Rl/3ELKWKOLeDFN91zC3q4a6dtzQGwBidWex+2dO8Z69sBd2aTS6 -S6nyGs53kTspDHfBeyg= + +hFAXNfsTOgO6dICHSH+/pjnIfHkPyJTGZ6yonVJC5Z9BwxNGbTnJHbwSHScZgMMl3XQcyzreqBIn +64k8WkPQj8bpz/3LpKuVxU6T/fmNqiQ1sCz6lJQQPLZWGB3JZINOUgZMrNt5kyt7Sm/yzK7ldJ4R +BhaJHCE8/CjZoq3bSZU= @@ -64,23 +64,23 @@ Ahu4PzJ6hggAlWWMy245JwIYuV0s1Oi39GVTxVNOBIX//AONZlGWO4S2Psb1mqdZ99b/MugsaA== -4btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869CounterSignature maniac +4btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869CounterSignature maniac - + -Nr5JKCScyqjKWneJw2AC6YmdU/2JZA5Pj40fkVCFbL4= +1beHuwu4eWbtenZbo12JwTq/UMVjFe0jzd4VbmXvfSk= - + -PJWDlPFdx4TJ5AXT3nIbFa2euboYGM7bpuBmol4nKSQ= +st13c9/4E9J4F3CVg00e16Sk4PtFL70aSk73v6B9Bd8= - -MCcli4l5rAsGeJX2gOmoYDIAXnbszmRwOH94y7kHnpxCwGwoVrCaLSONtgKEy8mj9Ehqa/R5Dj5g -DgC77VkNIaRKD613JMnbsaqZ7PD0iVivLvBkasTch4PX92UEbk8EvCgzxvhlgN+L6zSLsv65sFcR -lFUGluMtzrqlDKvts38= + +WUlk+RW1vw8babYeqYedwRudkcac5J2L5PyZVFRYHVi9Pg6OiM9Vwea0rR7G4pj4LvGEmrcrGtr5 +gcN7qTWh3+3cdRdmgMLX2OWhMOpb4xDXCreXV9t5KX7e8p2Iv0bf77aKC4MN0cQqW3I2/UldsuJM +JFxk0U1sSW8KobFMUP0= @@ -99,6 +99,6 @@ Ahu4PzJ6hggAlWWMy245JwIYuV0s1Oi39GVTxVNOBIX//AONZlGWO4S2Psb1mqdZ99b/MugsaA== -2012-10-13T14:26:18.783+01:004btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869 +2017-02-06T15:20:05.160-02:004btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869 \ No newline at end of file diff --git a/src/test/xml/document.signed.bes.extres.xml b/src/test/xml/document.signed.bes.extres.xml index b21c7fe3..0cea533a 100644 --- a/src/test/xml/document.signed.bes.extres.xml +++ b/src/test/xml/document.signed.bes.extres.xml @@ -129,4 +129,4 @@ ljkYzFJf2Ax8wY44yTeuNCn29SeHG1Ga3FpTjitu1bogo1RzTtIqtVYB6+lfwfzKwq+VmD298gfl HnyW+oXUdRn1l3pWfE/yrk4YPpK9WKbtpe0Rk2QnEIyfl97h1nARrsxB6RvZsjhqX/m7Ss+iJzhh 7EYA6rN8PGuyNmkkSxjnnK/pmfVYCZfH29rwfmvRDkrex3H0r2/v5iZkhl7msmtGZTU7w3+/NN8v 4g32qJdbOZ1QxFcEv+vLpJB1m1rCpF7g8hityiZgtmH96EWV1zakAAAAAA== - \ No newline at end of file + diff --git a/src/test/xml/document.signed.bes.xml b/src/test/xml/document.signed.bes.xml index c5598a05..4ee0c3f9 100644 --- a/src/test/xml/document.signed.bes.xml +++ b/src/test/xml/document.signed.bes.xml @@ -266,4 +266,4 @@ TbiMMqnA8rXLG4CMf45zyMjmCiXDk7T0WWwWHqlzXvddUGLg2Qydqz7YQOkohZguMbFJnTwHour+ IvZ9bRr9oFUeuwNN2C3EPCouTieT0f9hkz8EsB/kv94j+mxLcKXb3MZJX0DpZK+ChU20icLzvPM6 bx8mchtEa2titwwnA1EMR6avzDd6wueEccQUBJC9FFoGwN4VE8wDUpouiYrhPGeeNcK9MGhwO81/ 9OUidEzJa7+x0aeXwRQls8+VuwgfyBa7KD9Cl0Vt3C1MQu8Edgv5AAAAAA== - \ No newline at end of file + diff --git a/src/test/xml/document.signed.c.xml b/src/test/xml/document.signed.c.xml index 2e4f78ee..6588c4c8 100644 --- a/src/test/xml/document.signed.c.xml +++ b/src/test/xml/document.signed.c.xml @@ -160,4 +160,4 @@ Cekhoi7bllq32DR1iDHQyM/QxrY2lPt6iFUmjZjwVA+JobnFN0yUwKktw6zvJpBCj44zU5TxgxhG 80w0ZRGnqhjGI1+nbhizNoS4shILRIF1DmW0MK2qFcnPnQggncLnBOJiI41vNi9QXRa6uB0HvcRt gIeUENZxDteMMKKmJ+KfkzxkO38kZ+7GDU8dkTE9JPtqQsh2CakwbQJTFf6Rc9NhPgWw17FXhebM Cu8HAKCqf6lFv3jT9RsHxrYK60C8oARJQwnElSULswg26Wo4P0NgAAAAAA==6F5s4TtqSCTt+/C6kxSWxGiyQ/2uBQ/tQ2IFjNjna3U=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1 - \ No newline at end of file + diff --git a/src/test/xml/document.signed.epes.xml b/src/test/xml/document.signed.epes.xml new file mode 100644 index 00000000..f6ba8928 --- /dev/null +++ b/src/test/xml/document.signed.epes.xml @@ -0,0 +1,68 @@ + + + Questions, unanswered + Steve and the flubberblubs + 1989 + + + What do you know? + Steve and the flubberblubs + 2006-10-17-08:31 + + + Who do you know? + Steve and the flubberblubs + 2006-10-17-08:35 + + + When do you know? + Steve and the flubberblubs + 2006-10-17-08:39 + + + Do you know? + Steve and the flubberblubs + 2006-10-17-08:44 + + + + + + + + + + + + +rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ= + + + +t4kyAbjOiEoiYF8TuAI9H+MuZpMfKhpRQqbICIgyE6E= + + + +G/rANuc0vjLuNbf/jByuFy3BnknZ7QgNjwMmw0/jypd0K692W4aJtNjv8GiYH0YbWdoTM3A89ywG +e4MUpBz5wm4UaHZffhEM2hLIfRvi/j7doq1OZ3XlXJToMKhIxnX2sm5/nVrSsi9Caj9DPhgk8U/Q +R/7IxyzPBO5kqcKsyyM= + + + + +MIICbTCCAdqgAwIBAgIQpkK0uals+ItHxBlpJuypOTAJBgUrDgMCHQUAMD8xCzAJBgNVBAYTAlBU +MQ0wCwYDVQQKEwRJU0VMMQswCQYDVQQLEwJDQzEUMBIGA1UEAxMLSXRlcm1lZGlhdGUwHhcNMTAw +NjI1MTc1ODQ5WhcNMzkxMjMxMjM1OTU5WjBCMQswCQYDVQQGEwJQVDENMAsGA1UEChMESVNFTDEL +MAkGA1UECxMCQ0MxFzAVBgNVBAMTDkx1aXMgR29uY2FsdmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQCpP9acMX69Dbg9ciMLFc5dm1tlpTY9OTNZ/EaCYoGVhh/3+DFgyIbEer6SA24hpREm +AhNG9+Ca0AurDPPgb3aKWFY9pj1WcOctis0VsR0YvzqP+2IGFqKDCd7bXFvv2tI0dEvpdc0oO6PF +Q02xvJG0kxQf44XljOCjUBU43jkJawIDAQABo28wbTBrBgNVHQEEZDBigBBdbbL4pDKLT56PpOpA +/56toTwwOjELMAkGA1UEBhMCUFQxDTALBgNVBAoTBElTRUwxCzAJBgNVBAsTAkNDMQ8wDQYDVQQD +EwZUZXN0Q0GCEN00x9qe7SuWQvpLK0/oay8wCQYFKw4DAh0FAAOBgQBSma8g9dQjiQo4WUljRRuG +yMUVRyCqW/9oRz8+0EoLNR/AhrIlGqdNbqQ1BkncgNNdqMAus5VD34v/EhgrkgWN5fZajMpYsmcR +Ahu4PzJ6hggAlWWMy245JwIYuV0s1Oi39GVTxVNOBIX//AONZlGWO4S2Psb1mqdZ99b/MugsaA== + + + +2017-02-06T15:20:07.457-02:004btVb5gQ5cdcNhGpvDSWQZabPQrR9jf1x8e3YF9Ajss=CN=Itermediate,OU=CC,O=ISEL,C=PT-119284162484605703133798696662099777223vm5QpbblsWV7fCYXotPhNTeCt4nk8cLFuF36L5RJ4Ok=CN=TestCA,OU=CC,O=ISEL,C=PT-46248926895392336918291885380930606289AUaN+IdhKQqxIVmEOrFwq+Dn22ebTkXJqD3BoOP/x8E=CN=TestCA,OU=CC,O=ISEL,C=PT-99704378678639105802976522062798066869oid:/1.2.4.0.9.4.5MaW9PDxJruPHhjBVAMWyrF9zP+kMnNqkoQOUXxDDGAk= + \ No newline at end of file diff --git a/src/test/xml/document.signed.t.bes.xml b/src/test/xml/document.signed.t.bes.xml index d574f0c3..dc671659 100644 --- a/src/test/xml/document.signed.t.bes.xml +++ b/src/test/xml/document.signed.t.bes.xml @@ -98,4 +98,4 @@ Lcd/KV5jxrcvYwfFt9HnEoze9Muboc51M38MeyezM2fpZnOh/w5jjxlAnRAJvnmCuGeoMI/8eKU0 JKjkudNgvHeO5yLaR//MltpWHV0nAyCSZaR/2zfwRh+mBvADSESUx7RmQcal5Be5SiRbqvrL2vvP vbeixAwGn9ubzPFP06sncYTx83QDTGmeDcJAfTsr6CEuwdqDipS1MinqGQj+BvvT0mND3EgTp1KP fSr/r5XLGjjWmJRaNXZh9AXupNjUn6RFri22J4N2+XCKzXQHoJ5Q8EfjjMoAAAAA - \ No newline at end of file + diff --git a/src/test/xml/document.signed.t.epes.xml b/src/test/xml/document.signed.t.epes.xml index e9cde6a7..4de6b9e4 100644 --- a/src/test/xml/document.signed.t.epes.xml +++ b/src/test/xml/document.signed.t.epes.xml @@ -159,4 +159,4 @@ bwVIR3TupA+gzwB3O2ZIA+ihperl9vW8XeAvf9nbzbB8laKRMb2PcG1b0vtv/QyP+7IPZeRIrwXB hhiFW3hRCckdJ0sy3qt70KJXjnREG533I1vvP/b/kWFQqtI7CyB43ePsrwGJpNqpN7YgdWhUMVSu 2/5zoGHJJQeIOuO5JLfnebBK/lf67r21N+g+jiSeDM02Rs9+WIUEHJ4RYEYbBCA69+jwPKXDN8V7 ULGDXMEbeJgfTUqGzHmlpxfX0DhukqFuN+3qnmJFTJQKicCpqYdwAAAAAA== - \ No newline at end of file + diff --git a/src/test/xml/document.verified.c.x.xml b/src/test/xml/document.verified.c.x.xml new file mode 100644 index 00000000..89e4b36f --- /dev/null +++ b/src/test/xml/document.verified.c.x.xml @@ -0,0 +1,69 @@ + + + Questions, unanswered + Steve and the flubberblubs + 1989 + + + What do you know? + Steve and the flubberblubs + 2006-10-17-08:31 + + + Who do you know? + Steve and the flubberblubs + 2006-10-17-08:35 + + + When do you know? + Steve and the flubberblubs + 2006-10-17-08:39 + + + Do you know? + Steve and the flubberblubs + 2006-10-17-08:44 + + + + + + + + + + + + +rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ= + + + +zwzK6BtmRlNofKKECv1Pu1ZeHY2N3lKYNY4IlQ/Fqrw= + + + +ul1on82xj75EKz2DmGTae0D80R9FKH36MV8wKoIWOMOjXR1FqrEBusBO6e4GaBoLQrWfqB8cANNY +OnhO8Qt570RwqEiA0pzF8tCY0aoJJ/bA+KDSgfyvy/5XNZvtrz8bU8BrpdIDha2SIWuS9vYfyxDZ +2kCy5dTYFQOps08uLac= + + + + +MIIChjCCAe+gAwIBAgIBCDANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMP +VS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMT +DENBMi1DUC4wMi4wMTAeFw05OTAxMDExMjAxMDBaFw00ODAxMDExMjAxMDBaMGAxCzAJBgNVBAYT +AlVTMRgwFgYDVQQKEw9VLlMuIEdvdmVybm1lbnQxDDAKBgNVBAsTA0RvRDEQMA4GA1UECxMHVGVz +dGluZzEXMBUGA1UEAxMOVXNlcjEtQ1AuMDIuMDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AOzYq2murB5ZjQd4wReI51Lc1F5VwK90OMGRfi71YvwdRjgCudeDXZGW5ayid82y+eTDKFSzo1Li +/BPTUXMpeqHHMCmLeefqxAWmz3aDoilF8IQ53PlejnXJdntsal44w6WdP6ssiXlwzcZDnobAfuDT +PgsnWWfzAkr1/LqEw/QZAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIF4DAWBgNVHSAEDzANMAsGCWCG +SAFlAwEwATARBgNVHQ4ECgQIP5tVdEyxotcwEwYDVR0jBAwwCoAIoI0mSmDmzZUwDQYJKoZIhvcN +AQEFBQADgYEAkVx9S/20Hir8qMnfMpMGTgMKoVeWoljxim83IkNs1Xqe1oLGHdyDUA66uF8wPkoT +qGrfDYvgBa5Mi0iJREnMWoiWvCe467+L1b2gtvRBMl9bcRj40bvelk0Wn4lBl3VuKXarP5M0PKT5 +OWvN2cPLNeXHvV6ZIrC4rmK2ISpIXX4= + + + +2017-02-06T15:23:21.057-02:00yScwzjo9znhFNIxY7fl2gnK1UhFTFcjXJE8aGBgfzfk=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=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CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02: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 + \ No newline at end of file diff --git a/src/test/xml/document.verified.c.xl.a.xml b/src/test/xml/document.verified.c.xl.a.xml new file mode 100644 index 00000000..d330d14f --- /dev/null +++ b/src/test/xml/document.verified.c.xl.a.xml @@ -0,0 +1,69 @@ + + + Questions, unanswered + Steve and the flubberblubs + 1989 + + + What do you know? + Steve and the flubberblubs + 2006-10-17-08:31 + + + Who do you know? + Steve and the flubberblubs + 2006-10-17-08:35 + + + When do you know? + Steve and the flubberblubs + 2006-10-17-08:39 + + + Do you know? + Steve and the flubberblubs + 2006-10-17-08:44 + + + + + + + + + + + + +rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ= + + + +zwzK6BtmRlNofKKECv1Pu1ZeHY2N3lKYNY4IlQ/Fqrw= + + + +ul1on82xj75EKz2DmGTae0D80R9FKH36MV8wKoIWOMOjXR1FqrEBusBO6e4GaBoLQrWfqB8cANNY +OnhO8Qt570RwqEiA0pzF8tCY0aoJJ/bA+KDSgfyvy/5XNZvtrz8bU8BrpdIDha2SIWuS9vYfyxDZ +2kCy5dTYFQOps08uLac= + + + + +MIIChjCCAe+gAwIBAgIBCDANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMP +VS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMT +DENBMi1DUC4wMi4wMTAeFw05OTAxMDExMjAxMDBaFw00ODAxMDExMjAxMDBaMGAxCzAJBgNVBAYT +AlVTMRgwFgYDVQQKEw9VLlMuIEdvdmVybm1lbnQxDDAKBgNVBAsTA0RvRDEQMA4GA1UECxMHVGVz +dGluZzEXMBUGA1UEAxMOVXNlcjEtQ1AuMDIuMDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AOzYq2murB5ZjQd4wReI51Lc1F5VwK90OMGRfi71YvwdRjgCudeDXZGW5ayid82y+eTDKFSzo1Li +/BPTUXMpeqHHMCmLeefqxAWmz3aDoilF8IQ53PlejnXJdntsal44w6WdP6ssiXlwzcZDnobAfuDT +PgsnWWfzAkr1/LqEw/QZAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIF4DAWBgNVHSAEDzANMAsGCWCG +SAFlAwEwATARBgNVHQ4ECgQIP5tVdEyxotcwEwYDVR0jBAwwCoAIoI0mSmDmzZUwDQYJKoZIhvcN +AQEFBQADgYEAkVx9S/20Hir8qMnfMpMGTgMKoVeWoljxim83IkNs1Xqe1oLGHdyDUA66uF8wPkoT +qGrfDYvgBa5Mi0iJREnMWoiWvCe467+L1b2gtvRBMl9bcRj40bvelk0Wn4lBl3VuKXarP5M0PKT5 +OWvN2cPLNeXHvV6ZIrC4rmK2ISpIXX4= + + + +2017-02-06T15:23:21.057-02:00yScwzjo9znhFNIxY7fl2gnK1UhFTFcjXJE8aGBgfzfk=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=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CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02: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 + \ No newline at end of file diff --git a/src/test/xml/document.verified.c.xl.xml b/src/test/xml/document.verified.c.xl.xml index f4fb7fb3..6066bc97 100644 --- a/src/test/xml/document.verified.c.xl.xml +++ b/src/test/xml/document.verified.c.xl.xml @@ -1,4 +1,4 @@ - + Questions, unanswered Steve and the flubberblubs @@ -26,26 +26,26 @@ - + - + rD/g8soqKz8EiPUBhEWfcQacS0ta4ULHX3dKMEH6ZoQ= - + -aUk8iTN0h7Snb2gsR/SVc6rDbNBWVbBEKUoX5jMyujU= +zwzK6BtmRlNofKKECv1Pu1ZeHY2N3lKYNY4IlQ/Fqrw= - -P06o2MQxP+6FNykD21GCPXnTVlJqxhRT3RUoo9oeEGY6iglq/A6b1lPANQyqVcD/od6nj1HaZ4pf -Kb/Pekgg5Z5pYcVICQ6Wd2jYC95WSCjnc7dA/l4uyZ+j/9jW2i2bzxNG3ORkj12j6RTnbkVH4Pz9 -9WFQqkHYHRkp1XV8Uww= + +ul1on82xj75EKz2DmGTae0D80R9FKH36MV8wKoIWOMOjXR1FqrEBusBO6e4GaBoLQrWfqB8cANNY +OnhO8Qt570RwqEiA0pzF8tCY0aoJJ/bA+KDSgfyvy/5XNZvtrz8bU8BrpdIDha2SIWuS9vYfyxDZ +2kCy5dTYFQOps08uLac= @@ -65,200 +65,5 @@ OWvN2cPLNeXHvV6ZIrC4rmK2ISpIXX4= -2012-10-13T14:26:32.006+01:00yScwzjo9znhFNIxY7fl2gnK1UhFTFcjXJE8aGBgfzfk=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US8MIAGCSqGSIb3DQEHAqCAMIIPNgIBAzELMAkGBSsOAwIaBQAwgcUGCyqGSIb3DQEJEAEEoIG1BIGy -MIGvAgEBBgwrBgEEAb9VAwIBAQAwITAJBgUrDgMCGgUABBSpE6FPADpE041XbFBOEZP3tqrdJQIE -AN8GzxgPMjAxMjEwMTMxMzI2MjhaAQH/AgYBOlpOAKygV6RVMFMxCzAJBgNVBAYTAkVTMR8wHQYD -VQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExEjAQBgNVBAMTCVRT -QTEgQUNDVqCCC5wwggUJMIID8aADAgECAgRFYzz2MA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT -AkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExJzAl -BgNVBAMTHlJvb3QgQ0EgR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTAeFw0wNjExMjExNzUyNTRaFw0x -NjExMTgxNjUyNTRaMFMxCzAJBgNVBAYTAkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNp -YW5hMQ8wDQYDVQQLEwZQS0lHVkExEjAQBgNVBAMTCVRTQTEgQUNDVjCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAKwMbrM6lluLkG5kI+8fgAEz85QfzTb3ATQxSKMRQiLYExhGHXWcDuBN -DUq3eoLcVEq1tBVNkbLTJzQc1rTaDHZhYfofKsEA8WSL+wc0uyHgYO3AeDeV1ZmC9DmPoh5PbWWb -pVu+MzWVG73lZgfcpPw+hr/S2r6WrZbNWqo95YW+I4Kyc9UGQ2C8OC4DOwcyvW8LFSX6aaW2QWaW -tCj4zPD0NzuV+DqHfnTbrbxO76RTN66vOscE7Z1NReZO+q0rI9s4+nMWGcgYl89iJrdTyjI7udqf -m3XDCurKiCjEX99qb+03Wl6PjTtmm0DiRQ76F9qQHSu47glgTA7SXVW1du8CAwEAAaOCAc4wggHK -MA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAWoGA1UdIASCAWEwggFd -MIIBWQYLKwYBBAG/VQMUAQAwggFIMIIBIAYIKwYBBQUHAgIwggESHoIBDgBTAGUAcgB2AGkAZABv -AHIAIABkAGUAIABTAGUAbABsAGEAZABvACAAZABlACAAVABpAGUAbQBwAG8AIABkAGUAIABsAGEA -IABBAHUAdABvAHIAaQB0AGEAdAAgAGQAZQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzACAAZABl -ACAAbABhACAAQwBvAG0AdQBuAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4AIABMAGEA -IABDAFAAUwAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAaAB0AHQAcAA6AC8ALwB3 -AHcAdwAuAGEAYwBjAHYALgBlAHMALwBjAHAAczAiBggrBgEFBQcCARYWaHR0cDovL3d3dy5hY2N2 -LmVzL2NwczAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5ndmEu -ZXMwDQYJKoZIhvcNAQEFBQADggEBABmIKSsrv7jfIX94OISi7g3Or2CRzyWaB0l05/u2oP5LlgdC -OrtQaZKjuIqinESwE5WVO51nQlNsUkhMPbZZOsZbo4+/otqMFpu64mfE9X8elEFQQ9D38JgXnk9H -1QNFiCEdCDFyOLcsrT4ttMpJuRecjgYW67LT0bYm5xe43/iSprnEFJt/MWeS0/6UZ+GVkWmQnXgA -HHCPKLfCoIpD9GgdvTelK/ofgntpIcX5fH8nbi4b1UhQbZFzlzQyHBF+D4bHE4HPJDXH/GUlFh6z -Ms+nxPgClOU/1GLaAdaPTNDbzToLZWo8XS+W/VnEb3ZhotH+GQHkgvqv8/R1a7HFKZ8wggaLMIIF -c6ADAgECAgQ7ReVoMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAkVTMR8wHQYDVQQKExZHZW5l -cmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExJzAlBgNVBAMTHlJvb3QgQ0EgR2Vu -ZXJhbGl0YXQgVmFsZW5jaWFuYTAeFw0wMTA3MDYxNjIyNDdaFw0yMTA3MDExNTIyNDdaMGgxCzAJ -BgNVBAYTAkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lH -VkExJzAlBgNVBAMTHlJvb3QgQ0EgR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAMYqq1cRNy8iisoDdB3K7S2iC7wzUkAmR75aaaY7cjYXTOjfuLsv -duFARnRlApBSCLT/qIzB4MeJVhA5M+9otF9f2m0joYleIqNKBvAn8Fe5+OlOMncKP0Fk8+tl7nb+ -VKp9HSCu89d0wgpf9QgoUgjMVV3SD9uagaW7obPBlM1U4DJ1MZEaYrLedeLPT4nZkZAPQRu0Wkp3 -vWeD4JPnXqcM54HT9FKsU7IDx0Qm+3nlyzRgUBB7G9tr10erX3xoym6dQQMQ7muZe14lqMKr5MDz -XJzjvs4xTGQeXoCi9YN+DNbKjFWOvuC+SQcPoyRBelgdhOpYEsjht+3vk96UCDECAwEAAaOCAzsw -ggM3MDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AucGtpLmd2YS5lczAS -BgNVHRMBAf8ECDAGAQH/AgECMIICNAYDVR0gBIICKzCCAicwggIjBgorBgEEAb9VAgEAMIICEzCC -AegGCCsGAQUFBwICMIIB2h6CAdYAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkA -ZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEcAZQBuAGUAcgBhAGwAaQB0 -AGEAdAAgAFYAYQBsAGUAbgBjAGkAYQBuAGEALgANAAoATABhACAARABlAGMAbABhAHIAYQBjAGkA -8wBuACAAZABlACAAUAByAOEAYwB0AGkAYwBhAHMAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBj -AGkA8wBuACAAcQB1AGUAIAByAGkAZwBlACAAZQBsACAAZgB1AG4AYwBpAG8AbgBhAG0AaQBlAG4A -dABvACAAZABlACAAbABhACAAcAByAGUAcwBlAG4AdABlACAAQQB1AHQAbwByAGkAZABhAGQAIABk -AGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAcwBlACAAZQBuAGMAdQBlAG4AdAByAGEA -IABlAG4AIABsAGEAIABkAGkAcgBlAGMAYwBpAPMAbgAgAHcAZQBiACAAaAB0AHQAcAA6AC8ALwB3 -AHcAdwAuAHAAawBpAC4AZwB2AGEALgBlAHMALwBjAHAAczAlBggrBgEFBQcCARYZaHR0cDovL3d3 -dy5wa2kuZ3ZhLmVzL2NwczAdBgNVHQ4EFgQUezXTQNIceBlm73QQKNw+T7J4BPwwgZUGA1UdIwSB -jTCBioAUezXTQNIceBlm73QQKNw+T7J4BPyhbKRqMGgxCzAJBgNVBAYTAkVTMR8wHQYDVQQKExZH -ZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExJzAlBgNVBAMTHlJvb3QgQ0Eg -R2VuZXJhbGl0YXQgVmFsZW5jaWFuYYIEO0XlaDANBgkqhkiG9w0BAQUFAAOCAQEAJGFO9bXIQgIq -s1x1rcVtyueUP6VolYjBVMAQaaISLxg/JVCofErqxgnZ9HXGQNqvUJ09pRa7bTHGx3MKSP4gcu1v -zOiDYRZGkAGVS32OmlIJL/ZvHOShcc+MKloXc4NHTQ82+wRNSVHiFMlkYfvUFOD0nrc0jwomvZdc -9Hk6SjAZzK1PoJiKtDGXKuJzbX54uPiIiU+xIpFkS/VQ3gPb5cV25xNmdX5l+wGfk4eInflGV3xN -YK+YcxMjpCCRgfrQYWa4fdGv1m8ebD3pEf2p+YIihpkzcVrqGVc9kc2pwKNuBxOmye34aKOew1py -CYco0cRzxHMYX1B1FjGft+h8wzGCArowggK2AgEBMHAwaDELMAkGA1UEBhMCRVMxHzAdBgNVBAoT -FkdlbmVyYWxpdGF0IFZhbGVuY2lhbmExDzANBgNVBAsTBlBLSUdWQTEnMCUGA1UEAxMeUm9vdCBD -QSBHZW5lcmFsaXRhdCBWYWxlbmNpYW5hAgRFYzz2MAkGBSsOAwIaBQCgggEfMBoGCSqGSIb3DQEJ -AzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMTIxMDEzMTMyNjI4WjAjBgkqhkiG9w0B -CQQxFgQUWM7qRsKRkNVKkFVQyExAB7keGRQwgb0GCyqGSIb3DQEJEAIMMYGtMIGqMIGnMBYEFBoc -doV/v0QImB514nMBThkR9apzMIGMBBSgc+XFvUNhDYZMIRMKhVhXzJzqRjB0MGykajBoMQswCQYD -VQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZB -MScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmECBDtF5WgwDQYJKoZIhvcN -AQEBBQAEggEAYIF08VpzyEDLrvOcbTuby+z91SnMANLBW4Xf3XSEoJC5snPTyWeQeKTJpmp2A16+ -Ns1fcLqVm0Z4eCjx7VZ+t5nNsy7NdRo2G0S8EUTaYWE9S2Z+fWWhpQhEKtmpAbCsJoIk36rQIMo7 -dsST8VmMv+lghiTU+bthB1FSrXTLwmz69RMsa6P5J0CZH6yeF55bJQ80LQduis8E+5iasqkJeKSa -kFj8WXPKS7v0nLk3jDA5npAkvQO2QknkV9XkYcZGFUeFmk3sNisZSjOlYRXNowOEIQ5end2qeqR1 -l47EpaH8v54guy3ZmKbWjXerFiiO/IEBW+7mFhbavS3aGqPO+AAAAAA=6F5s4TtqSCTt+/C6kxSWxGiyQ/2uBQ/tQ2IFjNjna3U=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T12:01:00.000Z1MIAGCSqGSIb3DQEHAqCAMIIPNgIBAzELMAkGBSsOAwIaBQAwgcUGCyqGSIb3DQEJEAEEoIG1BIGy -MIGvAgEBBgwrBgEEAb9VAwIBAQAwITAJBgUrDgMCGgUABBRCG+JPtKN6FA2EoOFh/o2rbJ64ggIE -QUv+xRgPMjAxNDA1MTEyMTA4MThaAQH/AgYBRe0ddtegV6RVMFMxCzAJBgNVBAYTAkVTMR8wHQYD -VQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExEjAQBgNVBAMTCVRT -QTEgQUNDVqCCC5wwggUJMIID8aADAgECAgRFYzz2MA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT -AkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExJzAl -BgNVBAMTHlJvb3QgQ0EgR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTAeFw0wNjExMjExNzUyNTRaFw0x -NjExMTgxNjUyNTRaMFMxCzAJBgNVBAYTAkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNp -YW5hMQ8wDQYDVQQLEwZQS0lHVkExEjAQBgNVBAMTCVRTQTEgQUNDVjCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAKwMbrM6lluLkG5kI+8fgAEz85QfzTb3ATQxSKMRQiLYExhGHXWcDuBN -DUq3eoLcVEq1tBVNkbLTJzQc1rTaDHZhYfofKsEA8WSL+wc0uyHgYO3AeDeV1ZmC9DmPoh5PbWWb -pVu+MzWVG73lZgfcpPw+hr/S2r6WrZbNWqo95YW+I4Kyc9UGQ2C8OC4DOwcyvW8LFSX6aaW2QWaW -tCj4zPD0NzuV+DqHfnTbrbxO76RTN66vOscE7Z1NReZO+q0rI9s4+nMWGcgYl89iJrdTyjI7udqf -m3XDCurKiCjEX99qb+03Wl6PjTtmm0DiRQ76F9qQHSu47glgTA7SXVW1du8CAwEAAaOCAc4wggHK -MA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAWoGA1UdIASCAWEwggFd -MIIBWQYLKwYBBAG/VQMUAQAwggFIMIIBIAYIKwYBBQUHAgIwggESHoIBDgBTAGUAcgB2AGkAZABv -AHIAIABkAGUAIABTAGUAbABsAGEAZABvACAAZABlACAAVABpAGUAbQBwAG8AIABkAGUAIABsAGEA -IABBAHUAdABvAHIAaQB0AGEAdAAgAGQAZQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzACAAZABl -ACAAbABhACAAQwBvAG0AdQBuAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4AIABMAGEA -IABDAFAAUwAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAaAB0AHQAcAA6AC8ALwB3 -AHcAdwAuAGEAYwBjAHYALgBlAHMALwBjAHAAczAiBggrBgEFBQcCARYWaHR0cDovL3d3dy5hY2N2 -LmVzL2NwczAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5ndmEu -ZXMwDQYJKoZIhvcNAQEFBQADggEBABmIKSsrv7jfIX94OISi7g3Or2CRzyWaB0l05/u2oP5LlgdC -OrtQaZKjuIqinESwE5WVO51nQlNsUkhMPbZZOsZbo4+/otqMFpu64mfE9X8elEFQQ9D38JgXnk9H -1QNFiCEdCDFyOLcsrT4ttMpJuRecjgYW67LT0bYm5xe43/iSprnEFJt/MWeS0/6UZ+GVkWmQnXgA -HHCPKLfCoIpD9GgdvTelK/ofgntpIcX5fH8nbi4b1UhQbZFzlzQyHBF+D4bHE4HPJDXH/GUlFh6z -Ms+nxPgClOU/1GLaAdaPTNDbzToLZWo8XS+W/VnEb3ZhotH+GQHkgvqv8/R1a7HFKZ8wggaLMIIF -c6ADAgECAgQ7ReVoMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAkVTMR8wHQYDVQQKExZHZW5l -cmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lHVkExJzAlBgNVBAMTHlJvb3QgQ0EgR2Vu -ZXJhbGl0YXQgVmFsZW5jaWFuYTAeFw0wMTA3MDYxNjIyNDdaFw0yMTA3MDExNTIyNDdaMGgxCzAJ -BgNVBAYTAkVTMR8wHQYDVQQKExZHZW5lcmFsaXRhdCBWYWxlbmNpYW5hMQ8wDQYDVQQLEwZQS0lH -VkExJzAlBgNVBAMTHlJvb3QgQ0EgR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAMYqq1cRNy8iisoDdB3K7S2iC7wzUkAmR75aaaY7cjYXTOjfuLsv -duFARnRlApBSCLT/qIzB4MeJVhA5M+9otF9f2m0joYleIqNKBvAn8Fe5+OlOMncKP0Fk8+tl7nb+ -VKp9HSCu89d0wgpf9QgoUgjMVV3SD9uagaW7obPBlM1U4DJ1MZEaYrLedeLPT4nZkZAPQRu0Wkp3 -vWeD4JPnXqcM54HT9FKsU7IDx0Qm+3nlyzRgUBB7G9tr10erX3xoym6dQQMQ7muZe14lqMKr5MDz -XJzjvs4xTGQeXoCi9YN+DNbKjFWOvuC+SQcPoyRBelgdhOpYEsjht+3vk96UCDECAwEAAaOCAzsw -ggM3MDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AucGtpLmd2YS5lczAS -BgNVHRMBAf8ECDAGAQH/AgECMIICNAYDVR0gBIICKzCCAicwggIjBgorBgEEAb9VAgEAMIICEzCC -AegGCCsGAQUFBwICMIIB2h6CAdYAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkA -ZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEcAZQBuAGUAcgBhAGwAaQB0 -AGEAdAAgAFYAYQBsAGUAbgBjAGkAYQBuAGEALgANAAoATABhACAARABlAGMAbABhAHIAYQBjAGkA -8wBuACAAZABlACAAUAByAOEAYwB0AGkAYwBhAHMAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBj -AGkA8wBuACAAcQB1AGUAIAByAGkAZwBlACAAZQBsACAAZgB1AG4AYwBpAG8AbgBhAG0AaQBlAG4A -dABvACAAZABlACAAbABhACAAcAByAGUAcwBlAG4AdABlACAAQQB1AHQAbwByAGkAZABhAGQAIABk -AGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAcwBlACAAZQBuAGMAdQBlAG4AdAByAGEA -IABlAG4AIABsAGEAIABkAGkAcgBlAGMAYwBpAPMAbgAgAHcAZQBiACAAaAB0AHQAcAA6AC8ALwB3 -AHcAdwAuAHAAawBpAC4AZwB2AGEALgBlAHMALwBjAHAAczAlBggrBgEFBQcCARYZaHR0cDovL3d3 -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 -OWvN2cPLNeXHvV6ZIrC4rmK2ISpIXX4=MIIClTCCAf6gAwIBAgIBBzANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMP -VS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMT -DENBMS1DUC4wMi4wMTAeFw05OTAxMDExMjAxMDBaFw00ODAxMDExMjAxMDBaMF4xCzAJBgNVBAYT -AlVTMRgwFgYDVQQKEw9VLlMuIEdvdmVybm1lbnQxDDAKBgNVBAsTA0RvZDEQMA4GA1UECxMHVGVz -dGluZzEVMBMGA1UEAxMMQ0EyLUNQLjAyLjAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx -/mIo1Ma/IN8OR7KOjclvIwsv0JFXD/T258DruDZUuGoYiEbAc/ZN7R8OHI7dnv9pBfsvyEl7m2DV -oLZnP0eXJTHjdZxb1TwPHoSIysi9u3xWlPRg+v+GGfKLB9pL0m8SZh97SngerZI14w7vQy0kkXzi -GatSpBoXtWNmsHJNuQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAW -BgNVHSAEDzANMAsGCWCGSAFlAwEwATARBgNVHQ4ECgQIoI0mSmDmzZUwEwYDVR0jBAwwCoAINsJc -xaBqdugwDQYJKoZIhvcNAQEFBQADgYEAcfs1pH12Qwdhv4NOJO2xxgMZZo8+A9Zl9c7RxsvuoZOO -yCxoE9wT/lPdUpGoGxtIPoWQs1qXEXnAlXJCXjLCJUHIG1/E6gQUXW0Ty6Ztpc5Dz06pPTN2gt+4 -1B3JsL/Klqc4iyCaWr8sYgEPQ8nColWRmIwk9gAasPNkNhyxA3Y=MIIClTCCAf6gAwIBAgIBBjANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMP -VS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMT -DFRydXN0IEFuY2hvcjAeFw05OTAxMDExMjAxMDBaFw00ODAxMDExMjAxMDBaMF4xCzAJBgNVBAYT -AlVTMRgwFgYDVQQKEw9VLlMuIEdvdmVybm1lbnQxDDAKBgNVBAsTA0RvZDEQMA4GA1UECxMHVGVz -dGluZzEVMBMGA1UEAxMMQ0ExLUNQLjAyLjAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ -lQLtWKzklgYuzhjMiK2CzFmzODsEY/JIVNdn9T8MW4ufpGwnfIV62EUHCFeMYydKBm8Hyjbjrz1o -tINJmrGL5WSAX1/UPtHy1chgXOsFYD6nAHjZAJJGw74nUbKw5+L1wUHU8qXABaaTrRpS1UdKSq4T -CZ18NCjC4Oxcf/yDdQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAW -BgNVHSAEDzANMAsGCWCGSAFlAwEwATARBgNVHQ4ECgQINsJcxaBqdugwEwYDVR0jBAwwCoAIq5rr -+cLnVI8wDQYJKoZIhvcNAQEFBQADgYEAOQP3iUX7FtJlL9nvu4F+8o/N5vr+OB28OsbYtW+Q1FzE -fjkUGtT9RiteradpN/xUnS/oj3BfqFtNANkYKrBeqRtm2VeOC3kdCVFnWFME2aoRAQZbWvOwCFc3 -yLA7JBdENtDNI54yYHMHPA4/2CuNQq1Iu1ektAS95DIe7ddxL18=MIICbDCCAdWgAwIBAgIDAYafMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMRgwFgYDVQQK -Ew9VLlMuIEdvdmVybm1lbnQxDDAKBgNVBAsTA0RvRDEQMA4GA1UECxMHVGVzdGluZzEVMBMGA1UE -AxMMVHJ1c3QgQW5jaG9yMB4XDTk5MDEwMTEyMDEwMFoXDTQ4MDEwMTEyMDEwMFowXjELMAkGA1UE -BhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEMMAoGA1UECxMDRG9EMRAwDgYDVQQLEwdU -ZXN0aW5nMRUwEwYDVQQDEwxUcnVzdCBBbmNob3IwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB -ANPzucEztz+nJ/ZBHVyceZ2q0pUQt4TO2qPlWAw+TotWvz6qIS1QE/7zGS56yxHP89O4X1efnZeA -rx2VVxLfNNS9865N53ymINQETtpjYT49Ko03z8U8yfn68DlIBHi9sN31JEYzoUafF58Eu883lAwT -Q6qQrJF4HbrzGIQqgitHAgMBAAGjODA2MBEGA1UdDgQKBAirmuv5wudUjzAMBgNVHRMEBTADAQH/ -MBMGA1UdIwQMMAqACKua6/nC51SPMA0GCSqGSIb3DQEBBQUAA4GBABZWD2Gsh4tP62QSG8OFWUpo -4TulIcFZLpGsaP4T/2Nt7lXUoIJMN7wWjqkmYf5/Rvo4HxNcimq3EkeYcrm1VoDueJUYGvRjcCY5 -mxkghI27Yl/fLKE9/BvQOrvYzBs2EqKrrT7m4VK0dRMR7CeVpmPP08z0Tti6uK2tzBplp1pFMIIBbzCB2QIBATANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5TLiBH -b3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMTDFRydXN0 -IEFuY2hvchcNOTkwMTAxMTIwMTAwWhcNNDgwMTAxMTIwMTAwWjAiMCACAScXDTk5MDEwMTEyMDAw -MFowDDAKBgNVHRUEAwoBAaAjMCEwCgYDVR0UBAMCAQEwEwYDVR0jBAwwCoAIq5rr+cLnVI8wDQYJ -KoZIhvcNAQEFBQADgYEAC7lqZwejJRW7QvzH11/7cYcL3racgMxH3PSU/ufvyLk7ahR++RtHary/ -WeCvRdyznLiIOA8ZBiguWtVPqsNysNn7WLofQIVa+/TD3T+lece4e1NwGQvj5Q+e2wRtGXg+gCuT -jTKUFfKRnWz7O7RyiJKKim0jtAF4RkCpLebNChY=MIIBSzCBtQIBATANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5TLiBH -b3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMTDENBMi1D -UC4wMi4wMRcNOTkwMTAxMTIwMTAwWhcNNDgwMTAxMTIwMTAwWqAjMCEwCgYDVR0UBAMCAQEwEwYD -VR0jBAwwCoAIoI0mSmDmzZUwDQYJKoZIhvcNAQEFBQADgYEAhAHPQxpcrTTN0GXeOwoMXuQUoHMv -ezEpM0BYOVLzI3KbRXWa9iWZINr99cRQvonMtOGkhIH3iSwSNbsjmF9HX5UvNzrofOWataVP+mac -pCuNlK0NS3xxJjKRWOB9C1Ib7tiSSrQqIPcchlF6vofy2ALEL6Usa1UTVYMhzGYnVZU=MIIBSzCBtQIBATANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5TLiBH -b3Zlcm5tZW50MQwwCgYDVQQLEwNEb2QxEDAOBgNVBAsTB1Rlc3RpbmcxFTATBgNVBAMTDENBMS1D -UC4wMi4wMRcNOTkwMTAxMTIwMTAwWhcNNDgwMTAxMTIwMTAwWqAjMCEwCgYDVR0UBAMCAQEwEwYD -VR0jBAwwCoAINsJcxaBqdugwDQYJKoZIhvcNAQEFBQADgYEAlBaVVfrZqvyRhGXNYFik169nBHiN -fKpw8k1YgFAQeNYdmfScq1KHmKzDhsx9kQteczBL7ltviKTN3CKlZW82c16mfd4yYx0l5tkU80lw -KCHSUzx92+qrvYjSMup+bqSsi8JhqByBf6b0JbKfyx53Vpw1OCzjxrVHcfHPx8Q/vR4= +2017-02-06T15:23:21.057-02:00yScwzjo9znhFNIxY7fl2gnK1UhFTFcjXJE8aGBgfzfk=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=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CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US7YvyeUickj8O9yN2JXM991ULBr5v7aQnC0Dl5yNm6HnM=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US63FPfkaoV1oQ0qvdahhmupFWYoIMIw0Aekh0eJz5BpH0=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US99999BxdjnMvJhUPD983q3SAF8rwz+xj9kig5cqMbuQlF3Y4=CN=CA2-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001i1mVIJL63tU9nnIhbS1uxiOHYSrEtMsehOKLK2hDD8U=CN=Trust Anchor,OU=Testing,OU=DoD,O=U.S. Government,C=US1999-01-01T10:01:00.000-02:001v4dIw4odnHNqHyAoHbc1l3C17kCF/F3Z2ii9v8n2TYA=CN=CA1-CP.02.01,OU=Testing,OU=Dod,O=U.S. Government,C=US1999-01-01T10:01:00.000-02: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 \ No newline at end of file diff --git a/time contraints on CRLs timestamps and certs.txt b/time contraints on CRLs timestamps and certs.txt new file mode 100644 index 00000000..37622c64 --- /dev/null +++ b/time contraints on CRLs timestamps and certs.txt @@ -0,0 +1,51 @@ +# +# specific to XAdES-C, XAdES-X and forms that are based on them +# + +# certificate should be valid before usage +CompleteCertificateRefs.notBeforeDate < min(SignatureTimeStamp, SigAndRefsTimeStamp, ArchiveTimeStamp) +# for signature to be valid certificate has to be used before its revocation +min(SignatureTimeStamp, SigAndRefsTimeStamp, ArchiveTimeStamp) < CompleteCertificateRefs.revocationDate +# for signature to be valid certificate has to be used before end of its validity +min(SignatureTimeStamp, SigAndRefsTimeStamp, ArchiveTimeStamp) < CompleteCertificateRefs.notAfterDate +# CRLs for signing certificate should be published after signing took place +min(SignatureTimeStamp, SigAndRefsTimeStamp, ArchiveTimeStamp) + gracePeriod "should be" < CompleteRevocationRefs.thisUpdate +# CRL has to be published before certificate validity end +CompleteRevocationRefs.thisUpdate < CompleteCertificateRefs.notAfterDate +# CRL must be valid for the time it was used +min(SignatureTimeStamp, SigAndRefsTimeStamp, ArchiveTimeStamp) < CompleteRevocationRefs.nextUpdate +# SignatureTimeStamp TSA certificate must be used before its validity end +min(SigAndRefsTimeStamp, ArchiveTimeStamp) < AttributeCertificateRefs.notBeforeDate +# SignatureTimeStamp TSA certificate must be used before its revocation +min(SigAndRefsTimeStamp, ArchiveTimeStamp) < AttributeCertificateRefs.revocationDate +# SignatureTimeStamp TSA must be used before its validity end +AttributeCertificateRefs.notAfterDate < min(SigAndRefsTimeStamp, ArchiveTimeStamp) +# CRLs for TSA cert should be published after SignatureTimeStamp creation took place +min(SigAndRefsTimeStamp, ArchiveTimeStamp) + gracePeriod "should be " < AttributeRevocationRefs.thisUpdate +# CRLs for TSA cert should be valid at the time the checking takes place +min(SigAndRefsTimeStamp, ArchiveTimeStamp) < AttributeRevocationRefs.nextUpdate +# signing certificate can be used after its validity starts +CompleteCertificateRefs.notBeforeDate < SigAndRefsTimeStamp +# signature CRLs have to be published before second time stamping +CompleteRevocationRefs.thisUpdate < SigAndRefsTimeStamp + +# +# specific to XAdES-X-L and XAdES-A +# + +# signature certificate can be used only after its validity starts +CertificateValues.notBeforeDate < ArchiveTimeStamp +# signature CRLs must be created before archive time stamp +RevocationValues.thisUpdate < ArchiveTimeStamp +# SignatureTimeStamp and SigAndRefsTimeStamp TSA can be used only after their validity starts +AttrAuthoritiesCertValues.notBeforeDate < ArchiveTimeStamp +# SignatureTimeStamp or SigAndRefsTimeStamp TSA can be used only if they are still valid +ArchiveTimeStamp < AttrAuthoritiesCertValues.notAfterDate +# CRLs for Signature should be published after SignatureTimeStamp or SigAndRefsTimeStamp +min(SignatureTimeStamp, SigAndRefsTimeStamp) + gracePeriod "should be" < RevocationValues.thisUpdate + +# +# all forms (any property may be omitted) +# +# time stamps should be monotonic +SignatureTimeStamp < SigAndRefsTimeStamp < ArchiveTimeStamp