XAdESSignatureBuilder.java
// @formatter:off
/**
* DSS - Digital Signature Services
* Copyright (C) 2015 European Commission, provided under the CEF programme
* <p>
* This file is part of the "DSS - Digital Signature Services" project.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.signature;
import eu.europa.esig.dss.enumerations.*;
import eu.europa.esig.dss.model.*;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.spi.DSSASN1Utils;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.spi.exception.IllegalInputException;
import eu.europa.esig.dss.spi.validation.CertificateVerifier;
import eu.europa.esig.dss.spi.x509.tsp.TimestampInclude;
import eu.europa.esig.dss.spi.x509.tsp.TimestampToken;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.xades.DSSObject;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.SignatureBuilder;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.dataobject.DSSDataObjectFormat;
import eu.europa.esig.dss.xades.dataobject.DataObjectFormatBuilder;
import eu.europa.esig.dss.xades.definition.xades132.XAdES132Attribute;
import eu.europa.esig.dss.xades.reference.*;
import eu.europa.esig.dss.xades.validation.XAdESAttributeIdentifier;
import eu.europa.esig.dss.xml.common.definition.DSSElement;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigAttribute;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigElement;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigPath;
import eu.europa.esig.dss.xml.utils.DomUtils;
import eu.europa.esig.dss.xml.utils.XMLCanonicalizer;
import org.apache.xml.security.transforms.Transforms;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.*;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;
/**
* This class implements all the necessary mechanisms to build each form of the XML signature.
*
*/
public abstract class XAdESSignatureBuilder extends XAdESBuilder implements SignatureBuilder {
private static final Logger LOG = LoggerFactory.getLogger(XAdESSignatureBuilder.class);
/**
* Indicates if the signature was already built. (Two steps building)
*/
protected boolean built = false;
/**
* This is the reference to the original document to sign
*/
protected DSSDocument document;
/** The canonicalization method used for KeyInfo signing */
protected String keyInfoCanonicalizationMethod;
/** The canonicalization method used for SignedInfo signing */
protected String signedInfoCanonicalizationMethod;
/** The canonicalization method used for SignedProperties signing */
protected String signedPropertiesCanonicalizationMethod;
/** The deterministic Id used for elements creation */
protected final String deterministicId;
/** This variable represents the current DOM signature object. */
protected Element signatureDom;
/** Cached KeyInfo element */
protected Element keyInfoDom;
/** Cached SignedInfo element */
protected Element signedInfoDom;
/** Cached SignatureValue element */
protected Element signatureValueDom;
/** Cached QualifyingProperties element */
protected Element qualifyingPropertiesDom;
/** Cached SignedProperties element */
protected Element signedPropertiesDom;
/** Cached SignedSignatureProperties element */
protected Element signedSignaturePropertiesDom;
/** Cached SignedDataObjectProperties element */
protected Element signedDataObjectPropertiesDom;
/** Cached UnsignedSignatureProperties element */
protected Element unsignedSignaturePropertiesDom;
/** Id-prefix for Reference element */
protected static final String REFERENCE_PREFIX = "r-";
/** Id-prefix for KeyInfo element */
protected static final String KEYINFO_PREFIX = "keyInfo-";
/** Id-prefix for SignatureValue element */
protected static final String VALUE_PREFIX = "value-";
/** Id-prefix for Signature element */
protected static final String XADES_PREFIX = "xades-";
/**
* Creates the signature according to the packaging
*
* @param params
* The set of parameters relating to the structure and process of the creation or extension of the
* electronic signature.
* @param document
* The original document to sign.
* @param certificateVerifier
* the certificate verifier with its OCSPSource,...
* @return the signature builder linked to the packaging
*/
public static XAdESSignatureBuilder getSignatureBuilder(final XAdESSignatureParameters params, final DSSDocument document,
final CertificateVerifier certificateVerifier) {
Objects.requireNonNull(params.getSignaturePackaging(), "Cannot create a SignatureBuilder. SignaturePackaging is not defined!");
switch (params.getSignaturePackaging()) {
case ENVELOPED:
return new EnvelopedSignatureBuilder(params, document, certificateVerifier);
case ENVELOPING:
return new EnvelopingSignatureBuilder(params, document, certificateVerifier);
case DETACHED:
return new DetachedSignatureBuilder(params, document, certificateVerifier);
case INTERNALLY_DETACHED:
return new InternallyDetachedSignatureBuilder(params, document, certificateVerifier);
default:
throw new DSSException("Unsupported packaging " + params.getSignaturePackaging());
}
}
/**
* The default constructor for SignatureBuilder.
*
* @param params
* The set of parameters relating to the structure and process of the creation or extension of the
* electronic signature.
* @param document
* The original document to sign.
* @param certificateVerifier
* the certificate verifier with its OCSPSource,...
*/
protected XAdESSignatureBuilder(final XAdESSignatureParameters params, final DSSDocument document, final CertificateVerifier certificateVerifier) {
super(certificateVerifier);
this.params = params;
this.document = document;
this.deterministicId = params.getDeterministicId();
setCanonicalizationMethods(params);
}
private void setCanonicalizationMethods(final XAdESSignatureParameters params) {
this.keyInfoCanonicalizationMethod = params.getKeyInfoCanonicalizationMethod();
this.signedInfoCanonicalizationMethod = params.getSignedInfoCanonicalizationMethod();
this.signedPropertiesCanonicalizationMethod = params.getSignedPropertiesCanonicalizationMethod();
}
/**
* This is the main method which is called to build the XML signature
*
* @return A byte array is returned with XML that represents the canonicalized SignedInfo segment of signature.
* This data are used to define the SignatureValue element.
* @throws DSSException
* if an error occurred
*/
public byte[] build() throws DSSException {
assertSignaturePossible();
ensureConfigurationValidity();
xadesPath = getCurrentXAdESPath();
initRootDocumentDom();
incorporateFiles();
incorporateSignatureDom();
incorporateSignedInfo();
incorporateSignatureValue();
incorporateKeyInfo();
incorporateObjects();
/**
* We create <ds:Reference> segment only now, because we need first to define the SignedProperties segment to
* calculate the digest of references.
*/
// ########################OVERRIDE_DSS#########################
// ##### Bu alan, TÜBİTAK BES uyumu için Reference #
// ##### sıralamasının düzeltildiği kritik noktadır. #
// ##### DSS orijinalinden farklı sıralama uygulanır. #
// #############################################################
//
// DSS kütüphanesinin orijinal kodunda, ds:Reference
// elemanlarının SignedInfo içindeki sıralaması TÜBİTAK
// BES formatı ile uyumsuzdur.
//
// TÜBİTAK beklentisi:
// 1. SignedProperties referansı (en başta)
// 2. Data/Object referansı (sonra)
// 3. KeyInfo referansı (en sonda)
//
// Problem: DSS orijinalinde bu sıra farklıdır ve TÜBİTAK
// doğrulayıcısı bu durumda Enveloped imzaları bile
// yanlış yorumlar. Transform ile Enveloped belirtilse
// bile, sıra kayması nedeniyle imza türü Tümleşik
// (Detached) olarak algılanır ve doğrulama başarısız olur.
//
// Çözüm: Reference sıralaması TÜBİTAK BES'e uygun olarak
// yeniden düzenlenmiştir:
// - incorporateReferenceSignedProperties() → İlk
// - incorporateReferences() → İkinci (data/object)
// - incorporateReferenceKeyInfo() → Son
//
// Bu sayede TÜBİTAK doğrulayıcısı imza türünü doğru
// şekilde tanır ve doğrulama başarılı olur.
// #############################################################
if (Utils.isArrayEmpty(params.getSignedData())) {
incorporateReferenceSignedProperties();
incorporateReferences();
incorporateReferenceKeyInfo();
}
// Preparation of SignedInfo
byte[] canonicalizedSignedInfo = XMLCanonicalizer.createInstance(signedInfoCanonicalizationMethod).canonicalize(getNodeToCanonicalize(signedInfoDom));
if (LOG.isTraceEnabled()) {
LOG.trace("Canonicalized SignedInfo --> {}", new String(canonicalizedSignedInfo));
final byte[] digest = DSSUtils.digest(DigestAlgorithm.SHA256, canonicalizedSignedInfo);
LOG.trace("Canonicalized SignedInfo SHA256 --> {}", Utils.toBase64(digest));
}
built = true;
return canonicalizedSignedInfo;
}
private void assertSignaturePossible() {
if (!DomUtils.isDOM(document)) {
return;
}
initRootDocumentDom();
final NodeList signatureNodeList = DSSXMLUtils.getAllSignaturesExceptCounterSignatures(documentDom);
if (signatureNodeList == null || signatureNodeList.getLength() == 0) {
return;
}
final Node parentSignatureNode = getParentNodeOfSignature();
final Set<Node> parentNodes = getParentNodesChain(parentSignatureNode);
for (int ii = 0; ii < signatureNodeList.getLength(); ii++) {
final Node signatureNode = signatureNodeList.item(ii);
NodeList referenceNodeList = DSSXMLUtils.getReferenceNodeList(signatureNode);
if (referenceNodeList == null || referenceNodeList.getLength() == 0) {
continue;
}
for (int jj = 0; jj < referenceNodeList.getLength(); jj++) {
final Node referenceNode = referenceNodeList.item(jj);
if (isSignatureCoveredNodeAffected(referenceNode, parentNodes)) {
assertDoesNotContainEnvelopedTransform(referenceNode);
}
}
}
}
private Set<Node> getParentNodesChain(Node node) {
final Set<Node> nodesChain = new LinkedHashSet<>();
nodesChain.add(node);
for (Node parentNode = node.getParentNode(); parentNode != null; parentNode = parentNode.getParentNode()) {
nodesChain.add(parentNode);
}
return nodesChain;
}
private boolean isSignatureCoveredNodeAffected(Node referenceNode, Set<Node> affectedNodes) {
final String id = DSSXMLUtils.getAttribute(referenceNode, XMLDSigAttribute.URI.getAttributeName());
if (id == null) {
return false;
} else if (Utils.isStringEmpty(id)) {
// covers the whole file
return true;
} else {
Node referencedNode = DomUtils.getElementById(documentDom, id);
return affectedNodes.contains(referencedNode);
}
}
private void assertDoesNotContainEnvelopedTransform(final Node referenceNode) {
NodeList transformList = DomUtils.getNodeList(referenceNode, XMLDSigPath.TRANSFORMS_TRANSFORM_PATH);
if (transformList != null && transformList.getLength() > 0) {
for (int jj = 0; jj < transformList.getLength(); jj++) {
final Element transformElement = (Element) transformList.item(jj);
String transformAlgorithm = transformElement
.getAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName());
if (Transforms.TRANSFORM_ENVELOPED_SIGNATURE.equals(transformAlgorithm)) {
throw new IllegalInputException(String.format(
"The parallel signature is not possible! The provided file contains a signature with an '%s' transform.",
Transforms.TRANSFORM_ENVELOPED_SIGNATURE));
}
}
}
}
private void ensureConfigurationValidity() {
checkSignaturePackagingValidity();
final List<DSSReference> references = params.getReferences();
if (Utils.isCollectionEmpty(references)) {
final ReferenceBuilder referenceBuilder = initReferenceBuilder();
final List<DSSReference> defaultReferences = referenceBuilder.build();
// The SignatureParameters object is updated with the default references
// in order to ensure validity on next steps
params.getContext().setReferences(defaultReferences);
} else {
final ReferenceVerifier referenceVerifier = new ReferenceVerifier(params);
referenceVerifier.checkReferencesValidity();
}
}
private ReferenceBuilder initReferenceBuilder() {
List<DSSDocument> detachedContent = Utils.isCollectionNotEmpty(params.getDetachedContents()) ?
params.getDetachedContents() : Arrays.asList(document);
final ReferenceIdProvider referenceIdProvider = new ReferenceIdProvider();
referenceIdProvider.setSignatureParameters(params);
return new ReferenceBuilder(detachedContent, params, referenceIdProvider);
}
private void checkSignaturePackagingValidity() {
if (!SignaturePackaging.ENVELOPING.equals(params.getSignaturePackaging())) {
if (params.isManifestSignature()) {
throw new IllegalArgumentException(String.format("The signature packaging %s is not compatible with manifestSignature(true) configuration!",
params.getSignaturePackaging()));
}
if (params.isEmbedXML()) {
throw new IllegalArgumentException(String.format("The signature packaging %s is not compatible with embedXML(true) configuration!",
params.getSignaturePackaging()));
}
}
}
/**
* This method is used to incorporate the provided documents within the final file
*/
protected void incorporateFiles() {
// not implemented by default
}
/**
* This method is used to instantiate a root {@code Document} DOM, when needed
*/
protected void initRootDocumentDom() {
if (documentDom == null) {
documentDom = buildRootDocumentDom();
}
}
/**
* Builds an empty {@code Document}
*
* @return {@link Document}
*/
protected Document buildRootDocumentDom() {
return DomUtils.buildDOM();
}
/**
* This method creates a new instance of Signature element.
*/
public void incorporateSignatureDom() {
signatureDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE);
DomUtils.addNamespaceAttribute(signatureDom, getXmldsigNamespace());
signatureDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), deterministicId);
final Node parentNodeOfSignature = getParentNodeOfSignature();
incorporateSignatureDom(parentNodeOfSignature);
}
/**
* Returns a parent node of the signature
*
* @return {@link Node}
*/
protected Node getParentNodeOfSignature() {
return documentDom;
}
/**
* Incorporates the signature element to the parent node
*
* @param parentNodeOfSignature {@link Node} the parent node
*/
protected void incorporateSignatureDom(Node parentNodeOfSignature) {
parentNodeOfSignature.appendChild(signatureDom);
}
/**
* This method incorporates the SignedInfo tag
*
* <pre>
* {@code
* <ds:SignedInfo>
* <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
* <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
* ...
* </ds:SignedInfo>
* }
* </pre>
*/
public void incorporateSignedInfo() {
if (Utils.isArrayNotEmpty(params.getSignedData())) {
LOG.debug("Using explicit SignedInfo from parameter");
signedInfoDom = DomUtils.buildDOM(params.getSignedData()).getDocumentElement();
signedInfoDom = (Element) documentDom.importNode(signedInfoDom, true);
signatureDom.appendChild(signedInfoDom);
return;
}
signedInfoDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNED_INFO);
signatureDom.appendChild(signedInfoDom);
incorporateCanonicalizationMethod(signedInfoDom, signedInfoCanonicalizationMethod);
final Element signatureMethod = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE_METHOD);
signedInfoDom.appendChild(signatureMethod);
final SignatureAlgorithm signatureAlgorithm = params.getSignatureAlgorithm();
final String signatureAlgorithmXMLId = signatureAlgorithm.getUri();
if (Utils.isStringBlank(signatureAlgorithmXMLId)) {
throw new UnsupportedOperationException("Unsupported signature algorithm " + signatureAlgorithm);
}
signatureMethod.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signatureAlgorithmXMLId);
}
/**
* This method created the CanonicalizationMethod tag like :
*
* <pre>
* {@code
* <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
* }
* </pre>
*
* @param parentDom
* the parent element
* @param signedInfoCanonicalizationMethod
* the canonicalization algorithm
*/
private void incorporateCanonicalizationMethod(final Element parentDom, final String signedInfoCanonicalizationMethod) {
final Element canonicalizationMethodDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.CANONICALIZATION_METHOD);
parentDom.appendChild(canonicalizationMethodDom);
canonicalizationMethodDom.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signedInfoCanonicalizationMethod);
}
/**
* This method creates the ds:Reference elements in the signature.
*/
private void incorporateReferences() {
ReferenceProcessor referenceProcessor = new ReferenceProcessor(params);
referenceProcessor.incorporateReferences(signedInfoDom, params.getReferences(), getXmldsigNamespace());
}
/**
* Creates KeyInfo tag.
* NOTE: when trust anchor baseline profile policy is defined only the certificates previous to the trust anchor are
* included.
*
* <pre>
* {@code
* <ds:KeyInfo>
* <ds:X509Data>
* <ds:X509Certificate>
* MIIB....
* </ds:X509Certificate>
* <ds:X509Certificate>
* MIIB+...
* </ds:X509Certificate>
* </ds:X509Data>
* </ds:KeyInfo>
* }
* </pre>
*
* <pre>
* {@code
* <ds:KeyInfo>
* <ds:X509Data>
* <ds:X509Certificate>
* MIIB....
* </ds:X509Certificate>
* <ds:X509Certificate>
* MIIB+...
* </ds:X509Certificate>
* </ds:X509Data>
* </ds:KeyInfo>
* }
* </pre>
*
* @throws DSSException
* if an error occurred
*/
protected void incorporateKeyInfo() throws DSSException {
if (params.getSigningCertificate() == null && params.isGenerateTBSWithoutCertificate()) {
LOG.debug("Signing certificate not available and must be added to signature DOM later");
return;
}
// <ds:KeyInfo>
final Element keyInfoElement = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.KEY_INFO);
signatureDom.appendChild(keyInfoElement);
if (params.isSignKeyInfo()) {
keyInfoElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), KEYINFO_PREFIX + deterministicId);
}
/*
* ######################## OVERRIDE_DSS #########################
* Zinciri (certificate chain) verince DSS varsayılanı olarak KeyInfo altına
* tüm zinciri eklerdi:
*
* <ds:KeyInfo>
* <ds:X509Data>
* <ds:X509Certificate> [Imzacı] </ds:X509Certificate>
* <ds:X509Certificate> [Ara] </ds:X509Certificate>
* ...
* </ds:X509Data>
* </ds:KeyInfo>
*
* Ancak Tübitak'nın XAdES uygulama kılavuzu gereği KeyInfo'da SADECE imzacı
* sertifika olmalıdır, zincir değil. DSS'de zincir başka alanlarda gerekli
* olduğu için kod zincirden geçiyor, fakat burada sadece imzacı sertifika
* eklenmektedir.
*
* Ayrıca sertifika base64 değeri TÜBİTAK standardına göre 76 karakterde
* satır sonuna gitmelidir (XadesUtil.formatWithBase64).
*
* Bu davranış OVERRIDE edilmiştir!
*/
List<CertificateToken> certificates = new ArrayList<CertificateToken>();
certificates.add(params.getSigningCertificate());
if (params.isAddX509SubjectName()) {
for (CertificateToken token : certificates) {
// <ds:X509Data>
final Element x509DataDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.X509_DATA);
keyInfoElement.appendChild(x509DataDom);
addSubjectAndCertificate(x509DataDom, token);
}
} else {
// <ds:X509Data>
final Element x509DataDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.X509_DATA);
keyInfoElement.appendChild(x509DataDom);
for (CertificateToken token : certificates) {
addCertificate(x509DataDom, token);
}
}
/*
* ######################## OVERRIDE_DSS #########################
* Tübitak XAdES uygulama kılavuzu gereği KeyInfo içinde X509Data'dan
* sonra KeyValue (RSAKeyValue) eklenmesi gerekiyor.
*
* Yapı:
* <ds:KeyInfo>
* <ds:X509Data>
* <ds:X509Certificate>...</ds:X509Certificate>
* </ds:X509Data>
* <ds:KeyValue>
* <ds:RSAKeyValue>
* <ds:Modulus>...</ds:Modulus>
* <ds:Exponent>...</ds:Exponent>
* </ds:RSAKeyValue>
* </ds:KeyValue>
* </ds:KeyInfo>
*/
addKeyValue(keyInfoElement,params.getSigningCertificate());
this.keyInfoDom = keyInfoElement;
}
private void addKeyValue(Element keyInfoElement, CertificateToken signingCertificate) {
PublicKey publicKey = signingCertificate.getPublicKey();
if ((publicKey instanceof RSAPublicKey)) {
addRSAKeyValue(keyInfoElement,signingCertificate);
}else if(publicKey instanceof ECPublicKey){
addECKeyValue(keyInfoElement,signingCertificate);
}else{
LOG.warn("Public key is not supported type, skipping KeyValue element");
}
}
/**
* EC tabanlı sertifikalar için KeyInfo altına dsig11:ECKeyValue bloğunu ekler.
* Public key'i sıkıştırılmamış noktaya çevirerek Base64 yazar ve eğri OID'sini
* varsa NamedCurve olarak ekler (TÜBİTAK XAdES gereksinimleri).
*/
private void addECKeyValue(Element keyInfoElement, CertificateToken signingCertificate) {
// <ds:KeyValue>
final String xmldsigUri = getXmldsigNamespace().getUri();
final String xmldsigPrefix = getXmldsigNamespace().getPrefix();
final Element keyValueElement = documentDom.createElementNS(xmldsigUri, xmldsigPrefix + ":KeyValue");
keyInfoElement.appendChild(keyValueElement);
Element ecKeyValue = documentDom.createElementNS("http://www.w3.org/2009/xmldsig11#", "dsig11:ECKeyValue");
ECPublicKey ecKey = (ECPublicKey)signingCertificate.getPublicKey();
Element publicKeyElem = documentDom.createElementNS("http://www.w3.org/2009/xmldsig11#", "dsig11:PublicKey");
publicKeyElem.setTextContent(XadesUtil.ecPublicKeyToUncompressedPointBase64(ecKey));
ecKeyValue.appendChild(publicKeyElem);
String oid = XadesUtil.extractNamedCurveOID(ecKey);
if (oid != null) {
Element namedCurve = documentDom.createElementNS("http://www.w3.org/2009/xmldsig11#", "dsig11:NamedCurve");
namedCurve.setTextContent("urn:oid:" + oid);
ecKeyValue.appendChild(namedCurve);
}
keyValueElement.appendChild(ecKeyValue);
}
/**
* This method creates the X509SubjectName (optional) and X509Certificate (mandatory) tags
*
* <pre>
* {@code
* <ds:X509SubjectName>...</X509SubjectName>
* <ds:X509Certificate>...</ds:X509Certificate>
* }
* </pre>
*
* @param x509DataDom
* the parent X509Data tag
* @param token
* the certificate to add
*/
private void addSubjectAndCertificate(final Element x509DataDom, final CertificateToken token) {
DomUtils.addTextElement(documentDom, x509DataDom, getXmldsigNamespace(), XMLDSigElement.X509_SUBJECT_NAME, token.getSubject().getRFC2253());
addCertificate(x509DataDom, token);
}
/**
* This method creates the X509Certificate tag which is mandatory
*
* <pre>
* {@code
* <ds:X509Certificate>...</ds:X509Certificate>
* }
* </pre>
*
* @param x509DataDom
* the parent X509Data tag
* @param token
* the certificate to add
*/
private void addCertificate(final Element x509DataDom, final CertificateToken token) {
// TÜBİTAK standart base64 (76 karakter satır sonu)
DomUtils.addTextElement(documentDom, x509DataDom, getXmldsigNamespace(), XMLDSigElement.X509_CERTIFICATE,
XadesUtil.formatWithBase64(token.getEncoded()));
}
/**
* This method creates the KeyValue tag with RSAKeyValue for Tübitak compliance
*
* <pre>
* {@code
* <ds:KeyValue>
* <ds:RSAKeyValue>
* <ds:Modulus>...</ds:Modulus>
* <ds:Exponent>...</ds:Exponent>
* </ds:RSAKeyValue>
* </ds:KeyValue>
* }
* </pre>
*
* @param keyInfoElement
* the parent KeyInfo tag
* @param token
* the certificate token to extract the public key from
*/
private void addRSAKeyValue(final Element keyInfoElement, final CertificateToken token) {
try {
PublicKey publicKey = token.getPublicKey();
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
BigInteger modulus = rsaPublicKey.getModulus();
BigInteger exponent = rsaPublicKey.getPublicExponent();
final String xmldsigUri = getXmldsigNamespace().getUri();
final String xmldsigPrefix = getXmldsigNamespace().getPrefix();
// <ds:KeyValue>
final Element keyValueElement = documentDom.createElementNS(xmldsigUri, xmldsigPrefix + ":KeyValue");
keyInfoElement.appendChild(keyValueElement);
// <ds:RSAKeyValue>
final Element rsaKeyValueElement = documentDom.createElementNS(xmldsigUri, xmldsigPrefix + ":RSAKeyValue");
keyValueElement.appendChild(rsaKeyValueElement);
// <ds:Modulus> - TÜBİTAK standart base64 (76 karakter satır sonu)
final Element modulusElement = documentDom.createElementNS(xmldsigUri, xmldsigPrefix + ":Modulus");
modulusElement.setTextContent(XadesUtil.formatWithBase64(modulus.toByteArray()));
rsaKeyValueElement.appendChild(modulusElement);
// <ds:Exponent> - Kısa değer, formatlamaya gerek yok
final Element exponentElement = documentDom.createElementNS(xmldsigUri, xmldsigPrefix + ":Exponent");
exponentElement.setTextContent(Utils.toBase64(exponent.toByteArray()));
rsaKeyValueElement.appendChild(exponentElement);
} catch (Exception e) {
LOG.error("Error adding RSAKeyValue to KeyInfo: {}", e.getMessage(), e);
throw new DSSException("Failed to add RSAKeyValue element", e);
}
}
/**
* This method incorporates the ds:Object tags
*
* <pre>
* {@code
* <ds:Object>
* ...
* </ds:Object>
* <ds:Object>
* ...
* </ds:Object>
* }
* </pre>
*
*/
protected void incorporateObjects() {
incorporateQualifyingProperties();
incorporateSignedObjects();
incorporateCustomObjects();
}
/**
* This method incorporates the ds:Object with xades:QualifyingProperties element
*
* <pre>
* {@code
* <ds:Object>
* <xades:QualifyingProperties>
* <xades:SignedProperties>
* ...
* </xades:SignedProperties>
* </xades:QualifyingProperties>
* </ds:Object>
* }
* </pre>
*
*/
protected void incorporateQualifyingProperties() {
if (Utils.isArrayNotEmpty(params.getSignedAdESObject())) {
LOG.debug("Incorporating signed XAdES Object from parameter");
if (DomUtils.isDOM(params.getSignedAdESObject())) {
Node signedObjectDom = DomUtils.buildDOM(params.getSignedAdESObject()).getDocumentElement();
signedObjectDom = documentDom.importNode(signedObjectDom, true);
signatureDom.appendChild(signedObjectDom);
} else {
throw new IllegalArgumentException("The signed AdES Object shall represent an XML!");
}
return;
}
final Element objectDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.OBJECT);
signatureDom.appendChild(objectDom);
qualifyingPropertiesDom = DomUtils.addElement(documentDom, objectDom, getXadesNamespace(), getCurrentXAdESElements().getElementQualifyingProperties());
DomUtils.addNamespaceAttribute(qualifyingPropertiesDom, getXadesNamespace());
qualifyingPropertiesDom.setAttribute(TARGET, DomUtils.toElementReference(deterministicId));
incorporateSignedProperties();
}
/**
* Incorporates the list of signed ds:Object elements (used for Enveloping packaging)
*/
protected void incorporateSignedObjects() {
// process only for enforced objects by default
final List<DSSReference> references = params.getReferences();
for (final DSSReference reference : references) {
if (reference.getObject() != null) {
incorporateObject(reference.getObject());
}
}
}
/**
* Incorporates a list of custom ds:Object elements within the ds:Signature element
*/
protected void incorporateCustomObjects() {
if (Utils.isCollectionNotEmpty(params.getObjects())) {
for (DSSObject object : params.getObjects()) {
incorporateObject(object);
}
}
}
/**
* Incorporates the given {@code object} within the ds:Signature
*
* @param object {@link DSSObject} to incorporate
*/
protected void incorporateObject(DSSObject object) {
if (object.getContent() == null) {
throw new IllegalArgumentException("The content shall be defined inside DSSObject element! " +
"Incorporation is not possible.");
}
// incorporate ds:Object dom
final Element objectDom = DomUtils.addElement(documentDom, signatureDom, getXmldsigNamespace(),
XMLDSigElement.OBJECT);
// incorporate content
if (DomUtils.isDOM(object.getContent())) {
Document contentDom = DomUtils.buildDOM(object.getContent());
DomUtils.adoptChildren(objectDom, contentDom);
} else {
Node objectContentDom = documentDom.createTextNode(new String(DSSUtils.toByteArray(object.getContent())));
objectDom.appendChild(objectContentDom);
}
// incorporate Id attribute
if (Utils.isStringNotBlank(object.getId())) {
objectDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), object.getId());
}
// incorporate MimeType attribute
if (Utils.isStringNotBlank(object.getMimeType())) {
objectDom.setAttribute(XMLDSigAttribute.MIME_TYPE.getAttributeName(), object.getMimeType());
}
// incorporate Encoding attribute
if (Utils.isStringNotBlank(object.getEncodingAlgorithm())) {
objectDom.setAttribute(XMLDSigAttribute.ENCODING.getAttributeName(), object.getEncodingAlgorithm());
}
}
/**
* This method incorporates ds:References
*
* <pre>
* {@code
* <ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI=
"#xades-id-A43023AFEB149830C242377CC941360F">
* <ds:Transforms>
* <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
* </ds:Transforms>
* <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
* <ds:DigestValue>uijX/nvuu8g10ZVEklEnYatvFe8=</ds:DigestValue>
* </ds:Reference>
* }
* </pre>
*/
protected void incorporateReferenceSignedProperties() {
final Element reference = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.REFERENCE);
signedInfoDom.appendChild(reference);
reference.setAttribute(XMLDSigAttribute.TYPE.getAttributeName(), xadesPath.getSignedPropertiesUri());
reference.setAttribute(XMLDSigAttribute.URI.getAttributeName(), DomUtils.toElementReference(XADES_PREFIX + deterministicId));
final Element transforms = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORMS);
reference.appendChild(transforms);
final Element transform = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORM);
transforms.appendChild(transform);
transform.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), signedPropertiesCanonicalizationMethod);
final DigestAlgorithm digestAlgorithm = DSSXMLUtils.getReferenceDigestAlgorithmOrDefault(params);
DSSXMLUtils.incorporateDigestMethod(reference, digestAlgorithm, getXmldsigNamespace());
// This is a workaround to ensure the canonicalization is performed successfully when same namespace prefix is used within the element
if (getXmldsigNamespace().getPrefix() != null && getXmldsigNamespace().getPrefix().equals(getXadesNamespace().getPrefix())) {
signedPropertiesDom = DSSXMLUtils.ensureNamespacesDefined(documentDom, deterministicId, xadesPath.getSignedPropertiesPath());
}
final byte[] digestValue = DSSXMLUtils.getDigestOnCanonicalizedNode(getNodeToCanonicalize(signedPropertiesDom),
digestAlgorithm, signedPropertiesCanonicalizationMethod).getValue();
if (LOG.isTraceEnabled()) {
LOG.trace("Canonicalization method REF_SigProps --> {}", signedPropertiesCanonicalizationMethod);
LOG.trace("Digest on canonicalized REF_SigProps --> {}", Utils.toHex(digestValue));
}
incorporateDigestValueOfReference(reference, digestValue);
}
/**
* Method incorporates KeyInfo ds:References.
*
* <pre>
* {@code
* <ds:Reference URI="#keyInfo-id-A43023AFEB149830C242377CC941360F">
* <ds:Transforms>
* <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
* </ds:Transforms>
* <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
* <ds:DigestValue>uijX/nvuu2g10ZVEklEnYatvFe4=</ds:DigestValue>
* </ds:Reference>
* }
* </pre>
*/
protected void incorporateReferenceKeyInfo() {
if (!params.isSignKeyInfo()) {
return;
}
final Element reference = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.REFERENCE);
signedInfoDom.appendChild(reference);
reference.setAttribute(XMLDSigAttribute.ID.getAttributeName(), REFERENCE_PREFIX + KEYINFO_PREFIX + deterministicId);
reference.setAttribute(XMLDSigAttribute.URI.getAttributeName(), DomUtils.toElementReference(KEYINFO_PREFIX + deterministicId));
final Element transforms = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORMS);
reference.appendChild(transforms);
final Element transform = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.TRANSFORM);
transforms.appendChild(transform);
transform.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(), keyInfoCanonicalizationMethod);
final DigestAlgorithm digestAlgorithm = DSSXMLUtils.getReferenceDigestAlgorithmOrDefault(params);
DSSXMLUtils.incorporateDigestMethod(reference, digestAlgorithm, getXmldsigNamespace());
final byte[] digestValue = DSSXMLUtils.getDigestOnCanonicalizedNode(getNodeToCanonicalize(keyInfoDom),
digestAlgorithm, keyInfoCanonicalizationMethod).getValue();
if (LOG.isTraceEnabled()) {
LOG.trace("Canonicalization method REF_KeyInfo --> {}", keyInfoCanonicalizationMethod);
LOG.trace("Digest on canonicalized REF_KeyInfo --> {}", Utils.toHex(digestValue));
}
incorporateDigestValueOfReference(reference, digestValue);
}
/**
* Creates the ds:DigestValue DOM object for the given {@code digestValue}
*
* @param referenceDom - the parent element to append new DOM element to
* @param digestValue - digest value of the reference computed on a canonicalized content
*/
private void incorporateDigestValueOfReference(final Element referenceDom, final byte[] digestValue) {
final Element digestValueDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(),
XMLDSigElement.DIGEST_VALUE);
final String base64EncodedDigestBytes = Utils.toBase64(digestValue);
final Text textNode = documentDom.createTextNode(base64EncodedDigestBytes);
digestValueDom.appendChild(textNode);
referenceDom.appendChild(digestValueDom);
}
/**
* This method incorporates the signature value.
*/
protected void incorporateSignatureValue() {
signatureValueDom = DomUtils.createElementNS(documentDom, getXmldsigNamespace(), XMLDSigElement.SIGNATURE_VALUE);
signatureDom.appendChild(signatureValueDom);
signatureValueDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), VALUE_PREFIX + deterministicId);
}
/**
* Creates the SignedProperties DOM object element.
*
* <pre>
* {@code
* <SignedProperties Id="xades-ide5c549340079fe19f3f90f03354a5965">
* }
* </pre>
*/
protected void incorporateSignedProperties() {
signedPropertiesDom = DomUtils.addElement(documentDom, qualifyingPropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedProperties());
signedPropertiesDom.setAttribute(XMLDSigAttribute.ID.getAttributeName(), XADES_PREFIX + deterministicId);
incorporateSignedSignatureProperties();
incorporateSignedDataObjectProperties();
}
/**
* Creates the SignedSignatureProperties DOM object element.
*
* <pre>
* {@code
* <SignedSignatureProperties>
* ...
* </SignedSignatureProperties>
* }
* </pre>
*
*/
protected void incorporateSignedSignatureProperties() {
signedSignaturePropertiesDom = DomUtils.addElement(documentDom, signedPropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedSignatureProperties());
incorporateSigningTime();
incorporateSigningCertificate();
incorporatePolicy();
incorporateSignatureProductionPlace();
incorporateSignerRole();
}
/**
* Creates SignaturePolicyIdentifier DOM object:
*
* <pre>
* {@code
* <xades:SignaturePolicyIdentifier>
* <xades:SignaturePolicyId>
* <xades:SigPolicyId>
* <xades:Identifier Qualifier="OIDAsURN">urn:oid:1.3.6.1.4.1.10015.1000.3.2.1</xades:Identifier>
* </xades:SigPolicyId>
* <xades:SigPolicyHash>
* <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
* <ds:DigestValue>3Tl1oILSvOAWomdI9VeWV6IA/32eSXRUri9kPEz1IVs=</ds:DigestValue>
* </xades:SigPolicyHash>
* <xades:SigPolicyQualifiers>
* <xades:SigPolicyQualifier>
* <xades:SPURI>http://spuri.test</xades:SPURI>
* </xades:SigPolicyQualifier>
* </xades:SigPolicyQualifiers>
* </xades:SignaturePolicyId>
* </xades:SignaturePolicyIdentifier>
* }
* </pre>
*/
private void incorporatePolicy() {
final Policy signaturePolicy = params.bLevel().getSignaturePolicy();
if (signaturePolicy != null) {
final Element signaturePolicyIdentifierDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSignaturePolicyIdentifier());
final String signaturePolicyId = signaturePolicy.getId();
if (Utils.isStringEmpty(signaturePolicyId)) { // implicit
DomUtils.addElement(documentDom, signaturePolicyIdentifierDom, getXadesNamespace(),
getCurrentXAdESElements().getElementSignaturePolicyImplied());
} else { // explicit
final Element signaturePolicyIdDom = DomUtils.addElement(documentDom, signaturePolicyIdentifierDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSignaturePolicyId());
final Element sigPolicyIdDom = DomUtils.addElement(documentDom, signaturePolicyIdDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyId());
final Element identifierDom = DomUtils.addTextElement(documentDom, sigPolicyIdDom, getXadesNamespace(),
getCurrentXAdESElements().getElementIdentifier(), signaturePolicyId);
final ObjectIdentifierQualifier qualifier = signaturePolicy.getQualifier();
if (qualifier != null) {
identifierDom.setAttribute(XAdES132Attribute.QUALIFIER.getAttributeName(), qualifier.getValue());
}
final String description = signaturePolicy.getDescription();
if (Utils.isStringNotEmpty(description)) {
DomUtils.addTextElement(documentDom, sigPolicyIdDom, getXadesNamespace(),
getCurrentXAdESElements().getElementDescription(), description);
}
final String[] documentationReferences = signaturePolicy.getDocumentationReferences();
if (Utils.isArrayNotEmpty(documentationReferences)) {
incorporateDocumentationReferences(sigPolicyIdDom, documentationReferences);
}
if (signaturePolicy instanceof XmlPolicyWithTransforms) {
final XmlPolicyWithTransforms xmlPolicy = (XmlPolicyWithTransforms) signaturePolicy;
DSSXMLUtils.incorporateTransforms(signaturePolicyIdDom, xmlPolicy.getTransforms(), getXmldsigNamespace());
}
if (signaturePolicy.getDigestAlgorithm() != null && signaturePolicy.getDigestValue() != null) {
final Element sigPolicyHashDom = DomUtils.addElement(documentDom, signaturePolicyIdDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyHash());
final DigestAlgorithm digestAlgorithm = signaturePolicy.getDigestAlgorithm();
incorporateDigestMethod(sigPolicyHashDom, digestAlgorithm);
final byte[] hashValue = signaturePolicy.getDigestValue();
final String base64EncodedHashValue = Utils.toBase64(hashValue);
incorporateDigestValue(sigPolicyHashDom, base64EncodedHashValue);
}
if (signaturePolicy.isSPQualifierPresent()) {
incorporateSigPolicyQualifiers(signaturePolicyIdDom, signaturePolicy);
}
}
}
}
/**
* Creates SigPolicyQualifiers DOM object:
*
* <pre>
* {@code
* <xades:SigPolicyQualifiers>
* <xades:SigPolicyQualifier>
* <xades:SPURI>http://signinghubbeta.cloudapp.net:7777/adss/policy/sample_sig_policy_document.txt</xades:SPURI>
* </xades:SigPolicyQualifier>
* <xades:SigPolicyQualifier>
* <xades:SPUserNotice>
* <xades:ExplicitText>This is a test policy</xades:ExplicitText>
* </xades:SPUserNotice>
* </xades:SigPolicyQualifier>
* </xades:SigPolicyQualifiers>
* }
* </pre>
*/
private void incorporateSigPolicyQualifiers(Element signaturePolicyIdDom, Policy signaturePolicy) {
final Element sigPolicyQualifiers = DomUtils.addElement(documentDom, signaturePolicyIdDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifiers());
final String spUri = signaturePolicy.getSpuri();
if (Utils.isStringNotEmpty(spUri)) {
final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier());
DomUtils.addTextElement(documentDom, sigPolicyQualifier, getXadesNamespace(),
getCurrentXAdESElements().getElementSPURI(), spUri);
}
final UserNotice userNotice = signaturePolicy.getUserNotice();
if (userNotice != null && !userNotice.isEmpty()) {
DSSUtils.assertSPUserNoticeConfigurationValid(userNotice);
final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier());
final Element spUserNotice = DomUtils.addElement(documentDom, sigPolicyQualifier,
getXadesNamespace(), getCurrentXAdESElements().getElementSPUserNotice());
final String organization = userNotice.getOrganization();
final int[] noticeNumbers = userNotice.getNoticeNumbers();
if (Utils.isStringNotEmpty(organization) && noticeNumbers != null && noticeNumbers.length > 0) {
final Element noticeRef = DomUtils.addElement(documentDom, spUserNotice,
getXadesNamespace(), getCurrentXAdESElements().getElementNoticeRef());
DomUtils.addTextElement(documentDom, noticeRef, getXadesNamespace(),
getCurrentXAdESElements().getElementOrganization(), organization);
final Element noticeNumbersElement = DomUtils.addElement(documentDom, noticeRef,
getXadesNamespace(), getCurrentXAdESElements().getElementNoticeNumbers());
for (int number : noticeNumbers) {
DomUtils.addTextElement(documentDom, noticeNumbersElement, getXadesNamespace(),
getCurrentXAdESElements().getElementint(), String.valueOf(number));
}
}
final String explicitText = userNotice.getExplicitText();
if (Utils.isStringNotEmpty(explicitText)) {
DomUtils.addTextElement(documentDom, spUserNotice, getXadesNamespace(),
getCurrentXAdESElements().getElementExplicitText(), explicitText);
}
}
final SpDocSpecification spDocSpecification = signaturePolicy.getSpDocSpecification();
if (spDocSpecification != null && Utils.isStringNotEmpty(spDocSpecification.getId())) {
final Element sigPolicyQualifier = DomUtils.addElement(documentDom, sigPolicyQualifiers,
getXadesNamespace(), getCurrentXAdESElements().getElementSigPolicyQualifier());
incorporateSPDocSpecification(sigPolicyQualifier, spDocSpecification);
}
}
/**
* Creates SigningTime DOM object element like :
*
* <pre>
* {@code
* <SigningTime>2013-11-23T11:22:52Z</SigningTime>
* }
* </pre>
*
* <p><b>Override notu (issue #7):</b> DSS upstream
* {@link DomUtils#createXMLGregorianCalendar(Date)} kullanır ve çıktıyı
* her zaman UTC ({@code Z} sonekli) basar. TÜBİTAK MA3 referans
* çıktısında {@code SigningTime} timezone'lu yazıldığı için ve İMZAGER
* gibi yerel doğrulayıcılarda XML metnine bakan operatör için kafa
* karışıklığı yarattığı için, çıktının zaman dilimini
* {@link XAdESSigningTimeZoneHolder#getZone()} üzerinden parametrik
* hale getirdik. Default {@code +03:00}, ENV
* {@code XADES_SIGNING_TIME_ZONE} ile değiştirilebilir
* ({@code Z}/{@code UTC} dahil).</p>
*/
private void incorporateSigningTime() {
final Date signingDate = params.bLevel().getSigningDate();
// ########################OVERRIDE_DSS#########################
// DSS orijinali (UTC sabit):
// final XMLGregorianCalendar xmlGregorianCalendar = DomUtils.createXMLGregorianCalendar(signingDate);
// final String xmlSigningTime = xmlGregorianCalendar.toXMLFormat();
// TÜBİTAK uyumu için configurable timezone (default +03:00, ENV ile değiştirilebilir).
final String xmlSigningTime = XAdESSigningTimeZoneHolder.formatSigningTime(signingDate);
// #############################################################
final Element signingTimeDom = DomUtils.createElementNS(documentDom, getXadesNamespace(), getCurrentXAdESElements().getElementSigningTime());
signedSignaturePropertiesDom.appendChild(signingTimeDom);
final Text textNode = documentDom.createTextNode(xmlSigningTime);
signingTimeDom.appendChild(textNode);
}
/**
* Creates SigningCertificate(V2) building block DOM object:
*
* <pre>
* {@code
* <SigningCertificate>
* <Cert>
* <CertDigest>
* <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
* <ds:DigestValue>fj8SJujSXU4fi342bdtiKVbglA0=</ds:DigestValue>
* </CertDigest>
* <IssuerSerial>
* <ds:X509IssuerName>CN=ICA A,O=DSS,C=AA</ds:X509IssuerName>
* <ds:X509SerialNumber>4</ds:X509SerialNumber>
* </IssuerSerial>
* </Cert>
* </SigningCertificate>
* }
* </pre>
*/
private void incorporateSigningCertificate() {
if (params.getSigningCertificate() == null && params.isGenerateTBSWithoutCertificate()) {
return;
}
final Set<CertificateToken> certificates = new HashSet<>();
certificates.add(params.getSigningCertificate());
if (params.isEn319132()) {
incorporateSigningCertificateV2(certificates);
} else {
incorporateSigningCertificateV1(certificates);
}
}
private void incorporateSigningCertificateV1(Set<CertificateToken> certificates) {
Element signingCertificateDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(),
getCurrentXAdESElements().getElementSigningCertificate());
DigestAlgorithm signingCertificateDigestMethod = params.getSigningCertificateDigestMethod();
for (final CertificateToken certificate : certificates) {
incorporateCert(signingCertificateDom, certificate, signingCertificateDigestMethod);
}
}
private void incorporateSigningCertificateV2(Set<CertificateToken> certificates) {
Element signingCertificateDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(),
getCurrentXAdESElements().getElementSigningCertificateV2());
DigestAlgorithm signingCertificateDigestMethod = params.getSigningCertificateDigestMethod();
for (final CertificateToken certificate : certificates) {
incorporateCert(signingCertificateDom, certificate, signingCertificateDigestMethod);
}
}
/**
* This method incorporates the SignedDataObjectProperties DOM element like :
*
* <pre>
* {@code
* <SignedDataObjectProperties> ...
* <DataObjectFormat>
* ...
* </DataObjectFormat>
* <CommitmentTypeIndication>
* ...
* </CommitmentTypeIndication>
* <AllDataObjectsTimeStamp>
* ...
* </AllDataObjectsTimeStamp>
* <IndividualDataObjectsTimeStamp>
* ...
* </IndividualDataObjectsTimeStamp>
* </SignedDataObjectProperties>
* }
* </pre>
*/
private void incorporateSignedDataObjectProperties() {
incorporateDataObjectFormat();
incorporateCommitmentTypeIndications();
incorporateContentTimestamps();
}
private Element getSignedDataObjectPropertiesDom() {
/*
* 4.3.5 The SignedDataObjectProperties container
*
* A XAdES signature shall not incorporate an empty SignedDataObjectProperties element.
*/
if (signedDataObjectPropertiesDom == null) {
signedDataObjectPropertiesDom = DomUtils.addElement(documentDom, signedPropertiesDom, getXadesNamespace(),
getCurrentXAdESElements().getElementSignedDataObjectProperties());
}
return signedDataObjectPropertiesDom;
}
/**
* This method incorporates the SignedDataObjectProperties DOM element like :
*
* <pre>
* {@code
* <DataObjectFormat ObjectReference="#detached-ref-id">
* <MimeType>text/plain</MimeType>
* ...
* </DataObjectFormat>
* }
* </pre>
*/
private void incorporateDataObjectFormat() {
List<DSSDataObjectFormat> dataObjectFormats = params.getDataObjectFormatList();
if (dataObjectFormats == null) {
dataObjectFormats = new DataObjectFormatBuilder().setReferences(params.getReferences()).build();
if (params.isSignKeyInfo()) {
DSSDataObjectFormat keyInfoDataObjectFormat = getKeyInfoDataObjectFormat();
dataObjectFormats.add(keyInfoDataObjectFormat);
}
}
for (final DSSDataObjectFormat dataObjectFormat : dataObjectFormats) {
assertDataObjectFormatValid(dataObjectFormat);
// create xades:DataObjectFormat
final Element dataObjectFormatDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(),
getXadesNamespace(), getCurrentXAdESElements().getElementDataObjectFormat());
// add xades:DataObjectFormat/xades:Description
if (dataObjectFormat.getDescription() != null) {
final Element descriptionDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(),
getCurrentXAdESElements().getElementDescription());
DomUtils.setTextNode(documentDom, descriptionDom, dataObjectFormat.getDescription());
}
// add xades:DataObjectFormat/xades:ObjectIdentifier
if (dataObjectFormat.getObjectIdentifier() != null) {
final Element objectIdentifierDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(),
getCurrentXAdESElements().getElementObjectIdentifier());
incorporateObjectIdentifier(objectIdentifierDom, dataObjectFormat.getObjectIdentifier());
}
// add xades:DataObjectFormat/xades:MimeType
if (dataObjectFormat.getMimeType() != null) {
final Element mimeTypeDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(),
getCurrentXAdESElements().getElementMimeType());
DomUtils.setTextNode(documentDom, mimeTypeDom, dataObjectFormat.getMimeType());
}
// add xades:DataObjectFormat/xades:Encoding
if (dataObjectFormat.getEncoding() != null) {
final Element encodingDom = DomUtils.addElement(documentDom, dataObjectFormatDom, getXadesNamespace(),
getCurrentXAdESElements().getElementEncoding());
DomUtils.setTextNode(documentDom, encodingDom, dataObjectFormat.getEncoding());
}
// add xades:DataObjectFormat@ObjectReference
if (dataObjectFormat.getObjectReference() != null) {
dataObjectFormatDom.setAttribute(XAdES132Attribute.OBJECT_REFERENCE.getAttributeName(), dataObjectFormat.getObjectReference());
}
}
}
private DSSDataObjectFormat getKeyInfoDataObjectFormat() {
final DSSDataObjectFormat keyInfoDataObjectFormat = new DSSDataObjectFormat();
keyInfoDataObjectFormat.setObjectReference(DomUtils.toElementReference(REFERENCE_PREFIX + KEYINFO_PREFIX + deterministicId));
keyInfoDataObjectFormat.setMimeType(MimeTypeEnum.XML.getMimeTypeString());
return keyInfoDataObjectFormat;
}
/**
* This method verifies if the DataObjectFormat is conformant to the XAdES specification
*
* @param dataObjectFormat {@link DSSDataObjectFormat} to check
*/
private void assertDataObjectFormatValid(DSSDataObjectFormat dataObjectFormat) {
Objects.requireNonNull(dataObjectFormat, "DataObjectFormat cannot be null!");
if (dataObjectFormat.getDescription() == null && dataObjectFormat.getObjectIdentifier() == null
&& dataObjectFormat.getMimeType() == null) {
throw new IllegalArgumentException("At least one of the Description, ObjectIdentifier or MimeType " +
"shall be defined for a DataObjectFormat object!");
}
if (dataObjectFormat.getObjectReference() == null) {
throw new IllegalArgumentException("ObjectReference attribute of DataObjectFormat shall be present!");
}
if (!DomUtils.isElementReference(dataObjectFormat.getObjectReference())) {
throw new IllegalArgumentException("ObjectReference attribute of DataObjectFormat " +
"shall define a reference to an element within signature (i.e. shall begin with '#')!");
}
}
/**
* This method incorporate the content-timestamps within the signature being created.
*/
private void incorporateContentTimestamps() {
final List<TimestampToken> contentTimestamps = params.getContentTimestamps();
if (contentTimestamps == null) {
return;
}
for (final TimestampToken contentTimestamp : contentTimestamps) {
final TimestampType timeStampType = contentTimestamp.getTimeStampType();
Element timestampDom;
if (TimestampType.ALL_DATA_OBJECTS_TIMESTAMP.equals(timeStampType)) {
timestampDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(),
getXadesNamespace(), getCurrentXAdESElements().getElementAllDataObjectsTimeStamp());
} else if (TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP.equals(timeStampType)) {
timestampDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(),
getXadesNamespace(), getCurrentXAdESElements().getElementIndividualDataObjectsTimeStamp());
} else {
throw new UnsupportedOperationException("Only types ALL_DATA_OBJECTS_TIMESTAMP and INDIVIDUAL_DATA_OBJECTS_TIMESTAMP are allowed");
}
addContentTimestamp(timestampDom, contentTimestamp);
}
}
/**
* This method incorporates the signer claimed roleType into signed signature properties.
*/
private void incorporateSignerRole() {
final List<String> claimedSignerRoles = params.bLevel().getClaimedSignerRoles();
final List<String> signedAssertions = params.bLevel().getSignedAssertions();
Element signerRoleDom = null;
if (claimedSignerRoles != null) {
if (params.isEn319132()) {
signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRoleV2());
} else {
signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRole());
}
if (Utils.isCollectionNotEmpty(claimedSignerRoles)) {
final Element claimedRolesDom = DomUtils.addElement(documentDom, signerRoleDom, getXadesNamespace(), getCurrentXAdESElements().getElementClaimedRoles());
addRoles(claimedSignerRoles, claimedRolesDom, getCurrentXAdESElements().getElementClaimedRole());
}
}
if (signedAssertions != null && params.isEn319132()) {
if (signerRoleDom == null){
signerRoleDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignerRoleV2());
}
if (Utils.isCollectionNotEmpty(signedAssertions)) {
final Element signedAssertionsDom = DomUtils.addElement(documentDom, signerRoleDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedAssertions());
addAssertions(signedAssertions, signedAssertionsDom);
}
}
}
private void addRoles(final List<String> signerRoles, final Element rolesDom, final DSSElement roleType) {
for (final String signerRole : signerRoles) {
final Element roleDom = DomUtils.addElement(documentDom, rolesDom, getXadesNamespace(), roleType);
DomUtils.setTextNode(documentDom, roleDom, signerRole);
}
}
private void incorporateSignatureProductionPlace() {
final SignerLocation signatureProductionPlace = params.bLevel().getSignerLocation();
if (signatureProductionPlace != null && !signatureProductionPlace.isEmpty()) {
final Element signatureProductionPlaceDom;
if (params.isEn319132()) {
signatureProductionPlaceDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSignatureProductionPlaceV2());
} else {
signatureProductionPlaceDom = DomUtils.addElement(documentDom, signedSignaturePropertiesDom,
getXadesNamespace(), getCurrentXAdESElements().getElementSignatureProductionPlace());
}
final String city = signatureProductionPlace.getLocality();
if (city != null) {
DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(), getCurrentXAdESElements().getElementCity(), city);
}
if (params.isEn319132()) {
final String streetAddress = signatureProductionPlace.getStreetAddress();
if (streetAddress != null) {
DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(),
getCurrentXAdESElements().getElementStreetAddress(), streetAddress);
}
}
final String stateOrProvince = signatureProductionPlace.getStateOrProvince();
if (stateOrProvince != null) {
DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(),
getCurrentXAdESElements().getElementStateOrProvince(), stateOrProvince);
}
final String postalCode = signatureProductionPlace.getPostalCode();
if (postalCode != null) {
DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(),
getCurrentXAdESElements().getElementPostalCode(), postalCode);
}
final String country = signatureProductionPlace.getCountry();
if (country != null) {
DomUtils.addTextElement(documentDom, signatureProductionPlaceDom, getXadesNamespace(),
getCurrentXAdESElements().getElementCountryName(), country);
}
}
}
/**
* Below follows the schema definition for this element.
*
* <xsd:element name="CommitmentTypeIndication" type="CommitmentTypeIndicationType"/>
* <xsd:complexType name="CommitmentTypeIndicationType">
* ...<xsd:sequence>
* ......<xsd:element name="CommitmentTypeId" type="ObjectIdentifierType"/>
* ......<xsd:choice>
* .........<xsd:element name="ObjectReference" type="xsd:anyURI" maxOccurs="unbounded"/>
* .........<xsd:element name="AllSignedDataObjects"/>
* ......</xsd:choice>
* ......<xsd:element name="CommitmentTypeQualifiers" type="CommitmentTypeQualifiersListType" minOccurs="0"/>
* ...</xsd:sequence>
* </xsd:complexType>
*
* <xsd:complexType name="CommitmentTypeQualifiersListType">
* ......<xsd:sequence>
* .........<xsd:element name="CommitmentTypeQualifier"* type="AnyType" minOccurs="0" maxOccurs="unbounded"/>
* ......</xsd:sequence>
* </xsd:complexType
*/
private void incorporateCommitmentTypeIndications() {
List<CommitmentType> commitmentTypeIndications = params.bLevel().getCommitmentTypeIndications();
if (Utils.isCollectionNotEmpty(commitmentTypeIndications)) {
for (final CommitmentType commitmentTypeIndication : commitmentTypeIndications) {
assertCommitmentTypeNotNull(commitmentTypeIndication);
final Element commitmentTypeIndicationDom = DomUtils.addElement(documentDom, getSignedDataObjectPropertiesDom(),
getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeIndication());
final Element commitmentTypeIdDom = DomUtils.addElement(documentDom, commitmentTypeIndicationDom,
getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeId());
incorporateObjectIdentifier(commitmentTypeIdDom, commitmentTypeIndication);
String[] signedDataObjects = null;
CommitmentQualifier[] commitmentTypeQualifiers = null;
if (commitmentTypeIndication instanceof CommonCommitmentType) {
CommonCommitmentType commonCommitmentType = (CommonCommitmentType) commitmentTypeIndication;
signedDataObjects = commonCommitmentType.getSignedDataObjects();
commitmentTypeQualifiers = commonCommitmentType.getCommitmentTypeQualifiers();
}
if (Utils.isArrayNotEmpty(signedDataObjects)) {
// add xades:ObjectReference
for (String signedDataObjectUri : signedDataObjects) {
if (Utils.isStringBlank(signedDataObjectUri)) {
throw new IllegalArgumentException("SignedDataObject URI cannot be null!");
}
signedDataObjectUri = DomUtils.toElementReference(signedDataObjectUri);
DomUtils.addTextElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(),
getCurrentXAdESElements().getElementObjectReference(), signedDataObjectUri);
}
} else {
// add xades:AllSignedDataObjects
DomUtils.addElement(documentDom, commitmentTypeIndicationDom, getXadesNamespace(),
getCurrentXAdESElements().getElementAllSignedDataObjects());
}
// add xades:CommitmentTypeQualifiers
if (Utils.isArrayNotEmpty(commitmentTypeQualifiers)) {
final Element commitmentTypeQualifiersElement = DomUtils.addElement(documentDom, commitmentTypeIndicationDom,
getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeQualifiers());
for (CommitmentQualifier commitmentQualifier : commitmentTypeQualifiers) {
Objects.requireNonNull(commitmentQualifier, "CommitmentTypeQualifier cannot be null!");
DSSDocument content = commitmentQualifier.getContent();
if (content == null) {
throw new IllegalArgumentException("CommitmentTypeQualifier content cannot be null!");
}
final Element commitmentTypeQualifierElement = DomUtils.addElement(documentDom, commitmentTypeQualifiersElement,
getXadesNamespace(), getCurrentXAdESElements().getElementCommitmentTypeQualifier());
// incorporate content
Node objectContentDom;
if (DomUtils.isDOM(content)) {
objectContentDom = DomUtils.buildDOM(content).getDocumentElement();
objectContentDom = documentDom.importNode(objectContentDom, true);
} else {
LOG.info("None XML encoded CommitmentTypeQualifier has been provided. Incorporate as text node.");
objectContentDom = documentDom.createTextNode(new String(DSSUtils.toByteArray(content)));
}
commitmentTypeQualifierElement.appendChild(objectContentDom);
}
}
}
}
}
/**
* This method verifies if the commitment type is not null and contains at least one of the mandatory elements:
* URI or OID
*
* @param commitmentType {@link CommitmentType} to check
*/
private void assertCommitmentTypeNotNull(CommitmentType commitmentType) {
Objects.requireNonNull(commitmentType, "CommitmentType cannot be null!");
if (commitmentType.getUri() == null && commitmentType.getOid() == null) {
throw new IllegalArgumentException("The URI or OID must be defined for commitmentTypeIndication for XAdES creation!");
}
}
/**
* This method creates the xades:ObjectIdentifierType DOM object.
*
* <pre>
* {@code
* <xsd:complexType name="ObjectIdentifierType">
* <xsd:sequence>
* <xsd:element name="Identifier" type="IdentifierType"/>
* <xsd:element name="Description" type="xsd:string" minOccurs="0"/>
* <xsd:element name="DocumentationReferences" type="DocumentationReferencesType" minOccurs="0"/>
* </xsd:sequence>
* </xsd:complexType>
* }
* </pre>
*
* @param parentDom
* {@link Element} the parent element
* @param objectIdentifier
* {@link ObjectIdentifier}
*/
private void incorporateObjectIdentifier(final Element parentDom, ObjectIdentifier objectIdentifier) {
// add xades:Identifier
incorporateIdentifier(parentDom, objectIdentifier);
// add xades:Description
String description = objectIdentifier.getDescription();
if (description != null) {
DomUtils.addTextElement(documentDom, parentDom, getXadesNamespace(),
getCurrentXAdESElements().getElementDescription(), description);
}
// add xades:DocumentationReferences
String[] documentationReferences = objectIdentifier.getDocumentationReferences();
if (Utils.isArrayNotEmpty(documentationReferences)) {
incorporateDocumentationReferences(parentDom, documentationReferences);
}
}
/**
* This method creates the xades:ObjectIdentifierType DOM object.
*
* <pre>
* {@code
* <xsd:complexType name="ObjectIdentifierType">
* <xsd:sequence>
* <xsd:element name="Identifier" type="IdentifierType"/>
* <xsd:element name="Description" type="xsd:string" minOccurs="0"/>
* <xsd:element name="DocumentationReferences" type="DocumentationReferencesType" minOccurs="0"/>
* </xsd:sequence>
* </xsd:complexType>
* }
* </pre>
*
* @param parentDom
* {@link Element} the parent element
* @param objectIdentifier
* {@link ObjectIdentifier}
*/
private void incorporateIdentifier(final Element parentDom, ObjectIdentifier objectIdentifier) {
String uri = objectIdentifier.getUri();
String oid = objectIdentifier.getOid();
ObjectIdentifierQualifier qualifier = objectIdentifier.getQualifier();
if (Utils.isStringEmpty(uri)) {
if (Utils.isStringEmpty(oid)) {
throw new IllegalArgumentException("The URI or OID must be defined for XAdES IdentifierType element!");
}
if (qualifier == null) {
throw new IllegalArgumentException("When using OID as object identifier in XAdES, " +
"a Qualifier shall be provided! See EN 319 132-1 for more details.");
}
switch (qualifier) {
case OID_AS_URI:
if (DSSUtils.isUrnOid(oid)) {
throw new IllegalArgumentException(String.format(
"Qualifier '%s' shall not be used for URN encoded OID! " +
"See EN 319 132-1 for more details.", qualifier));
}
break;
case OID_AS_URN:
if (!DSSUtils.isUrnOid(oid)) {
oid = DSSUtils.toUrnOid(oid);
}
break;
default:
throw new UnsupportedOperationException(
String.format("The Qualifier '%s' is not supported!", qualifier));
}
uri = oid;
} else {
if (qualifier != null) {
throw new IllegalArgumentException("When using URI as object identifier in XAdES, " +
"a Qualifier shall not be present! See EN 319 132-1 for more details.");
}
}
// add xades:Identifier
final Element identifierDom = DomUtils.addTextElement(documentDom, parentDom,
getXadesNamespace(), getCurrentXAdESElements().getElementIdentifier(), uri);
// add xades:Identifier@Qualifier
if (qualifier != null) {
identifierDom.setAttribute(XAdES132Attribute.QUALIFIER.getAttributeName(), qualifier.getValue());
}
}
private void incorporateDocumentationReferences(Element parentElement, String[] documentationReferences) {
final Element documentReferencesDom = DomUtils.addElement(documentDom, parentElement,
getXadesNamespace(), getCurrentXAdESElements().getElementDocumentationReferences());
for (String ref : documentationReferences) {
DomUtils.addTextElement(documentDom, documentReferencesDom, getXadesNamespace(),
getCurrentXAdESElements().getElementDocumentationReference(), ref);
}
}
/**
* Adds signature value to the signature and returns XML signature (InMemoryDocument)
*
* @param signatureValue byte array
* @return {@link DSSDocument} representing the signature
*/
@Override
public DSSDocument signDocument(final byte[] signatureValue) {
if (!built) {
build();
}
final EncryptionAlgorithm encryptionAlgorithm = params.getEncryptionAlgorithm();
final byte[] signatureValueBytes;
// ########################OVERRIDE_DSS#########################
// ##### ECDSA imzalarında TÜBİTAK doğrulayıcısının beklediği #
// ##### DER dışı formatı korumak için ASN.1 dönüştürmesi #
// ##### atlanır. DSS varsayılanı bu imzalarda hata yaratır. #
// #############################################################
if (EncryptionAlgorithm.ECDSA.isEquivalent(encryptionAlgorithm)) {
signatureValueBytes = signatureValue;
} else {
signatureValueBytes = DSSASN1Utils.ensurePlainSignatureValue(encryptionAlgorithm, signatureValue);
}
final String signatureValueBase64Encoded = Utils.toBase64(signatureValueBytes);
final Text signatureValueNode = documentDom.createTextNode(signatureValueBase64Encoded);
signatureValueDom.appendChild(signatureValueNode);
return createXmlDocument();
}
/**
* Adds the content of a timestamp into a given timestamp element
*
* @param timestampElement {@link Element}
* @param token {@link TimestampToken}
*/
protected void addContentTimestamp(final Element timestampElement, final TimestampToken token) {
// List<TimestampInclude> includes, String canonicalizationMethod, TimestampToken encapsulatedTimestamp) {
// add includes: URI + referencedData = "true"
// add canonicalizationMethod: Algorithm
// add encapsulatedTimestamp: Encoding, Id, while its textContent is the base64 encoding of the data to digest
final List<TimestampInclude> includes = token.getTimestampIncludes();
if (includes != null) {
for (final TimestampInclude include : includes) {
final Element timestampIncludeElement = DomUtils.createElementNS(documentDom, getXadesNamespace(), getCurrentXAdESElements().getElementInclude());
String uri = DomUtils.toElementReference(include.getURI());
timestampIncludeElement.setAttribute(URI, uri);
timestampIncludeElement.setAttribute(REFERENCED_DATA, "true");
timestampElement.appendChild(timestampIncludeElement);
}
}
String canonicalizationMethod = token.getCanonicalizationMethod();
if (Utils.isStringNotEmpty(canonicalizationMethod)) {
final Element canonicalizationMethodElement = DomUtils.createElementNS(documentDom, getXmldsigNamespace(),
XMLDSigElement.CANONICALIZATION_METHOD);
canonicalizationMethodElement.setAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName(),
canonicalizationMethod);
timestampElement.appendChild(canonicalizationMethodElement);
} else {
throw new IllegalArgumentException("Unable to create a timestamp with empty canonicalization method. "
+ "See EN 319 132-1: 4.5 Managing canonicalization of XML nodesets.");
}
final Element encapsulatedTimestampElement = DomUtils.createElementNS(documentDom,
getXadesNamespace(), getCurrentXAdESElements().getElementEncapsulatedTimeStamp());
encapsulatedTimestampElement.setTextContent(Utils.toBase64(token.getEncoded()));
timestampElement.appendChild(encapsulatedTimestampElement);
// Build Id after time-stamp incorporation to ensure {@code timestampElement} contains a new time-stamp
final String timestampId = TIMESTAMP_PREFIX + toXmlIdentifier(XAdESAttributeIdentifier.build(timestampElement));
timestampElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), timestampId);
encapsulatedTimestampElement.setAttribute(XMLDSigAttribute.ID.getAttributeName(), ENCAPSULATED_TIMESTAMP_PREFIX + timestampId);
}
/**
* Returns a node to be canonicalized (applies indents if required)
*
* @param node {@link Node}
* @return {@link Node}
*/
protected Node getNodeToCanonicalize(Node node) {
if (params.isPrettyPrint()) {
return DSSXMLUtils.getIndentedNode(documentDom, node);
}
return node;
}
@Override
protected void alignNodes() {
if (unsignedSignaturePropertiesDom != null) {
DSSXMLUtils.alignChildrenIndents(unsignedSignaturePropertiesDom);
}
if (qualifyingPropertiesDom != null) {
DSSXMLUtils.alignChildrenIndents(qualifyingPropertiesDom);
}
}
private void addAssertions(final List<String> signedAssertions, final Element rolesDom) {
for (final String signedAssertion : signedAssertions) {
final Element roleDom = DomUtils.addElement(documentDom, rolesDom, getXadesNamespace(), getCurrentXAdESElements().getElementSignedAssertion());
Document samlAssertion = DomUtils.buildDOM(signedAssertion);
Element docEl = samlAssertion.getDocumentElement();
Node node = documentDom.importNode(docEl, true);
roleDom.appendChild(node);
}
}
}