Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* DSS - Digital Signature Services
* Copyright (C) 2015 European Commission, provided under the CEF programme
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package eu.europa.esig.dss.xades.validation;
import static eu.europa.esig.dss.xades.XPathQueryHolder.XMLE_ALGORITHM;
import static eu.europa.esig.dss.xades.XPathQueryHolder.XMLE_REFS_ONLY_TIME_STAMP;
import static eu.europa.esig.dss.xades.XPathQueryHolder.XMLE_SIGNATURE_TIME_STAMP;
import static eu.europa.esig.dss.xades.XPathQueryHolder.XMLE_SIG_AND_REFS_TIME_STAMP;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
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 javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.transform.stream.StreamSource;
import org.apache.xml.security.Init;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.keyresolver.KeyResolverException;
import org.apache.xml.security.signature.Reference;
import org.apache.xml.security.signature.ReferenceNotInitializedException;
import org.apache.xml.security.signature.SignedInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.GeneralName;
import org.digidoc4j.dss.xades.BDocTmSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSNotETSICompliantException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.DomUtils;
import eu.europa.esig.dss.EncryptionAlgorithm;
import eu.europa.esig.dss.SignatureAlgorithm;
import eu.europa.esig.dss.SignatureForm;
import eu.europa.esig.dss.SignatureLevel;
import eu.europa.esig.dss.TokenIdentifier;
import eu.europa.esig.dss.XAdESNamespaces;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.AdvancedSignature;
import eu.europa.esig.dss.validation.CRLRef;
import eu.europa.esig.dss.validation.CandidatesForSigningCertificate;
import eu.europa.esig.dss.validation.CertificateRef;
import eu.europa.esig.dss.validation.CertificateValidity;
import eu.europa.esig.dss.validation.CertifiedRole;
import eu.europa.esig.dss.validation.CommitmentType;
import eu.europa.esig.dss.validation.DefaultAdvancedSignature;
import eu.europa.esig.dss.validation.OCSPRef;
import eu.europa.esig.dss.validation.SignatureCryptographicVerification;
import eu.europa.esig.dss.validation.SignaturePolicyProvider;
import eu.europa.esig.dss.validation.SignatureProductionPlace;
import eu.europa.esig.dss.validation.TimestampInclude;
import eu.europa.esig.dss.validation.TimestampReference;
import eu.europa.esig.dss.validation.TimestampReferenceCategory;
import eu.europa.esig.dss.validation.TimestampToken;
import eu.europa.esig.dss.x509.ArchiveTimestampType;
import eu.europa.esig.dss.x509.CertificatePool;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.SignaturePolicy;
import eu.europa.esig.dss.x509.TimestampType;
import eu.europa.esig.dss.x509.crl.OfflineCRLSource;
import eu.europa.esig.dss.x509.ocsp.OfflineOCSPSource;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.XPathQueryHolder;
/**
* Parse an XAdES signature structure. Note that for each signature to be validated a new instance of this object must
* be created.
*
*/
public class XAdESSignature extends DefaultAdvancedSignature {
private static final Logger LOG = LoggerFactory.getLogger(XAdESSignature.class);
/**
* This array contains all the XAdES signatures levels TODO: do not return redundant levels.
*/
private static SignatureLevel[] signatureLevels = new SignatureLevel[] { SignatureLevel.XML_NOT_ETSI, SignatureLevel.XAdES_BASELINE_B,
SignatureLevel.XAdES_BASELINE_T, SignatureLevel.XAdES_C, SignatureLevel.XAdES_X, SignatureLevel.XAdES_BASELINE_LT,
SignatureLevel.XAdES_BASELINE_LTA };
/**
* This variable contains the list of {@code XPathQueryHolder} adapted to the specific signature schema.
*/
private final List xPathQueryHolders;
/**
* This variable contains the XPathQueryHolder adapted to the signature schema.
*/
protected XPathQueryHolder xPathQueryHolder;
public static final String DEFAULT_TIMESTAMP_VALIDATION_CANONICALIZATION_METHOD = CanonicalizationMethod.INCLUSIVE;
private final Element signatureElement;
/**
* Indicates the id of the signature. If not existing this attribute is auto calculated.
*/
private String signatureId;
private XAdESCertificateSource certificatesSource;
/**
* This variable contains all references found within the signature. They are extracted when the method
* {@code checkSignatureIntegrity} is called.
*/
private transient List references = new ArrayList();
/**
* Cached list of the Signing Certificate Timestamp References.
*/
private List signingCertificateTimestampReferences;
static {
Init.init();
/**
* Adds the support of ECDSA_RIPEMD160 for XML signature. Used by AT. The BC provider must be previously added.
*/
// final JCEMapper.Algorithm algorithm = new JCEMapper.Algorithm("",
// SignatureAlgorithm.ECDSA_RIPEMD160.getJCEId(), "Signature");
// final String xmlId = SignatureAlgorithm.ECDSA_RIPEMD160.getXMLId();
// JCEMapper.register(xmlId, algorithm);
// try {
// org.apache.xml.security.algorithms.SignatureAlgorithm.register(xmlId,
// SignatureECDSARIPEMD160.class);
// } catch (Exception e) {
// LOG.error("ECDSA_RIPEMD160 algorithm initialisation failed.", e);
// }
/**
* Adds the support of not standard algorithm name: http://www.w3.org/2001/04/xmldsig-more/rsa-ripemd160. Used
* by some AT signature providers. The BC
* provider must be previously added.
*/
final JCEMapper.Algorithm notStandardAlgorithm = new JCEMapper.Algorithm("", SignatureAlgorithm.RSA_RIPEMD160.getJCEId(), "Signature");
JCEMapper.register(SignatureRSARIPEMD160AT.XML_ID, notStandardAlgorithm);
try {
org.apache.xml.security.algorithms.SignatureAlgorithm.register(SignatureRSARIPEMD160AT.XML_ID, SignatureRSARIPEMD160AT.class);
} catch (Exception e) {
LOG.error("ECDSA_RIPEMD160AT algorithm initialisation failed.", e);
}
}
/**
* This constructor is used when creating the signature. The default {@code XPathQueryHolder} is set.
*
* @param signatureElement
* w3c.dom element
* @param certPool
* can be null
*/
public XAdESSignature(final Element signatureElement, final CertificatePool certPool) {
this(signatureElement, new ArrayList() {
{
add(new XPathQueryHolder());
}
}, certPool);
}
/**
* The default constructor for XAdESSignature.
*
* @param signatureElement
* w3c.dom element
* @param xPathQueryHolders
* List of {@code XPathQueryHolder} to use when handling signature
* @param certPool
* can be null
*/
public XAdESSignature(final Element signatureElement, final List xPathQueryHolders, final CertificatePool certPool) {
super(certPool);
if (signatureElement == null) {
throw new NullPointerException("signatureElement");
}
this.signatureElement = signatureElement;
this.xPathQueryHolders = xPathQueryHolders;
initialiseSettings();
}
/**
* This method is called when creating a new instance of the {@code XAdESSignature} with unknown schema.
*/
private void initialiseSettings() {
recursiveNamespaceBrowser(signatureElement);
if (xPathQueryHolder == null) {
LOG.warn("There is no suitable XPathQueryHolder to manage the signature. The default one will be used.");
xPathQueryHolder = new XPathQueryHolder();
}
}
/**
* This method sets the namespace which will determinate the {@code XPathQueryHolder} to use. The content of the
* Transform element is ignored.
*
* @param element
*/
public void recursiveNamespaceBrowser(final Element element) {
for (int ii = 0; ii < element.getChildNodes().getLength(); ii++) {
final Node node = element.getChildNodes().item(ii);
if (node.getNodeType() == Node.ELEMENT_NODE) {
final Element childElement = (Element) node;
final String namespaceURI = childElement.getNamespaceURI();
final String localName = childElement.getLocalName();
if (XPathQueryHolder.XMLE_TRANSFORM.equals(localName) && javax.xml.crypto.dsig.XMLSignature.XMLNS.equals(namespaceURI)) {
continue;
} else if (XPathQueryHolder.XMLE_QUALIFYING_PROPERTIES.equals(localName)) {
setXPathQueryHolder(namespaceURI);
return;
}
recursiveNamespaceBrowser(childElement);
}
}
}
private void setXPathQueryHolder(final String namespaceURI) {
for (final XPathQueryHolder xPathQueryHolder : xPathQueryHolders) {
final boolean canUseThisXPathQueryHolder = xPathQueryHolder.canUseThisXPathQueryHolder(namespaceURI);
if (canUseThisXPathQueryHolder) {
this.xPathQueryHolder = xPathQueryHolder;
}
}
}
/**
* This getter returns the {@code XPathQueryHolder}
*
* @return
*/
public XPathQueryHolder getXPathQueryHolder() {
return xPathQueryHolder;
}
/**
* This method returns the certificate pool used by this instance to handle encapsulated certificates.
*
* @return
*/
public CertificatePool getCertPool() {
return certPool;
}
/**
* Returns the w3c.dom encapsulated signature element.
*
* @return the signatureElement
*/
public Element getSignatureElement() {
return signatureElement;
}
@Override
public SignatureForm getSignatureForm() {
return SignatureForm.XAdES;
}
@Override
public EncryptionAlgorithm getEncryptionAlgorithm() {
final String xmlName = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_METHOD).getAttribute(XMLE_ALGORITHM);
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.forXML(xmlName, null);
if (signatureAlgorithm == null) {
return null;
}
return signatureAlgorithm.getEncryptionAlgorithm();
}
@Override
public DigestAlgorithm getDigestAlgorithm() {
final String xmlName = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_METHOD).getAttribute(XMLE_ALGORITHM);
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.forXML(xmlName, null);
if (signatureAlgorithm == null) {
return null;
}
return signatureAlgorithm.getDigestAlgorithm();
}
@Override
public XAdESCertificateSource getCertificateSource() {
if (certificatesSource == null) {
certificatesSource = new XAdESCertificateSource(signatureElement, xPathQueryHolder, certPool);
}
return certificatesSource;
}
/**
* This method resets the source of certificates. It must be called when any certificate is added to the KeyInfo or
* CertificateValues.
*/
public void resetCertificateSource() {
certificatesSource = null;
}
@Override
public OfflineCRLSource getCRLSource() {
if (offlineCRLSource == null) {
offlineCRLSource = new XAdESCRLSource(signatureElement, xPathQueryHolder);
}
return offlineCRLSource;
}
@Override
public OfflineOCSPSource getOCSPSource() {
if (offlineOCSPSource == null) {
offlineOCSPSource = new XAdESOCSPSource(signatureElement, xPathQueryHolder);
}
return offlineOCSPSource;
}
/**
* This method resets the sources of the revocation data. It must be called when -LT level is created.
*/
public void resetRevocationSources() {
offlineCRLSource = null;
offlineOCSPSource = null;
}
@Override
public CandidatesForSigningCertificate getCandidatesForSigningCertificate() {
if (candidatesForSigningCertificate != null) {
return candidatesForSigningCertificate;
}
candidatesForSigningCertificate = new CandidatesForSigningCertificate();
/**
* 5.1.4.1 XAdES processing
* Candidates for the signing certificate extracted from ds:KeyInfo element shall be checked against all
* references present in the
* ds:SigningCertificate property, if present, since one of these references shall be a reference to the signing
* certificate.
*/
final XAdESCertificateSource certSource = getCertificateSource();
for (final CertificateToken certificateToken : certSource.getKeyInfoCertificates()) {
final CertificateValidity certificateValidity = new CertificateValidity(certificateToken);
candidatesForSigningCertificate.add(certificateValidity);
}
return candidatesForSigningCertificate;
}
@Override
public void checkSigningCertificate() {
final CandidatesForSigningCertificate candidates = getCandidatesForSigningCertificate();
/**
* The ../SignedProperties/SignedSignatureProperties/SigningCertificate element MAY contain references and
* digests values of other certificates (that
* MAY form a chain up to the point of trust).
*/
boolean isEn319132 = false;
NodeList list = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_SIGNING_CERTIFICATE_CERT);
int length = list.getLength();
if (length == 0) {
list = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_SIGNING_CERTIFICATE_CERT_V2);
length = list.getLength();
isEn319132 = true;
}
if (length == 0) {
final CertificateValidity theCertificateValidity = candidates.getTheCertificateValidity();
final CertificateToken certificateToken = theCertificateValidity == null ? null : theCertificateValidity.getCertificateToken();
// The check need to be done at the level of KeyInfo
for (final Reference reference : references) {
final String uri = reference.getURI();
if (!uri.startsWith("#")) {
continue;
}
final String id = uri.substring(1);
final Element element = signatureElement.getOwnerDocument().getElementById(id);
// final Element element =
// DomUtils.getElement(signatureElement, "");
if (!hasSignatureAsParent(element)) {
continue;
}
if ((certificateToken != null) && id.equals(certificateToken.getXmlId())) {
theCertificateValidity.setSigned(element.getNodeName());
return;
}
}
}
// This Map contains the list of the references to the certificate which
// were already checked and which correspond to a certificate.
Map alreadyProcessedElements = new HashMap();
final List certificateValidityList = candidates.getCertificateValidityList();
for (final CertificateValidity certificateValidity : certificateValidityList) {
final CertificateToken certificateToken = certificateValidity.getCertificateToken();
for (int ii = 0; ii < length; ii++) {
certificateValidity.setAttributePresent(true);
final Element element = (Element) list.item(ii);
if (alreadyProcessedElements.containsKey(element)) {
continue;
}
final Element certDigestElement = DomUtils.getElement(element, xPathQueryHolder.XPATH__CERT_DIGEST);
certificateValidity.setDigestPresent(certDigestElement != null);
final Element digestMethodElement = DomUtils.getElement(certDigestElement, xPathQueryHolder.XPATH__DIGEST_METHOD);
if (digestMethodElement == null) {
continue;
}
final String xmlAlgorithmName = digestMethodElement.getAttribute(XMLE_ALGORITHM);
// The default algorithm is used in case of bad encoded
// algorithm name
final DigestAlgorithm digestAlgorithm = DigestAlgorithm.forXML(xmlAlgorithmName, DigestAlgorithm.SHA1);
final Element digestValueElement = DomUtils.getElement(element, xPathQueryHolder.XPATH__CERT_DIGEST_DIGEST_VALUE);
if (digestValueElement == null) {
continue;
}
// That must be a binary comparison
final byte[] storedBase64DigestValue = Utils.fromBase64(digestValueElement.getTextContent());
/**
* Step 1:
* Take the first child of the property and check that the content of ds:DigestValue matches the result
* of digesting the candidate for
* the signing certificate with the algorithm indicated in ds:DigestMethod. If they do not match, take
* the next child and repeat this step until
* a matching child element has been found or all children of the element have been checked. If they do
* match, continue with step 2. If the last
* element is reached without finding any match, the validation of this property shall be taken as
* failed and INVALID/FORMAT_FAILURE is
* returned.
*/
final byte[] digest = certificateToken.getDigest(digestAlgorithm);
certificateValidity.setDigestEqual(false);
BigInteger serialNumber = new BigInteger("0");
if (Arrays.equals(digest, storedBase64DigestValue)) {
X500Principal issuerName = null;
if (isEn319132) {
final Element issuerNameEl = DomUtils.getElement(element, xPathQueryHolder.XPATH__X509_ISSUER_V2);
if (issuerNameEl != null) {
final String textContent = issuerNameEl.getTextContent();
ASN1InputStream is = null;
GeneralName name = null;
ASN1Integer serial = null;
try {
is = new ASN1InputStream(Utils.fromBase64(textContent));
ASN1Sequence seq = (ASN1Sequence) is.readObject();
ASN1Sequence obj = (ASN1Sequence) seq.getObjectAt(0);
name = GeneralName.getInstance(obj.getObjectAt(0));
serial = (ASN1Integer) seq.getObjectAt(1);
} catch (IOException e) {
LOG.error("Unable to decode textContent " + textContent + " : " + e.getMessage(), e);
} finally {
Utils.closeQuietly(is);
}
try {
issuerName = new X500Principal(name.getName().toASN1Primitive().getEncoded());
} catch (Exception e) {
LOG.error("Unable to decode X500Principal : " + e.getMessage(), e);
}
try {
serialNumber = serial.getValue();
} catch (Exception e) {
LOG.error("Unable to decode serialNumber : " + e.getMessage(), e);
}
}
} else {
final Element issuerNameEl = DomUtils.getElement(element, xPathQueryHolder.XPATH__X509_ISSUER_NAME);
// This can be allayed when the distinguished name is not
// correctly encoded
// final String textContent =
// DSSUtils.unescapeMultiByteUtf8Literals(issuerNameEl.getTextContent());
final String textContent = issuerNameEl.getTextContent();
issuerName = DSSUtils.getX500PrincipalOrNull(textContent);
final Element serialNumberEl = DomUtils.getElement(element, xPathQueryHolder.XPATH__X509_SERIAL_NUMBER);
final String serialNumberText = serialNumberEl.getTextContent();
// serial number can contain leading and trailing whitespace.
serialNumber = new BigInteger(serialNumberText.trim());
}
final X500Principal candidateIssuerName = certificateToken.getIssuerX500Principal();
final boolean issuerNameMatches = DSSUtils.x500PrincipalAreEquals(candidateIssuerName, issuerName);
if (!issuerNameMatches) {
final String c14nCandidateIssuerName = candidateIssuerName.getName(X500Principal.CANONICAL);
LOG.info("candidateIssuerName: " + c14nCandidateIssuerName);
final String c14nIssuerName = issuerName == null ? "" : issuerName.getName(X500Principal.CANONICAL);
LOG.info("issuerName : " + c14nIssuerName);
}
final BigInteger candidateSerialNumber = certificateToken.getSerialNumber();
final boolean serialNumberMatches = candidateSerialNumber.equals(serialNumber);
certificateValidity.setDigestEqual(true);
certificateValidity.setSerialNumberEqual(serialNumberMatches);
certificateValidity.setDistinguishedNameEqual(issuerNameMatches);
// The certificate was identified
alreadyProcessedElements.put(element, true);
// If the signing certificate is not set yet then it must be
// done now. Actually if the signature is tempered then the
// method checkSignatureIntegrity cannot set the signing
// certificate.
if (candidates.getTheCertificateValidity() == null) {
candidates.setTheCertificateValidity(certificateValidity);
}
break;
}
}
}
}
/**
* Checks if the given {@code Element} has as parent the current signature. This is the security check.
*
* @param element
* the element to be checked (can be null)
* @return true if the given element has as parent the current signature element, false otherwise
*/
private boolean hasSignatureAsParent(final Element element) {
if (element == null) {
return false;
}
Node node = element;
String nodeName = node.getNodeName();
if (XPathQueryHolder.XMLE_X509CERTIFICATE.equals(nodeName)) {
node = node.getParentNode();
if (node == null) {
return false;
}
nodeName = node.getNodeName();
}
if (XPathQueryHolder.XMLE_X509DATA.equals(nodeName)) {
node = node.getParentNode();
if (node == null) {
return false;
}
nodeName = node.getNodeName();
}
if (XPathQueryHolder.XMLE_KEYINFO.equals(nodeName)) {
node = node.getParentNode();
if (node == null) {
return false;
}
}
if (!node.equals(signatureElement)) {
return false;
}
return true;
}
@Override
public Date getSigningTime() {
final Element signingTimeEl = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_SIGNING_TIME);
if (signingTimeEl == null) {
return null;
}
final String text = signingTimeEl.getTextContent();
return DomUtils.getDate(text);
}
@Override
public void checkSignaturePolicy(SignaturePolicyProvider signaturePolicyProvider) {
final Element policyIdentifier = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_POLICY_IDENTIFIER);
if (policyIdentifier != null) {
// There is a policy
final Element policyId = DomUtils.getElement(policyIdentifier, xPathQueryHolder.XPATH__POLICY_ID);
if (policyId != null) {
// Explicit policy
String policyIdString = policyId.getTextContent();
String policyUrlString = null;
if (DSSXMLUtils.isOid(policyIdString)) {
// urn:oid:1.2.3 --> 1.2.3
policyIdString = policyIdString.substring(policyIdString.lastIndexOf(':') + 1);
} else {
policyUrlString = policyIdString;
}
signaturePolicy = new SignaturePolicy(policyIdString);
final Node policyDigestMethod = DomUtils.getNode(policyIdentifier, xPathQueryHolder.XPATH__POLICY_DIGEST_METHOD);
final String policyDigestMethodString = policyDigestMethod.getTextContent();
if (Utils.isStringNotEmpty(policyDigestMethodString)) {
final DigestAlgorithm digestAlgorithm = DigestAlgorithm.forXML(policyDigestMethodString);
signaturePolicy.setDigestAlgorithm(digestAlgorithm);
}
final Element policyDigestValue = DomUtils.getElement(policyIdentifier, xPathQueryHolder.XPATH__POLICY_DIGEST_VALUE);
final String digestValue = policyDigestValue.getTextContent().trim();
signaturePolicy.setDigestValue(digestValue);
final Element policyUrl = DomUtils.getElement(policyIdentifier, xPathQueryHolder.XPATH__POLICY_SPURI);
if (policyUrl != null) {
policyUrlString = policyUrl.getTextContent().trim();
}
signaturePolicy.setUrl(policyUrlString);
signaturePolicy.setPolicyContent(signaturePolicyProvider.getSignaturePolicy(policyIdString, policyUrlString));
} else {
// Implicit policy
final Element signaturePolicyImplied = DomUtils.getElement(policyIdentifier, xPathQueryHolder.XPATH__SIGNATURE_POLICY_IMPLIED);
if (signaturePolicyImplied != null) {
signaturePolicy = new SignaturePolicy();
}
}
}
}
@Override
public SignatureProductionPlace getSignatureProductionPlace() {
NodeList nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_PRODUCTION_PLACE);
if ((nodeList.getLength() == 0) || (nodeList.item(0) == null)) {
nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_PRODUCTION_PLACE_V2);
if ((nodeList.getLength() == 0) || (nodeList.item(0) == null)) {
return null;
}
}
final SignatureProductionPlace signatureProductionPlace = new SignatureProductionPlace();
final NodeList list = nodeList.item(0).getChildNodes();
for (int ii = 0; ii < list.getLength(); ii++) {
final Node item = list.item(ii);
final String name = item.getLocalName();
final String nodeValue = item.getTextContent();
if (XPathQueryHolder.XMLE_CITY.equals(name)) {
signatureProductionPlace.setCity(nodeValue);
} else if (XPathQueryHolder.XMLE_STATE_OR_PROVINCE.equals(name)) {
signatureProductionPlace.setStateOrProvince(nodeValue);
} else if (XPathQueryHolder.XMLE_POSTAL_CODE.equals(name)) {
signatureProductionPlace.setPostalCode(nodeValue);
} else if (XPathQueryHolder.XMLE_COUNTRY_NAME.equals(name)) {
signatureProductionPlace.setCountryName(nodeValue);
} else if (XPathQueryHolder.XMLE_STREET_ADDRESS.equals(name)) {
signatureProductionPlace.setStreetAddress(nodeValue);
}
}
return signatureProductionPlace;
}
@Override
public String[] getClaimedSignerRoles() {
NodeList nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_CLAIMED_ROLE);
if (nodeList.getLength() == 0) {
nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_CLAIMED_ROLE_V2);
if (nodeList.getLength() == 0) {
return null;
}
}
final String[] roles = new String[nodeList.getLength()];
for (int ii = 0; ii < nodeList.getLength(); ii++) {
roles[ii] = nodeList.item(ii).getTextContent();
}
return roles;
}
@Override
public List getCertifiedSignerRoles() {
/**
*
*
*
*
*
*
*
*
*
*
*
*/
NodeList nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_CERTIFIED_ROLE);
if (nodeList.getLength() == 0) {
nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_CERTIFIED_ROLE_V2);
if (nodeList.getLength() == 0) {
return null;
}
}
final List roles = new ArrayList();
for (int ii = 0; ii < nodeList.getLength(); ii++) {
final Element certEl = (Element) nodeList.item(ii);
final String textContent = certEl.getTextContent();
CertifiedRole role = new CertifiedRole();
role.setRole(textContent);
if (!roles.contains(role)) {
roles.add(role);
}
}
return roles;
}
@Override
public String getContentType() {
return "text/xml";
}
@Override
public String getContentIdentifier() {
return null;
}
@Override
public String getContentHints() {
return null;
}
/**
* This method creates {@code TimestampToken} based on provided parameters.
*
* @param timestampElement
* contains the encapsulated timestamp
* @param timestampType
* {@code TimestampType}
* @return {@code TimestampToken} of the given type
* @throws DSSException
*/
private TimestampToken makeTimestampToken(final Element timestampElement, final TimestampType timestampType) throws DSSException {
final Element timestampTokenNode = DomUtils.getElement(timestampElement, xPathQueryHolder.XPATH__ENCAPSULATED_TIMESTAMP);
if (timestampTokenNode == null) {
// TODO (09/11/2014): The error message must be propagated to the
// validation report
LOG.warn("The timestamp (" + timestampType.name() + ") cannot be extracted from the signature!");
return null;
}
TimestampToken timestampToken = null;
try {
timestampToken = new TimestampToken(Utils.fromBase64(timestampTokenNode.getTextContent()), timestampType, certPool);
} catch (Exception e) {
throw new DSSException("Unable to extract timestamp", e);
}
timestampToken.setHashCode(timestampElement.hashCode());
timestampToken.setCanonicalizationMethod(getTimestampCanonicalizationMethod(timestampElement));
// TODO: timestampToken.setIncludes(element.getIncludes)...
// final NodeList includes =
// timestampTokenNode.getElementsByTagName("Include");
// for (int i = 0; i < includes.getLength(); ++i) {
// timestampToken.getTimestampIncludes().add(new
// TimestampInclude(includes.item(i).getBaseURI(),
// includes.item(i).getAttributes()));
// }
return timestampToken;
}
public Element getSignatureValue() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_VALUE);
}
public Element getObject() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_OBJECT);
}
/**
* This method returns the list of ds:Object elements for the current signature element.
*
* @return
*/
public NodeList getObjects() {
return DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_OBJECT);
}
public Element getCompleteCertificateRefs() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_COMPLETE_CERTIFICATE_REFS);
}
public Element getCompleteRevocationRefs() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_COMPLETE_REVOCATION_REFS);
}
public NodeList getSigAndRefsTimeStamp() {
NodeList nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_SIG_AND_REFS_TIMESTAMP);
if (nodeList == null || nodeList.getLength() == 0) {
nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_SIG_AND_REFS_TIMESTAMP_V2);
}
return nodeList;
}
public Element getCertificateValues() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_CERTIFICATE_VALUES);
}
public Element getRevocationValues() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_REVOCATION_VALUES);
}
/**
* Checks the presence of ... segment in the signature, what is the proof -B profile existence
*
* @return
*/
public boolean hasBProfile() {
return DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_SIGNED_SIGNATURE_PROPERTIES);
}
/**
* Checks the presence of SignatureTimeStamp segment in the signature, what is the proof -T profile existence
*
* @return
*/
public boolean hasTProfile() {
if(BDocTmSupport.hasBDocTmPolicyId(signatureElement, xPathQueryHolder)) {
//BDoc-TM has policy id and OCSP response containing TimeMark
boolean hasOcspResponse = Utils.isStringNotBlank(DomUtils.getValue(signatureElement, xPathQueryHolder.XPATH_OCSP_VALUES_ENCAPSULATED_OCSP));
return hasOcspResponse;
}
return DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_TIMESTAMP);
}
/**
* Checks the presence of CompleteCertificateRefs & CompleteRevocationRefs segments in the signature, what is the
* proof -C profile existence
*
* @return
*/
public boolean hasCProfile() {
final boolean certRefs = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_COMPLETE_CERTIFICATE_REFS);
final boolean revocationRefs = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_COMPLETE_REVOCATION_REFS);
return certRefs || revocationRefs;
}
/**
* Checks the presence of SigAndRefsTimeStamp segment in the signature, what is the proof -X profile existence
*
* @return true if the -X extension is present
*/
public boolean hasXProfile() {
return DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_SIG_AND_REFS_TIMESTAMP);
}
/**
* Checks the presence of CertificateValues and RevocationValues segments in the signature, what is the proof -LT
* (or -XL) profile existence
*
* @return true if -LT (or -XL) extension is present
*/
public boolean hasLTProfile() {
final boolean certValues = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_CERTIFICATE_VALUES);
final boolean revocationValues = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_REVOCATION_VALUES);
boolean notEmptyCRL = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_ENCAPSULATED_CRL_VALUES);
boolean notEmptyOCSP = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_OCSP_VALUES_ENCAPSULATED_OCSP);
boolean isLTProfile = revocationValues && (notEmptyCRL || notEmptyOCSP);
if (!isLTProfile && certValues) {
isLTProfile = hasTProfile();
}
return isLTProfile;
// return certValues || (revocationValues && (notEmptyCRL || notEmptyOCSP));
}
/**
* Checks the presence of CertificateValues and RevocationValues segments in the signature, what is the proof -LTA
* (or -A) profile existence
*
* @return true if -LTA (or -A) extension is present
*/
public boolean hasLTAProfile() {
final boolean archiveTimestamp = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_ARCHIVE_TIMESTAMP);
final boolean archiveTimestamp141 = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_ARCHIVE_TIMESTAMP_141);
final boolean archiveTimestampV2 = DomUtils.isNotEmpty(signatureElement, xPathQueryHolder.XPATH_ARCHIVE_TIMESTAMP_V2);
return archiveTimestamp || archiveTimestamp141 || archiveTimestampV2;
}
/**
* Utility method to add content timestamps.
*
* @param timestampTokens
* @param nodes
* @param timestampType
* {@code TimestampType}
*/
public void addContentTimestamps(final List timestampTokens, final NodeList nodes, TimestampType timestampType) {
for (int ii = 0; ii < nodes.getLength(); ii++) {
final Node node = nodes.item(ii);
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
final Element element = (Element) node;
final TimestampToken timestampToken = makeTimestampToken(element, timestampType);
// TODO : Strange code
if (timestampToken == null) {
continue;
}
if (timestampToken.getTimestampIncludes() == null) {
timestampToken.setTimestampIncludes(new ArrayList());
}
final NodeList timestampIncludes = DomUtils.getNodeList(element, xPathQueryHolder.XPATH__INCLUDE);
for (int jj = 0; jj < timestampIncludes.getLength(); jj++) {
final Element include = (Element) timestampIncludes.item(jj);
final String uri = include.getAttribute("URI").substring(1); // '#'
// is
// removed
timestampToken.getTimestampIncludes().add(new TimestampInclude(uri, include.getAttribute("referencedData")));
}
timestampTokens.add(timestampToken);
}
}
@Override
public byte[] getContentTimestampData(final TimestampToken timestampToken) {
switch (timestampToken.getTimeStampType()) {
case INDIVIDUAL_DATA_OBJECTS_TIMESTAMP:
return getIndividualDataObjectsTimestampData(timestampToken);
case ALL_DATA_OBJECTS_TIMESTAMP:
return getAllDataObjectsTimestampData(timestampToken);
default:
return null;
}
}
/**
* See ETSI TS 101 903 v1.4.1, clause G.2.2.16.1.2
*
* @param timestampToken
* @return
*/
public byte[] getIndividualDataObjectsTimestampData(final TimestampToken timestampToken) {
// TODO: check whether a warning would be more appropriate
if (!checkTimestampTokenIncludes(timestampToken)) {
throw new DSSException("The Included referencedData attribute is either not present or set to false!");
}
if (references.isEmpty()) {
throw new DSSException("The method 'checkSignatureIntegrity' must be invoked first!");
}
// get first include element
// check coherence of the value of the not-fragment part of the URI
// within its URI attribute according to the rules stated in 7.1.4.3.1
// de-reference the URI according to the rules in 7.1.4.3.1
// check that retrieved element is actually a ds:Reference element of
// the ds:SignedInfo of the qualified signature and that its Type
// attribute is not SignedProperties
// if result is node-set, canonicalize it using the indicated
// canonicalizationMethod element of the property || use standard canon.
// method
// concatenate the resulting bytes in an octet stream
// repeat for all subsequent include elements, in order of appearance,
// within the time-stamp container
// return digest of resulting byte stream using the algorithm indicated
// in the time-stamp token
// get include elements from signature
List includes = timestampToken.getTimestampIncludes();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (TimestampInclude include : includes) {
// retrieve reference element
// -> go through references and check for one whose URI matches the
// URI of include
for (final Reference reference : references) {
String id = include.getURI();
if (reference.getId().equals(id)) {
try {
final byte[] referencedBytes = reference.getReferencedBytes();
outputStream.write(referencedBytes);
} catch (IOException e) {
throw new DSSException(e);
} catch (ReferenceNotInitializedException e) {
throw new DSSException(e);
} catch (XMLSignatureException e) {
throw new DSSException(e);
}
}
}
}
byte[] octetStream = outputStream.toByteArray();
return octetStream;
}
/**
* See ETSI TS 101 903 v1.4.1, clause G.2.2.16.1.1
*
* Retrieves the data from {@code TimeStampToken} of type AllDataObjectsTimestampData
*
* @param timestampToken
* @return a {@code byte} array containing the concatenated data from all reference elements of type differing from
* SignedProperties
*/
public byte[] getAllDataObjectsTimestampData(final TimestampToken timestampToken) {
// TODO: check whether a warning would be more appropriate
if (!checkTimestampTokenIncludes(timestampToken)) {
throw new DSSException("The Included referencedData attribute is either not present or set to false!");
}
if (references.isEmpty()) {
throw new DSSException("The method 'checkSignatureIntegrity' must be invoked first!");
}
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (final Reference reference : references) {
// Take, the first ds:Reference element within ds:SignedInfo if and
// only if the Type attribute does not
// have the value "http://uri.etsi.org/01903#SignedProperties".
if (!xPathQueryHolder.XADES_SIGNED_PROPERTIES.equals(reference.getType())) {
try {
final byte[] referencedBytes = reference.getReferencedBytes();
outputStream.write(referencedBytes);
} catch (IOException e) {
throw new DSSException(e);
} catch (ReferenceNotInitializedException e) {
throw new DSSException(e);
} catch (XMLSignatureException e) {
throw new DSSException(e);
}
}
}
// compute digest of resulting octet stream using algorithm indicated in
// the time-stamp token
// -> digest is computed in TimestampToken verification/match
// return the computed digest
byte[] toTimestampBytes = outputStream.toByteArray();
if (LOG.isTraceEnabled()) {
LOG.trace("AllDataObjectsTimestampData bytes: " + new String(toTimestampBytes));
}
return toTimestampBytes;
}
private List getSignatureTimestampedReferences() {
final List references = new ArrayList();
final TimestampReference signatureReference = getSignatureTimestampReference();
references.add(signatureReference);
final List signingCertificateTimestampReferences = getSigningCertificateTimestampReferences();
references.addAll(signingCertificateTimestampReferences);
return references;
}
private List getSigningCertificateTimestampReferences() {
if (signingCertificateTimestampReferences == null) {
signingCertificateTimestampReferences = new ArrayList();
final NodeList list = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_CERT_DIGEST);
for (int jj = 0; jj < list.getLength(); jj++) {
final Element element = (Element) list.item(jj);
final TimestampReference signingCertReference = createCertificateTimestampReference(element);
signingCertificateTimestampReferences.add(signingCertReference);
}
}
return signingCertificateTimestampReferences;
}
/**
* This method ensures that all Include elements referring to the Reference elements have a referencedData
* attribute, which is set to "true". In case one of
* these Include elements has its referenceData set to false, the method returns false
*
* @param timestampToken
* @retun
*/
public boolean checkTimestampTokenIncludes(final TimestampToken timestampToken) {
final List timestampIncludes = timestampToken.getTimestampIncludes();
for (final TimestampInclude timestampInclude : timestampIncludes) {
if (!timestampInclude.isReferencedData()) {
return false;
}
}
return true;
}
@Override
public List getContentTimestamps() {
if (contentTimestamps == null) {
makeTimestampTokens();
}
return contentTimestamps;
}
@Override
public List getSignatureTimestamps() {
if (signatureTimestamps == null) {
makeTimestampTokens();
}
return signatureTimestamps;
}
@Override
public List getTimestampsX1() {
if (sigAndRefsTimestamps == null) {
makeTimestampTokens();
}
return sigAndRefsTimestamps;
}
@Override
public List getTimestampsX2() {
if (refsOnlyTimestamps == null) {
makeTimestampTokens();
}
return refsOnlyTimestamps;
}
@Override
public List getArchiveTimestamps() {
if (archiveTimestamps == null) {
makeTimestampTokens();
}
return archiveTimestamps;
}
/**
* This method must not be called more than once.
*/
private void makeTimestampTokens() {
contentTimestamps = new ArrayList();
signatureTimestamps = new ArrayList();
refsOnlyTimestamps = new ArrayList();
sigAndRefsTimestamps = new ArrayList();
archiveTimestamps = new ArrayList();
// TODO (20/12/2014): Browse in the physical order
final NodeList allDataObjectsTimestamps = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_ALL_DATA_OBJECTS_TIMESTAMP);
addContentTimestamps(contentTimestamps, allDataObjectsTimestamps, TimestampType.ALL_DATA_OBJECTS_TIMESTAMP);
final NodeList individualDataObjectsTimestampsNodes = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_INDIVIDUAL_DATA_OBJECTS_TIMESTAMP);
addContentTimestamps(contentTimestamps, individualDataObjectsTimestampsNodes, TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP);
final Element unsignedSignaturePropertiesDom = getUnsignedSignaturePropertiesDom();
if (unsignedSignaturePropertiesDom == null) {
return;
}
final List timestampedTimestamps = new ArrayList();
final NodeList unsignedProperties = unsignedSignaturePropertiesDom.getChildNodes();
for (int ii = 0; ii < unsignedProperties.getLength(); ii++) {
final Node node = unsignedProperties.item(ii);
if (node.getNodeType() != Node.ELEMENT_NODE) { // This can happened
// when there is a
// blank line
// between tags.
continue;
}
TimestampToken timestampToken;
final String localName = node.getLocalName();
if (XMLE_SIGNATURE_TIME_STAMP.equals(localName)) {
timestampToken = makeTimestampToken((Element) node, TimestampType.SIGNATURE_TIMESTAMP);
if (timestampToken == null) {
continue;
}
timestampToken.setTimestampedReferences(getSignatureTimestampedReferences());
signatureTimestamps.add(timestampToken);
} else if (XMLE_REFS_ONLY_TIME_STAMP.equals(localName) || XPathQueryHolder.XMLE_REFS_ONLY_TIME_STAMP_V2.equals(localName)) {
timestampToken = makeTimestampToken((Element) node, TimestampType.VALIDATION_DATA_REFSONLY_TIMESTAMP);
if (timestampToken == null) {
continue;
}
timestampToken.setTimestampedReferences(getTimestampedReferences());
refsOnlyTimestamps.add(timestampToken);
} else if (XMLE_SIG_AND_REFS_TIME_STAMP.equals(localName) || XPathQueryHolder.XMLE_SIG_AND_REFS_TIME_STAMP_V2.equals(localName)) {
timestampToken = makeTimestampToken((Element) node, TimestampType.VALIDATION_DATA_TIMESTAMP);
if (timestampToken == null) {
continue;
}
final List references = getSignatureTimestampedReferences();
references.addAll(getTimestampedReferences());
timestampToken.setTimestampedReferences(references);
sigAndRefsTimestamps.add(timestampToken);
} else if (isArchiveTimestamp(localName)) {
timestampToken = makeTimestampToken((Element) node, TimestampType.ARCHIVE_TIMESTAMP);
if (timestampToken == null) {
continue;
}
final ArchiveTimestampType archiveTimestampType = getArchiveTimestampType(node, localName);
timestampToken.setArchiveTimestampType(archiveTimestampType);
final List references = getSignatureTimestampedReferences();
for (final String timestampId : timestampedTimestamps) {
references.add(new TimestampReference(timestampId, TimestampReferenceCategory.TIMESTAMP));
}
references.addAll(getTimestampedReferences());
final List encapsulatedCertificates = getCertificateSource().getEncapsulatedCertificates();
for (final CertificateToken certificateToken : encapsulatedCertificates) {
final TimestampReference certificateTimestampReference = createCertificateTimestampReference(certificateToken);
if (!references.contains(certificateTimestampReference)) {
references.add(certificateTimestampReference);
}
}
addReferencesFromOfflineCRLSource(references);
addReferencesFromOfflineOCSPSource(references);
timestampToken.setTimestampedReferences(references);
archiveTimestamps.add(timestampToken);
} else {
continue;
}
timestampedTimestamps.add(timestampToken.getDSSIdAsString());
}
}
private ArchiveTimestampType getArchiveTimestampType(final Node node, final String localName) {
if (XPathQueryHolder.XMLE_ARCHIVE_TIME_STAMP_V2.equals(localName)) {
return ArchiveTimestampType.XAdES_141_V2;
} else if (XPathQueryHolder.XMLE_ARCHIVE_TIME_STAMP.equals(localName)) {
final String namespaceURI = node.getNamespaceURI();
if (XAdESNamespaces.XAdES141.equals(namespaceURI)) {
return ArchiveTimestampType.XAdES_141;
}
}
return ArchiveTimestampType.XAdES;
}
private TimestampReference getSignatureTimestampReference() {
final TimestampReference signatureReference = new TimestampReference(getId());
return signatureReference;
}
private String getTimestampCanonicalizationMethod(final Element timestampElement) {
final Element canonicalizationMethodElement = DomUtils.getElement(timestampElement, xPathQueryHolder.XPATH__CANONICALIZATION_METHOD);
String canonicalizationMethod = DEFAULT_TIMESTAMP_VALIDATION_CANONICALIZATION_METHOD;
if (canonicalizationMethodElement != null) {
canonicalizationMethod = canonicalizationMethodElement.getAttribute(XMLE_ALGORITHM);
}
return canonicalizationMethod;
}
/*
* Returns an unmodifiable list of all certificate tokens encapsulated in the signature
*
* @see eu.europa.esig.dss.validation.AdvancedSignature#getCertificates()
*/
@Override
public List getCertificates() {
return getCertificateSource().getCertificates();
}
/*
* Returns the list of certificates encapsulated in the KeyInfo segment
*/
public List getKeyInfoCertificates() {
return getCertificateSource().getKeyInfoCertificates();
}
/*
* Returns the list of certificates encapsulated in the KeyInfo segment
*/
public List getTimestampCertificates() {
return getCertificateSource().getTimestampCertificates();
}
@Override
public void checkSignatureIntegrity() {
if (signatureCryptographicVerification != null) {
return;
}
signatureCryptographicVerification = new SignatureCryptographicVerification();
final Document document = signatureElement.getOwnerDocument();
final Element rootElement = document.getDocumentElement();
DSSXMLUtils.setIDIdentifier(rootElement);
DSSXMLUtils.recursiveIdBrowse(rootElement);
try {
final XMLSignature santuarioSignature = new XMLSignature(signatureElement, "");
santuarioSignature.addResourceResolver(new XPointerResourceResolver(signatureElement));
santuarioSignature.addResourceResolver(new OfflineResolver(detachedContents, getDigestAlgorithm()));
boolean coreValidity = false;
final List certificateValidityList = getSigningCertificateValidityList(santuarioSignature, signatureCryptographicVerification,
providedSigningCertificateToken);
LOG.debug("Determining signing certificate from certificate candidates list");
final List preliminaryErrorMessages = new ArrayList();
int certificateNumber = 0;
for (final CertificateValidity certificateValidity : certificateValidityList) {
String errorMessagePrefix = "Certificate #" + (certificateNumber + 1) + ": ";
try {
final PublicKey publicKey = certificateValidity.getPublicKey();
coreValidity = santuarioSignature.checkSignatureValue(publicKey);
if (coreValidity) {
LOG.info("Determining signing certificate from certificate candidates list succeeded");
candidatesForSigningCertificate.setTheCertificateValidity(certificateValidity);
break;
} else {
// upon returning false, santuarioSignature (class XMLSignature) will log "Signature
// verification failed." with WARN level.
preliminaryErrorMessages.add(errorMessagePrefix + "Signature verification failed");
}
} catch (XMLSignatureException e) {
LOG.debug("Exception while probing candidate certificate as signing certificate: " + e.getMessage());
preliminaryErrorMessages.add(errorMessagePrefix + e.getMessage());
}
certificateNumber++;
}
if (!coreValidity) {
LOG.warn("Determining signing certificate from certificate candidates list failed: {}", preliminaryErrorMessages);
for (String preliminaryErrorMessage : preliminaryErrorMessages) {
signatureCryptographicVerification.setErrorMessage(preliminaryErrorMessage);
}
}
final SignedInfo signedInfo = santuarioSignature.getSignedInfo();
final int length = signedInfo.getLength();
boolean referenceDataFound = length > 0;
boolean referenceDataHashValid = length > 0;
boolean foundSignedProperties = false;
for (int ii = 0; ii < length; ii++) {
final Reference reference = signedInfo.item(ii);
if (xPathQueryHolder.XADES_SIGNED_PROPERTIES.equals(reference.getType())) {
foundSignedProperties = true;
}
if (!coreValidity) {
referenceDataHashValid = referenceDataHashValid && reference.verify();
}
references.add(reference);
}
// 1 reference for SignedProperties + 1 reference / signed object
referenceDataFound = referenceDataFound && foundSignedProperties;
signatureCryptographicVerification.setReferenceDataFound(referenceDataFound);
signatureCryptographicVerification.setReferenceDataIntact(referenceDataHashValid);
signatureCryptographicVerification.setSignatureIntact(coreValidity);
} catch (Exception e) {
LOG.error(e.getMessage());
LOG.debug(e.getMessage(), e);
StackTraceElement[] stackTrace = e.getStackTrace();
final String name = XAdESSignature.class.getName();
int lineNumber = 0;
for (StackTraceElement element : stackTrace) {
final String className = element.getClassName();
if (className.equals(name)) {
lineNumber = element.getLineNumber();
break;
}
}
signatureCryptographicVerification.setErrorMessage(e.getMessage() + "/ XAdESSignature/Line number/" + lineNumber);
}
}
/**
* This method returns a {@code List} of {@code SigningCertificateValidity} base on the certificates extracted from
* the signature or on the
* {@code providedSigningCertificateToken}. The field {@code candidatesForSigningCertificate} is instantiated in
* case where the signing certificated is
* provided.
*
* @param santuarioSignature
* The object created tro validate the signature
* @param scv
* {@code SignatureCryptographicVerification} containing information on the signature validation
* @param providedSigningCertificate
* provided signing certificate: {@code CertificateToken} @return
* @return the {@code List} of the {@code SigningCertificateValidity}
* @throws KeyResolverException
*/
private List getSigningCertificateValidityList(final XMLSignature santuarioSignature, SignatureCryptographicVerification scv,
final CertificateToken providedSigningCertificate) throws KeyResolverException {
List certificateValidityList;
if (providedSigningCertificate == null) {
// To determine the signing certificate it is necessary to browse
// through all candidates extracted from the signature.
final CandidatesForSigningCertificate candidates = getCandidatesForSigningCertificate();
certificateValidityList = candidates.getCertificateValidityList();
if (certificateValidityList.isEmpty()) {
// The public key can also be extracted from the signature.
final KeyInfo extractedKeyInfo = santuarioSignature.getKeyInfo();
final PublicKey publicKey;
if ((extractedKeyInfo == null) || ((publicKey = extractedKeyInfo.getPublicKey()) == null)) {
scv.setErrorMessage("There is no signing certificate within the signature.");
return certificateValidityList;
}
certificateValidityList = getSigningCertificateValidityList(publicKey);
}
} else {
candidatesForSigningCertificate = new CandidatesForSigningCertificate();
final CertificateValidity certificateValidity = new CertificateValidity(providedSigningCertificate);
candidatesForSigningCertificate.add(certificateValidity);
certificateValidityList = candidatesForSigningCertificate.getCertificateValidityList();
}
return certificateValidityList;
}
/**
* This method returns a {@code List} of {@code SigningCertificateValidity} base on the provided
* {@code providedSigningCertificateToken}. The field
* {@code candidatesForSigningCertificate} is instantiated.
*
* @param extractedPublicKey
* provided public key: {@code PublicKey}
* @return
*/
protected List getSigningCertificateValidityList(final PublicKey extractedPublicKey) {
candidatesForSigningCertificate = new CandidatesForSigningCertificate();
final CertificateValidity certificateValidity = new CertificateValidity(extractedPublicKey);
candidatesForSigningCertificate.add(certificateValidity);
final List certificateValidityList = candidatesForSigningCertificate.getCertificateValidityList();
return certificateValidityList;
}
/**
* This method retrieves the potential countersignatures embedded in the XAdES signature document. From ETSI TS 101
* 903 v1.4.2:
*
* 7.2.4.1 Countersignature identifier in Type attribute of ds:Reference
*
* A XAdES signature containing a ds:Reference element whose Type attribute has value
* "http://uri.etsi.org/01903#CountersignedSignature" will indicate that
* is is, in fact, a countersignature of the signature referenced by this element.
*
* 7.2.4.2 Enveloped countersignatures: the CounterSignature element
*
* The CounterSignature is an unsigned property that qualifies the signature. A XAdES signature MAY have more than
* one CounterSignature properties. As
* indicated by its name, it contains one countersignature of the qualified signature.
*
* @return a list containing the countersignatures embedded in the XAdES signature document
*/
@Override
public List getCounterSignatures() {
// see ETSI TS 101 903 V1.4.2 (2010-12) pp. 38/39/40
final NodeList counterSignatures = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_COUNTER_SIGNATURE);
if (counterSignatures == null) {
return null;
}
final List xadesList = new ArrayList();
for (int ii = 0; ii < counterSignatures.getLength(); ii++) {
final Element counterSignatureElement = (Element) counterSignatures.item(ii);
final Element signatureElement = DomUtils.getElement(counterSignatureElement, xPathQueryHolder.XPATH__SIGNATURE);
// Verify that the element is a proper signature by trying to build
// a XAdESSignature out of it
final XAdESSignature xadesCounterSignature = new XAdESSignature(signatureElement, xPathQueryHolders, certPool);
if (isCounterSignature(xadesCounterSignature)) {
xadesCounterSignature.setMasterSignature(this);
xadesList.add(xadesCounterSignature);
}
}
return xadesList;
}
/**
* This method verifies whether a given signature is a countersignature.
*
* From ETSI TS 101 903 V1.4.2: - The signature's ds:SignedInfo element MUST contain one ds:Reference element
* referencing the ds:Signature element of the
* embedding and countersigned XAdES signature - The content of the ds:DigestValue in the aforementioned
* ds:Reference element of the countersignature MUST
* be the base-64 encoded digest of the complete (and canonicalized) ds:SignatureValue element (i.e. including the
* starting and closing tags) of the
* embedding and countersigned XAdES signature.
*
* @param xadesCounterSignature
* @return
*/
private boolean isCounterSignature(final XAdESSignature xadesCounterSignature) {
final List signatureReferences = xadesCounterSignature.getSignatureReferences();
// gets Element with
// Type="http://uri.etsi.org/01903#CountersignedSignature"
for (final Element reference : signatureReferences) {
final String type = reference.getAttribute("Type");
if (xPathQueryHolder.XADES_COUNTERSIGNED_SIGNATURE.equals(type)) {
return true;
}
}
return false;
}
@Override
public List getCertificateRefs() {
Element signingCertEl = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_CERT_REFS);
if (signingCertEl == null) {
return null;
}
List certIds = new ArrayList();
NodeList certIdnodes = DomUtils.getNodeList(signingCertEl, "./xades:Cert");
for (int i = 0; i < certIdnodes.getLength(); i++) {
Element certId = (Element) certIdnodes.item(i);
Element issuerNameEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__X509_ISSUER_NAME);
Element issuerSerialEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__X509_SERIAL_NUMBER);
Element digestAlgorithmEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__CERT_DIGEST_DIGEST_METHOD);
Element digestValueEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__CERT_DIGEST_DIGEST_VALUE);
CertificateRef genericCertId = new CertificateRef();
if ((issuerNameEl != null) && (issuerSerialEl != null)) {
genericCertId.setIssuerName(issuerNameEl.getTextContent());
genericCertId.setIssuerSerial(issuerSerialEl.getTextContent());
}
String xmlName = digestAlgorithmEl.getAttribute(XMLE_ALGORITHM);
genericCertId.setDigestAlgorithm(DigestAlgorithm.forXML(xmlName));
genericCertId.setDigestValue(Utils.fromBase64(digestValueEl.getTextContent()));
certIds.add(genericCertId);
}
return certIds;
}
@Override
public List getCRLRefs() {
final List certIds = new ArrayList();
final Element signingCertEl = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_REVOCATION_CRL_REFS);
if (signingCertEl != null) {
final NodeList crlRefNodes = DomUtils.getNodeList(signingCertEl, xPathQueryHolder.XPATH__CRL_REF);
for (int i = 0; i < crlRefNodes.getLength(); i++) {
final Element certId = (Element) crlRefNodes.item(i);
final Element digestAlgorithmEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__DAAV_DIGEST_METHOD);
final Element digestValueEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__DAAV_DIGEST_VALUE);
final String xmlName = digestAlgorithmEl.getAttribute(XMLE_ALGORITHM);
final DigestAlgorithm digestAlgo = DigestAlgorithm.forXML(xmlName);
final CRLRef ref = new CRLRef(digestAlgo, Utils.fromBase64(digestValueEl.getTextContent()));
certIds.add(ref);
}
}
return certIds;
}
@Override
public List getOCSPRefs() {
final List certIds = new ArrayList();
final Element signingCertEl = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_OCSP_REFS);
if (signingCertEl != null) {
final NodeList ocspRefNodes = DomUtils.getNodeList(signingCertEl, xPathQueryHolder.XPATH__OCSPREF);
for (int i = 0; i < ocspRefNodes.getLength(); i++) {
final Element certId = (Element) ocspRefNodes.item(i);
final Element digestAlgorithmEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__DAAV_DIGEST_METHOD);
final Element digestValueEl = DomUtils.getElement(certId, xPathQueryHolder.XPATH__DAAV_DIGEST_VALUE);
if ((digestAlgorithmEl == null) || (digestValueEl == null)) {
throw new DSSNotETSICompliantException(DSSNotETSICompliantException.MSG.XADES_DIGEST_ALG_AND_VALUE_ENCODING);
}
final String xmlName = digestAlgorithmEl.getAttribute(XMLE_ALGORITHM);
final DigestAlgorithm digestAlgo = DigestAlgorithm.forXML(xmlName);
final String digestValue = digestValueEl.getTextContent();
final byte[] base64EncodedDigestValue = Utils.fromBase64(digestValue);
final OCSPRef ocspRef = new OCSPRef(digestAlgo, base64EncodedDigestValue, false);
certIds.add(ocspRef);
}
}
return certIds;
}
@Override
public byte[] getSignatureTimestampData(final TimestampToken timestampToken, String canonicalizationMethod) {
canonicalizationMethod = timestampToken != null ? timestampToken.getCanonicalizationMethod() : canonicalizationMethod;
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
writeCanonicalizedValue(xPathQueryHolder.XPATH_SIGNATURE_VALUE, canonicalizationMethod, buffer);
if (LOG.isTraceEnabled()) {
LOG.trace("Signature timestamp: canonicalization method --> {}", canonicalizationMethod);
LOG.trace(" : canonicalized string --> {}", buffer.toString());
}
} catch (IOException e) {
throw new DSSException("Error when computing the SignatureTimestamp", e);
}
return buffer.toByteArray();
}
@Override
public byte[] getTimestampX1Data(final TimestampToken timestampToken, String canonicalizationMethod) {
canonicalizationMethod = timestampToken != null ? timestampToken.getCanonicalizationMethod() : canonicalizationMethod;
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
writeCanonicalizedValue(xPathQueryHolder.XPATH_SIGNATURE_VALUE, canonicalizationMethod, buffer);
final NodeList signatureTimeStampNode = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_SIGNATURE_TIMESTAMP);
if (signatureTimeStampNode != null) {
for (int ii = 0; ii < signatureTimeStampNode.getLength(); ii++) {
final Node item = signatureTimeStampNode.item(ii);
final byte[] canonicalizedValue = DSSXMLUtils.canonicalizeSubtree(canonicalizationMethod, item);
buffer.write(canonicalizedValue);
}
}
writeCanonicalizedValue(xPathQueryHolder.XPATH_COMPLETE_CERTIFICATE_REFS, canonicalizationMethod, buffer);
writeCanonicalizedValue(xPathQueryHolder.XPATH_COMPLETE_REVOCATION_REFS, canonicalizationMethod, buffer);
if (LOG.isTraceEnabled()) {
LOG.trace("X1Timestamp (SigAndRefsTimeStamp) canonicalised string:\n" + buffer.toString());
}
return buffer.toByteArray();
} catch (IOException e) {
throw new DSSException("Error when computing the SigAndRefsTimeStamp (X1Timestamp)", e);
}
}
@Override
public byte[] getTimestampX2Data(final TimestampToken timestampToken, String canonicalizationMethod) {
canonicalizationMethod = timestampToken != null ? timestampToken.getCanonicalizationMethod() : canonicalizationMethod;
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
writeCanonicalizedValue(xPathQueryHolder.XPATH_COMPLETE_CERTIFICATE_REFS, canonicalizationMethod, buffer);
writeCanonicalizedValue(xPathQueryHolder.XPATH_COMPLETE_REVOCATION_REFS, canonicalizationMethod, buffer);
if (LOG.isTraceEnabled()) {
LOG.trace("TimestampX2Data (RefsOnlyTimeStamp) canonicalised string:\n" + buffer.toString());
}
return buffer.toByteArray();
} catch (IOException e) {
throw new DSSException("Error when computing the RefsOnlyTimeStamp (TimestampX2D)", e);
}
}
/**
* Gathers the data to be used to calculate the hash value sent to the TSA (messageImprint).
*
* @param timestampToken
* {@code TimestampToken} to validate, or {@code null} when adding a new archive timestamp
* @param canonicalizationMethod
* @return {@code byte} array containing the canonicalized and concatenated timestamped data
*/
@Override
public byte[] getArchiveTimestampData(final TimestampToken timestampToken, String canonicalizationMethod) {
if (LOG.isTraceEnabled()) {
LOG.trace("--->Get archive timestamp data:" + (timestampToken == null ? "--> CREATION" : "--> VALIDATION"));
}
canonicalizationMethod = timestampToken != null ? timestampToken.getCanonicalizationMethod() : canonicalizationMethod;
/**
* 8.2.1 Not distributed case
*
* When xadesv141:ArchiveTimeStamp and all the unsigned properties covered by its time-stamp certificateToken
* have the same parent, this property uses
* the Implicit mechanism for all the time-stamped data objects. The input to the computation of the digest
* value MUST be built as follows:
*/
try {
/**
* 1) Initialize the final octet stream as an empty octet stream.
*/
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
/**
* 2) Take all the ds:Reference elements in their order of appearance within ds:SignedInfo referencing
* whatever the signer wants to sign including
* the SignedProperties element. Process each one as indicated below:
* - Process the retrieved ds:Reference element according to the reference processing model of XMLDSIG.
* - If the result is a XML node set, canonicalize it. If ds:Canonicalization is present, the algorithm
* indicated by this element is used. If not,
* the standard canonicalization method specified by XMLDSIG is used.
* - Concatenate the resulting octets to the final octet stream.
*/
/**
* The references are already calculated {@see #checkSignatureIntegrity()}
*/
final Set referenceURIs = new HashSet();
for (final Reference reference : references) {
try {
String uri = reference.getURI();
if (uri.startsWith("#")) {
uri = uri.substring(1);
}
referenceURIs.add(uri);
final byte[] bytes = reference.getReferencedBytes();
Utils.write(bytes, buffer);
} catch (XMLSignatureException e) {
throw new DSSException(e);
}
}
/**
* 3) Take the following XMLDSIG elements in the order they are listed below, canonicalize each one and
* concatenate each resulting octet stream to
* the final octet stream:
* - The ds:SignedInfo element.
* - The ds:SignatureValue element.
* - The ds:KeyInfo element, if present.
*/
writeCanonicalizedValue(xPathQueryHolder.XPATH_SIGNED_INFO, canonicalizationMethod, buffer);
writeCanonicalizedValue(xPathQueryHolder.XPATH_SIGNATURE_VALUE, canonicalizationMethod, buffer);
writeCanonicalizedValue(xPathQueryHolder.XPATH_KEY_INFO, canonicalizationMethod, buffer);
/**
* 4) Take the unsigned signature properties that appear before the current xadesv141:ArchiveTimeStamp in
* the order they appear within the
* xades:UnsignedSignatureProperties, canonicalize each one and concatenate each resulting octet stream to
* the final octet stream. While
* concatenating the following rules apply:
*/
final Element unsignedSignaturePropertiesDom = getUnsignedSignaturePropertiesDom();
if (unsignedSignaturePropertiesDom == null) {
throw new NullPointerException(xPathQueryHolder.XPATH_UNSIGNED_SIGNATURE_PROPERTIES);
}
final NodeList unsignedProperties = unsignedSignaturePropertiesDom.getChildNodes();
for (int ii = 0; ii < unsignedProperties.getLength(); ii++) {
final Node node = unsignedProperties.item(ii);
if (node.getNodeType() != Node.ELEMENT_NODE) { // This can
// happened when
// there is a
// blank line
// between tags.
continue;
}
final String localName = node.getLocalName();
// In the SD-DSS implementation when validating the signature
// the framework will not add missing data. To do so the
// signature must be extended.
// if (localName.equals("CertificateValues")) {
/*
* - The xades:CertificateValues property MUST be added if it is not already present and the ds:KeyInfo
* element does not contain the full set of
* certificates used to validate the electronic signature.
*/
// } else if (localName.equals("RevocationValues")) {
/*
* - The xades:RevocationValues property MUST be added if it is not already present and the ds:KeyInfo
* element does not contain the revocation
* information that has to be shipped with the electronic signature
*/
// } else if (localName.equals("AttrAuthoritiesCertValues")) {
/*
* - The xades:AttrAuthoritiesCertValues property MUST be added if not already present and the following
* conditions are true: there exist an
* attribute certificate in the signature AND a number of certificates that have been used in its
* validation do not appear in CertificateValues.
* Its content will satisfy with the rules specified in clause 7.6.3.
*/
// } else if (localName.equals("AttributeRevocationValues")) {
/*
* - The xades:AttributeRevocationValues property MUST be added if not already present and there the
* following conditions are true: there exist
* an attribute certificate AND some revocation data that have been used in its validation do not appear
* in RevocationValues. Its content will
* satisfy with the rules specified in clause 7.6.4.
*/
// } else
if (isArchiveTimestamp(localName)) {
if ((timestampToken != null) && (timestampToken.getHashCode() == node.hashCode())) {
break;
}
} else if ("TimeStampValidationData".equals(localName)) {
/**
* ETSI TS 101 903 V1.4.2 (2010-12) 8.1 The new XAdESv141:TimeStampValidationData element ../.. This
* element is specified to serve as an
* optional container for validation data required for carrying a full verification of time-stamp
* tokens embedded within any of the
* different time-stamp containers defined in the present document. ../.. 8.1.1 Use of URI attribute
* ../.. a new
* xadesv141:TimeStampValidationData element SHALL be created containing the missing validation data
* information and it SHALL be added as a
* child of UnsignedSignatureProperties elements immediately after the respective time-stamp
* certificateToken container element.
*/
}
byte[] canonicalizedValue;
if (timestampToken == null) { // Creation of the timestamp
/**
* This is the work around for the name space problem: The issue was reported on:
* https://issues.apache.org/jira/browse/SANTUARIO-139 and
* considered as close. But for me (Bob) it still does not work!
*/
final byte[] bytesToCanonicalize = DSSXMLUtils.serializeNode(node);
canonicalizedValue = DSSXMLUtils.canonicalize(canonicalizationMethod, bytesToCanonicalize);
} else {
canonicalizedValue = DSSXMLUtils.canonicalizeSubtree(canonicalizationMethod, node);
}
if (LOG.isTraceEnabled()) {
LOG.trace(localName + ": Canonicalization: " + canonicalizationMethod);
LOG.trace(new String(canonicalizedValue) + "\n");
}
buffer.write(canonicalizedValue);
}
/**
* 5) Take all the ds:Object elements except the one containing xades:QualifyingProperties element.
* Canonicalize each one and concatenate each
* resulting octet stream to the final octet stream. If ds:Canonicalization is present, the algorithm
* indicated by this element is used. If not, the
* standard canonicalization method specified by XMLDSIG is used.
*/
boolean xades141 = (timestampToken == null) || !ArchiveTimestampType.XAdES.equals(timestampToken.getArchiveTimestampType());
final NodeList objects = getObjects();
for (int ii = 0; ii < objects.getLength(); ii++) {
final Node node = objects.item(ii);
final Node qualifyingProperties = DomUtils.getElement(node, xPathQueryHolder.XPATH__QUALIFYING_PROPERTIES);
if (qualifyingProperties != null) {
continue;
}
if (!xades141) {
/**
* !!! ETSI TS 101 903 V1.3.2 (2006-03) 5) Take any ds:Object element in the signature that is not
* referenced by any ds:Reference within
* ds:SignedInfo, except that one containing the QualifyingProperties element. Canonicalize each one
* and concatenate each resulting octet
* stream to the final octet stream. If ds:Canonicalization is present, the algorithm indicated by
* this element is used. If not, the
* standard canonicalization method specified by XMLDSIG is used.
*/
final NamedNodeMap attributes = node.getAttributes();
final int length = attributes.getLength();
String id = "";
for (int jj = 0; jj < length; jj++) {
final Node item = attributes.item(jj);
final String nodeName = item.getNodeName();
if (Utils.areStringsEqualIgnoreCase("ID", nodeName)) {
id = item.getNodeValue();
break;
}
}
final boolean contains = referenceURIs.contains(id);
if (contains) {
continue;
}
}
byte[] canonicalizedValue = DSSXMLUtils.canonicalizeSubtree(canonicalizationMethod, node);
buffer.write(canonicalizedValue);
}
final byte[] bytes = buffer.toByteArray();
return bytes;
} catch (IOException e) {
throw new DSSException("Error when computing the archive data", e);
}
}
private void writeCanonicalizedValue(final String xPathString, final String canonicalizationMethod, final ByteArrayOutputStream buffer) throws IOException {
final Element element = DomUtils.getElement(signatureElement, xPathString);
if (element != null) {
final byte[] canonicalizedValue = DSSXMLUtils.canonicalizeSubtree(canonicalizationMethod, element);
buffer.write(canonicalizedValue);
}
}
private boolean isArchiveTimestamp(final String localName) {
return XPathQueryHolder.XMLE_ARCHIVE_TIME_STAMP.equals(localName) || XPathQueryHolder.XMLE_ARCHIVE_TIME_STAMP_V2.equals(localName);
}
@Override
public String getId() {
if (signatureId == null) {
String idValue = DSSXMLUtils.getIDIdentifier(signatureElement);
if (idValue != null) {
signatureId = idValue;
} else {
final CertificateToken certificateToken = getSigningCertificateToken();
TokenIdentifier identifier = certificateToken == null ? null : certificateToken.getDSSId();
signatureId = DSSUtils.getDeterministicId(getSigningTime(), identifier);
}
}
return signatureId;
}
@Override
public List getTimestampedReferences() {
final List references = new ArrayList();
final Node completeCertificateRefsNode = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_COMPLETE_CERTIFICATE_REFS);
if (completeCertificateRefsNode != null) {
final NodeList nodes = DomUtils.getNodeList(completeCertificateRefsNode, xPathQueryHolder.XPATH__COMPLETE_CERTIFICATE_REFS__CERT_DIGEST);
for (int ii = 0; ii < nodes.getLength(); ii++) {
final Element certDigestElement = (Element) nodes.item(ii);
final TimestampReference certificateReference = createCertificateTimestampReference(certDigestElement);
references.add(certificateReference);
}
}
final Node completeRevocationRefsNode = DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_COMPLETE_REVOCATION_REFS);
if (completeRevocationRefsNode != null) {
final NodeList nodes = DomUtils.getNodeList(completeRevocationRefsNode, "./*/*/xades:DigestAlgAndValue");
for (int ii = 0; ii < nodes.getLength(); ii++) {
final Element element = (Element) nodes.item(ii);
final TimestampReference revocationReference = createRevocationTimestampReference(element);
references.add(revocationReference);
}
}
return references;
}
private TimestampReference createRevocationTimestampReference(Element element) {
String digestAlgorithmStr = DomUtils.getNode(element, xPathQueryHolder.XPATH__DIGEST_METHOD_ALGORITHM).getTextContent();
DigestAlgorithm digestAlgorithm = DigestAlgorithm.forXML(digestAlgorithmStr);
final String digestValue = DomUtils.getElement(element, xPathQueryHolder.XPATH__DIGEST_VALUE).getTextContent();
final TimestampReference revocationReference = new TimestampReference(digestAlgorithm, digestValue);
return revocationReference;
}
/**
* Retrieves the name of each node found under the unsignedSignatureProperties element
*
* @return an ArrayList containing the retrieved node names
*/
public List getUnsignedSignatureProperties() {
final List childrenNames = DomUtils.getChildrenNames(signatureElement, xPathQueryHolder.XPATH_UNSIGNED_SIGNATURE_PROPERTIES);
return childrenNames;
}
public List getSignedSignatureProperties() {
final List childrenNames = DomUtils.getChildrenNames(signatureElement, xPathQueryHolder.XPATH_SIGNED_SIGNATURE_PROPERTIES);
return childrenNames;
}
public List getSignedProperties() {
final List childrenNames = DomUtils.getChildrenNames(signatureElement, xPathQueryHolder.XPATH_SIGNED_PROPERTIES);
return childrenNames;
}
public List getUnsignedProperties() {
final List childrenNames = DomUtils.getChildrenNames(signatureElement, xPathQueryHolder.XPATH_UNSIGNED_PROPERTIES);
return childrenNames;
}
public List getSignedDataObjectProperties() {
final List childrenNames = DomUtils.getChildrenNames(signatureElement, xPathQueryHolder.XPATH_SIGNED_DATA_OBJECT_PROPERTIES);
return childrenNames;
}
/**
* This method creates
*
* @param element
* @return
* @throws eu.europa.esig.dss.DSSException
*/
private TimestampReference createCertificateTimestampReference(final Element element) throws DSSException {
final String xmlDigestAlgorithm = DomUtils.getNode(element, xPathQueryHolder.XPATH__DIGEST_METHOD_ALGORITHM).getTextContent();
final DigestAlgorithm digestAlgorithm = DigestAlgorithm.forXML(xmlDigestAlgorithm);
usedCertificatesDigestAlgorithms.add(digestAlgorithm);
final Element digestValueElement = DomUtils.getElement(element, xPathQueryHolder.XPATH__DIGEST_VALUE);
final String digestValue = (digestValueElement == null) ? "" : digestValueElement.getTextContent();
final TimestampReference reference = new TimestampReference(digestAlgorithm, digestValue);
return reference;
}
private TimestampReference createCertificateTimestampReference(final CertificateToken certificateToken) throws DSSException {
usedCertificatesDigestAlgorithms.add(DigestAlgorithm.SHA1);
final TimestampReference reference = new TimestampReference(DigestAlgorithm.SHA1, Utils.toBase64(certificateToken.getDigest(DigestAlgorithm.SHA1)));
return reference;
}
@Override
public boolean isDataForSignatureLevelPresent(final SignatureLevel signatureLevel) {
boolean dataForLevelPresent = true;
switch (signatureLevel) {
case XML_NOT_ETSI:
break;
case XAdES_BASELINE_LTA:
dataForLevelPresent = hasLTAProfile();
break;
case XAdES_BASELINE_LT:
dataForLevelPresent &= hasLTProfile();
break;
case XAdES_BASELINE_T:
dataForLevelPresent &= hasTProfile();
break;
case XAdES_BASELINE_B:
dataForLevelPresent &= hasBProfile();
break;
case XAdES_X:
dataForLevelPresent &= hasXProfile();
break;
case XAdES_C:
dataForLevelPresent &= hasCProfile();
break;
default:
throw new IllegalArgumentException("Unknown level " + signatureLevel);
}
return dataForLevelPresent;
}
@Override
public SignatureLevel[] getSignatureLevels() {
return signatureLevels;
}
@Override
public void validateStructure() {
final String string = DomUtils.xmlToString(signatureElement);
StringReader stringReader = new StringReader(string);
structureValidation = DSSXMLUtils.validateAgainstXSD(new StreamSource(stringReader));
}
/**
* This method returns the last timestamp validation data for an archive timestamp.
*
* @return
*/
public Element getLastTimestampValidationData() {
final List archiveTimestamps = getArchiveTimestamps();
TimestampToken mostRecentTimestamp = null;
for (final TimestampToken archiveTimestamp : archiveTimestamps) {
if (mostRecentTimestamp == null) {
mostRecentTimestamp = archiveTimestamp;
continue;
}
final Date generationTime = archiveTimestamp.getGenerationTime();
final Date mostRecentGenerationTime = mostRecentTimestamp.getGenerationTime();
if (generationTime.after(mostRecentGenerationTime)) {
mostRecentTimestamp = archiveTimestamp;
}
}
final int timestampHashCode = mostRecentTimestamp.getHashCode();
final NodeList nodeList = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_UNSIGNED_SIGNATURE_PROPERTIES + "/*");
boolean found = false;
for (int ii = 0; ii < nodeList.getLength(); ii++) {
final Element unsignedSignatureElement = (Element) nodeList.item(ii);
final int nodeHashCode = unsignedSignatureElement.hashCode();
if (nodeHashCode == timestampHashCode) {
found = true;
} else if (found) {
final String nodeName = unsignedSignatureElement.getLocalName();
if ("TimeStampValidationData".equals(nodeName)) {
return unsignedSignatureElement;
}
}
}
return null;
}
@Override
public CommitmentType getCommitmentTypeIndication() {
return null;
}
/**
* // TODO (11/09/2014): to be deleted, eu.europa.esig.dss.xades.validation.XAdESSignature#getReferences() to be
* used
*
* @return
*/
public List getSignatureReferences() {
final NodeList list = DomUtils.getNodeList(signatureElement, xPathQueryHolder.XPATH_REFERENCE);
List references = new ArrayList(list.getLength());
for (int ii = 0; ii < list.getLength(); ii++) {
final Node node = list.item(ii);
references.add((Element) node);
}
return references;
}
public List getReferences() {
return references;
}
/**
* @return
*/
public List getSignatureObjects() {
final NodeList list = DomUtils.getNodeList(signatureElement, XPathQueryHolder.XPATH_OBJECT);
final List references = new ArrayList(list.getLength());
for (int ii = 0; ii < list.getLength(); ii++) {
final Node node = list.item(ii);
final Element element = (Element) node;
if (DomUtils.getElement(element, xPathQueryHolder.XPATH__QUALIFYING_PROPERTIES_SIGNED_PROPERTIES) != null) {
// ignore signed properties
continue;
}
references.add(element);
}
return references;
}
/**
* This method allows to register a new {@code XPathQueryHolder}.
*
* @param xPathQueryHolder
* {@code XPathQueryHolder} to register
*/
public void registerXPathQueryHolder(final XPathQueryHolder xPathQueryHolder) {
xPathQueryHolders.add(xPathQueryHolder);
}
public Element getUnsignedSignaturePropertiesDom() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_UNSIGNED_SIGNATURE_PROPERTIES);
}
public Element getUnsignedPropertiesDom() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_UNSIGNED_PROPERTIES);
}
public Element getQualifyingPropertiesDom() {
return DomUtils.getElement(signatureElement, xPathQueryHolder.XPATH_QUALIFYING_PROPERTIES);
}
}