/* DigiDoc4J library
*
* This software is released under either the GNU Library General Public
* License (see LICENSE.LGPL).
*
* Note that the only valid version of the LGPL license as far as this
* project is concerned is the original GNU Library General Public License
* Version 2.1, February 1999
*/
package org.digidoc4j.impl.bdoc.xades;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.codec.binary.Base64;
import org.apache.xml.security.signature.Reference;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.digidoc4j.SignatureProfile;
import org.digidoc4j.X509Cert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.validation.SignatureProductionPlace;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.XPathQueryHolder;
import eu.europa.esig.dss.xades.validation.XAdESSignature;
public class BesSignature extends DssXadesSignature {
private final static Logger logger = LoggerFactory.getLogger(BesSignature.class);
private SignatureProductionPlace signerLocation;
private Element signatureElement;
private XPathQueryHolder xPathQueryHolder; // This variable contains the XPathQueryHolder adapted to the signature schema.
private X509Cert signingCertificate;
private Set<CertificateToken> encapsulatedCertificates;
public BesSignature(XadesValidationReportGenerator xadesReportGenerator) {
super(xadesReportGenerator);
XAdESSignature dssSignature = xadesReportGenerator.openDssSignature();
this.signatureElement = dssSignature.getSignatureElement();
this.xPathQueryHolder = dssSignature.getXPathQueryHolder();
logger.debug("Using xpath query holder: " + xPathQueryHolder.getClass());
}
@Override
public String getId() {
return getDssSignature().getId();
}
@Override
public String getSignatureMethod() {
return getDssSignature().getDigestAlgorithm().getXmlId();
}
@Override
public Date getSigningTime() {
return getDssSignature().getSigningTime();
}
@Override
public String getCity() {
return getSignerLocation() == null ? null : getSignerLocation().getCity();
}
@Override
public String getStateOrProvince() {
return getSignerLocation() == null ? null : getSignerLocation().getStateOrProvince();
}
@Override
public String getPostalCode() {
return getSignerLocation() == null ? null : getSignerLocation().getPostalCode();
}
@Override
public String getCountryName() {
return getSignerLocation() == null ? null : getSignerLocation().getCountryName();
}
@Override
public List<String> getSignerRoles() {
String[] claimedSignerRoles = getDssSignature().getClaimedSignerRoles();
return claimedSignerRoles == null ? Collections.<String>emptyList() : Arrays.asList(claimedSignerRoles);
}
@Override
public X509Cert getSigningCertificate() {
if (signingCertificate != null) {
return signingCertificate;
}
CertificateToken keyInfoCertificate = findKeyInfoCertificate();
if (keyInfoCertificate == null) {
logger.warn("Signing certificate not found");
return null;
}
X509Certificate certificate = keyInfoCertificate.getCertificate();
signingCertificate = new X509Cert(certificate);
return signingCertificate;
}
@Override
public SignatureProfile getProfile() {
return SignatureProfile.B_BES;
}
@Override
public byte[] getSignatureValue() {
logger.debug("Getting signature value");
Element signatureValueElement = getDssSignature().getSignatureValue();
String textContent = signatureValueElement.getTextContent();
return Base64.decodeBase64(textContent);
}
/**
* B_BES signature does not contain OCSP response time or Timestamp to provide trusted signing time.
*
* @return null
*/
@Override
public Date getTrustedSigningTime() {
logger.info("B_BES signature does not contain OCSP response time or Timestamp to provide trusted signing time");
return null;
}
/**
* B_BES signature does not contain OCSP response
*
* @return null
*/
@Override
public Date getOCSPResponseCreationTime() {
logger.info("The signature does not contain OCSP response");
return null;
}
/**
* B_BES signature does not contain OCSP response
*
* @return null
*/
@Override
public X509Cert getOCSPCertificate() {
logger.info("The signature does not contain OCSP response");
return null;
}
/**
* B_BES signature does not contain OCSP response
*
* @return null
*/
@Override
public List<BasicOCSPResp> getOcspResponses() {
logger.info("The signature does not contain OCSP response");
return Collections.emptyList();
}
/**
* B_BES signature does not contain Timestamp
*
* @return null
*/
@Override
public Date getTimeStampCreationTime() {
logger.info("The signature does not contain Timestamp");
return null;
}
/**
* B_BES signature does not contain Timestamp
*
* @return null
*/
@Override
public X509Cert getTimeStampTokenCertificate() {
logger.info("The signature does not contain Timestamp");
return null;
}
@Override
public List<Reference> getReferences() {
return getDssSignature().getReferences();
}
protected Element getSignatureElement() {
return signatureElement;
}
protected XPathQueryHolder getxPathQueryHolder() {
return xPathQueryHolder;
}
protected Set<CertificateToken> getEncapsulatedCertificates() {
if (encapsulatedCertificates == null) {
logger.debug("Finding encapsulated certificates");
encapsulatedCertificates = findCertificates(xPathQueryHolder.XPATH_ENCAPSULATED_X509_CERTIFICATE);
logger.debug("Found " + encapsulatedCertificates.size() + " encapsulated certificates");
}
return encapsulatedCertificates;
}
private CertificateToken findKeyInfoCertificate() {
logger.debug("Finding key info certificate");
Set<CertificateToken> keyInfoCertificates = findCertificates(xPathQueryHolder.XPATH_KEY_INFO_X509_CERTIFICATE);
if (keyInfoCertificates.isEmpty()) {
logger.debug("Signing certificate not found");
return null;
}
if (keyInfoCertificates.size() > 1) {
logger.warn("Found more than one signing certificate in the key info block: " + keyInfoCertificates.size());
}
return keyInfoCertificates.iterator().next();
}
protected Set<CertificateToken> findCertificates(String xPath) {
Set<CertificateToken> certificates = new HashSet<>();
NodeList nodeList = DSSXMLUtils.getNodeList(signatureElement, xPath);
for (int i = 0; i < nodeList.getLength(); i++) {
Element certificateElement = (Element) nodeList.item(i);
CertificateToken certToken = createCertificateToken(certificateElement);
if (!certificates.contains(certToken)) {
String idIdentifier = DSSXMLUtils.getIDIdentifier(certificateElement);
certToken.setXmlId(idIdentifier);
certificates.add(certToken);
}
}
return certificates;
}
private CertificateToken createCertificateToken(Element certificateElement) {
byte[] derEncoded = Base64.decodeBase64(certificateElement.getTextContent());
return DSSUtils.loadCertificate(derEncoded);
}
private SignatureProductionPlace getSignerLocation() {
if (signerLocation == null) {
logger.debug("Getting signature production place");
signerLocation = getDssSignature().getSignatureProductionPlace();
}
return signerLocation;
}
}