xades4j.verification.SignatureUtils 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 java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.content.X509Data;
import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
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.transforms.Transforms;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import xades4j.XAdES4jXMLSigException;
import xades4j.algorithms.GenericAlgorithm;
import xades4j.properties.QualifyingProperty;
import xades4j.providers.CertificateValidationException;
import xades4j.utils.DOMHelper;
/**
*
* @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);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy