xades4j.verification.XadesVerifierImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xades4j Show documentation
Show all versions of xades4j Show documentation
The XAdES4j library is an high-level, configurable and extensible Java implementation of XML Advanced
Electronic Signatures (XAdES 1.3.2 and 1.4.1). It enables producing, verifying and extending signatures in the
main XAdES forms: XAdES-BES, XAdES-EPES, XAdES-T and XAdES-C. Also, extended forms are supported through the
enrichment of an existing signature.
/*
* 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 javax.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.ResourceResolverSpi;
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.CertRef;
import xades4j.properties.data.PropertyDataObject;
import xades4j.properties.UnsignedProperties;
import xades4j.production.XadesSignatureFormatExtender;
import xades4j.properties.SignatureTimeStampProperty;
import xades4j.properties.data.SignatureTimeStampData;
import xades4j.properties.data.SigningCertificateData;
import xades4j.providers.CertificateValidationProvider;
import xades4j.providers.ValidationData;
import xades4j.providers.X500NameStyleProvider;
import xades4j.utils.CollectionUtils;
import xades4j.utils.ObjectUtils;
import xades4j.utils.PropertiesUtils;
import xades4j.utils.ResolverAnonymous;
import xades4j.verification.KeyInfoProcessor.KeyInfoRes;
import xades4j.verification.RawSignatureVerifier.RawSignatureVerifierContext;
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;
private final X500NameStyleProvider x500NameStyleProvider;
private boolean secureValidation;
@Inject
protected XadesVerifierImpl(
CertificateValidationProvider certificateValidator,
QualifyingPropertiesVerifier qualifyingPropertiesVerifier,
QualifyingPropertiesUnmarshaller qualifPropsUnmarshaller,
Set rawSigVerifiers,
Set customSigVerifiers,
X500NameStyleProvider x500NameStyleProvider)
{
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;
this.x500NameStyleProvider = x500NameStyleProvider;
this.secureValidation = false;
}
void setAcceptUnknownProperties(boolean accept)
{
this.qualifPropsUnmarshaller.setAcceptUnknownProperties(accept);
}
void setSecureValidation(boolean secureValidation)
{
this.secureValidation = secureValidation;
}
@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(), this.secureValidation);
} 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 */
Date validationDate = getValidationDate(qualifPropsData, signature, verificationOptions);
CertRef signingCertRefAttempt = tryGetSigningCertificateRef(qualifPropsData);
KeyInfoRes keyInfoRes = KeyInfoProcessor.process(signature.getKeyInfo(), signingCertRefAttempt, this.x500NameStyleProvider);
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);
if (verificationOptions.checkKeyUsage())
{
// Check key usage.
// - KeyUsage[0] = digitalSignature
// - KeyUsage[1] = nonRepudiation
boolean[] keyUsage = validationCert.getKeyUsage();
if (keyUsage != null && !keyUsage[0] && !keyUsage[1])
{
throw new SigningCertificateKeyUsageException();
}
}
/* 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,
this.x500NameStyleProvider),
/**/
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 CertRef tryGetSigningCertificateRef(Collection qualifPropsData){
// If the SigningCertificate property contains a single reference, it likely represents the signer's certificate.
// In that case, it can be used as a fallback to identify the signing certificate when processing KeyInfo.
// This is a temporary solution.
// - Properties should probably be verified in two stages (before and after cert path creation).
List certData = CollectionUtils.filterByType(qualifPropsData, SigningCertificateData.class);
if (certData.size() == 1)
{
Collection certRefs = certData.get(0).getCertRefs();
if (certRefs.size() == 1)
{
return certRefs.iterator().next();
}
}
return null;
}
private Date getValidationDate(
Collection qualifPropsData,
XMLSignature signature,
SignatureSpecificVerificationOptions verificationOptions) throws XAdES4jException
{
List sigTsData = CollectionUtils.filterByType(qualifPropsData, SignatureTimeStampData.class);
// If no signature time-stamp is present, use the current date.
if (sigTsData.isEmpty())
{
return verificationOptions.getDefaultVerificationDate();
}
// 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,
this.x500NameStyleProvider),
/**/
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 (ResourceResolverSpi resolver : resolvers)
{
signature.addResourceResolver(resolver);
}
}
signature.setFollowNestedManifests(verificationOptions.isFollowManifests());
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;
}
}