xades4j.verification.XadesVerifierImpl Maven / Gradle / Ivy
/*
* XAdES4j - A Java library for generation and verification of XAdES signatures.
* Copyright (C) 2010 Luis Goncalves.
*
* XAdES4j is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or any later version.
*
* XAdES4j is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with XAdES4j. If not, see .
*/
package xades4j.verification;
import com.google.inject.Inject;
import java.io.InputStream;
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 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 org.w3c.dom.Node;
import xades4j.properties.QualifyingProperty;
import xades4j.properties.UnsignedSignatureProperty;
import xades4j.XAdES4jException;
import xades4j.XAdES4jXMLSigException;
import xades4j.properties.data.PropertyDataObject;
import xades4j.properties.UnsignedProperties;
import xades4j.production.XadesSignatureFormatExtender;
import xades4j.properties.SignatureTimeStampProperty;
import xades4j.properties.data.SignatureTimeStampData;
import xades4j.providers.CertificateValidationProvider;
import xades4j.providers.ValidationData;
import xades4j.utils.CollectionUtils;
import xades4j.utils.ObjectUtils;
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;
/**
*
* @author Luís
*/
class XadesVerifierImpl implements XadesVerifier
{
static
{
org.apache.xml.security.Init.init();
initFormExtension();
}
/**/
private final CertificateValidationProvider certificateValidator;
private final QualifyingPropertiesVerifier qualifyingPropertiesVerifier;
private final QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller;
private final Set rawSigVerifiers;
private final Set customSigVerifiers;
@Inject
protected XadesVerifierImpl(
CertificateValidationProvider certificateValidator,
QualifyingPropertiesVerifier qualifyingPropertiesVerifier,
QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller,
Set rawSigVerifiers,
Set customSigVerifiers)
{
if (ObjectUtils.anyNull(
certificateValidator, qualifyingPropertiesVerifier, qualifPropsUnmarshaller, rawSigVerifiers, customSigVerifiers))
{
throw new NullPointerException("One or more arguments are null");
}
this.certificateValidator = certificateValidator;
this.qualifyingPropertiesVerifier = qualifyingPropertiesVerifier;
this.qualifPropsUnmarshaller = qualifPropsUnmarshaller;
this.rawSigVerifiers = rawSigVerifiers;
this.customSigVerifiers = customSigVerifiers;
}
void setAcceptUnknownProperties(boolean accept)
{
this.qualifPropsUnmarshaller.setAcceptUnknownProperties(accept);
}
@Override
public XAdESVerificationResult verify(Element signatureElem, SignatureSpecificVerificationOptions verificationOptions) throws XAdES4jException
{
if (null == signatureElem)
{
throw new NullPointerException("Signature node not specified");
}
if (null == verificationOptions)
{
verificationOptions = SignatureSpecificVerificationOptions.empty;
}
/* Unmarshal the signature */
XMLSignature signature;
try
{
signature = new XMLSignature(signatureElem, verificationOptions.getBaseUri());
} catch (XMLSecurityException ex)
{
throw new UnmarshalException("Bad XML signature", ex);
}
String signatureId = signature.getId();
if (null == signatureId)
{
throw new UnmarshalException("XML signature doesn't have an Id");
}
ReferencesRes referencesRes = SignatureUtils.processReferences(signature);
/* Apply early verifiers */
RawSignatureVerifierContext rawCtx = new RawSignatureVerifierContext(signature);
for (RawSignatureVerifier rawSignatureVerifier : this.rawSigVerifiers)
{
rawSignatureVerifier.verify(rawCtx);
}
/* Get and check the QualifyingProperties element */
Element qualifyingPropsElem = SignatureUtils.getQualifyingPropertiesElement(signature);
SignatureUtils.checkSignedPropertiesIncorporation(qualifyingPropsElem, referencesRes.signedPropsReference);
// Check the QualifyingProperties 'Target' attribute.
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");
}
/* Unmarshal the qualifying properties */
QualifPropsDataCollectorImpl propsDataCollector = new QualifPropsDataCollectorImpl();
qualifPropsUnmarshaller.unmarshalProperties(qualifyingPropsElem, propsDataCollector);
Collection qualifPropsData = propsDataCollector.getPropertiesData();
/* Certification path */
KeyInfoRes keyInfoRes = SignatureUtils.processKeyInfo(signature.getKeyInfo());
Date validationDate = getValidationDate(qualifPropsData, signature);
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);
// Create the properties verification context.
QualifyingPropertyVerificationContext qPropsCtx = new QualifyingPropertyVerificationContext(
signature,
new QualifyingPropertyVerificationContext.CertificationChainData(
certValidationRes.getCerts(),
certValidationRes.getCrls(),
keyInfoRes.issuerSerial),
/**/
new QualifyingPropertyVerificationContext.SignedObjectsData(
referencesRes.dataObjsReferences,
signature));
// Verify the properties. Data structure verification is included.
Collection props = this.qualifyingPropertiesVerifier.verifyProperties(qualifPropsData, qPropsCtx);
XAdESVerificationResult res = new XAdESVerificationResult(
XAdESFormChecker.checkForm(props),
signature,
certValidationRes,
props,
referencesRes.dataObjsReferences);
// Apply the custom signature verifiers.
for (CustomSignatureVerifier customVer : this.customSigVerifiers)
{
customVer.verify(res, qPropsCtx);
}
return res;
}
/*************************************************************************************/
/**/
private Date getValidationDate(
Collection qualifPropsData,
XMLSignature signature) throws XAdES4jException
{
List sigTsData = CollectionUtils.filterByType(qualifPropsData, SignatureTimeStampData.class);
// If no signature time-stamp is present, use the current date.
if (sigTsData.isEmpty())
{
return new Date();
}
// TODO support multiple SignatureTimeStamps (section 7.3 last paragraph of Standard v.1.4.2)
// This is a temporary solution.
// - Properties should probably be verified in two stages (before and after cert path creation).
// - Had to remove the custom structure verifier that checked if the SigningCertificate data was present.
QualifyingPropertyVerificationContext ctx = new QualifyingPropertyVerificationContext(
signature,
new QualifyingPropertyVerificationContext.CertificationChainData(
new ArrayList(0),
new ArrayList(0),
null),
/**/
new QualifyingPropertyVerificationContext.SignedObjectsData(
new ArrayList(0),
signature));
Collection props = this.qualifyingPropertiesVerifier.verifyProperties(sigTsData, ctx);
QualifyingProperty sigTs = props.iterator().next().getProperty();
return ((SignatureTimeStampProperty) sigTs).getTime();
}
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 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;
// BES/EPES -> C
FormExtensionPropsCollector cAndTPropsCol = new FormExtensionPropsCollector()
{
@Override
public void addProps(
Collection usp,
XAdESVerificationResult res)
{
PropertiesUtils.addXadesCProperties(usp, res.getValidationData());
PropertiesUtils.addXadesTProperties(usp);
}
};
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;
// C -> X-L
FormExtensionPropsCollector xlAndXPropsCol = new FormExtensionPropsCollector()
{
@Override
public void addProps(
Collection usp,
XAdESVerificationResult res)
{
PropertiesUtils.addXadesXLProperties(usp, res.getValidationData());
PropertiesUtils.addXadesXProperties(usp);
}
};
formsExtensionTransitions[XAdESForm.C.ordinal()][XAdESForm.X_L.ordinal()] = xlAndXPropsCol;
}
@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.X_L))
{
throw new IllegalArgumentException("Signature format can only be extended to XAdES-T, C, X or X-L");
}
XAdESVerificationResult res = this.verify(signatureElem, verificationOptions);
XAdESForm actualForm = res.getSignatureForm();
if (actualForm.before(finalForm))
{
// Valid form transitions:
// * BES/EPES -> T
// * BES/EPES -> C
// * T -> C
// * C -> X
// * C -> X-L
// * X -> X-L (not supported with the library defaults: X cannot be verified)
// * X-L -> A (not supported with the library defaults: X-L cannot be verified)
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy