be.fedict.eid.applet.service.signer.facets.XAdESXLSignatureFacet.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.applet.service.signer.facets.XAdESXLSignatureFacet.java

Source

/*
 * eID Applet Project.
 * Copyright (C) 2010 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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 software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.applet.service.signer.facets;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.transform.TransformerException;

import be.fedict.eid.applet.service.signer.DigestAlgo;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.utils.Constants;
import org.apache.xpath.XPathAPI;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.ResponderID;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.OCSPException;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.RespID;
import org.joda.time.DateTime;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import be.fedict.eid.applet.service.signer.SignatureFacet;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CRLIdentifierType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CRLRefType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CRLRefsType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CRLValuesType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CertIDListType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CertIDType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CertificateValuesType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CompleteCertificateRefsType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.CompleteRevocationRefsType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.DigestAlgAndValueType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.EncapsulatedPKIDataType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.OCSPIdentifierType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.OCSPRefType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.OCSPRefsType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.OCSPValuesType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.ObjectFactory;
import be.fedict.eid.applet.service.signer.jaxb.xades132.ResponderIDType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.RevocationValuesType;
import be.fedict.eid.applet.service.signer.jaxb.xades132.XAdESTimeStampType;
import be.fedict.eid.applet.service.signer.jaxb.xades141.ValidationDataType;
import be.fedict.eid.applet.service.signer.jaxb.xmldsig.CanonicalizationMethodType;
import be.fedict.eid.applet.service.signer.time.TimeStampService;

/**
 * XAdES-X-L v1.4.1 signature facet. This signature facet implementation will
 * upgrade a given XAdES-BES/EPES signature to XAdES-X-L.
 * 
 * We don't inherit from XAdESSignatureFacet as we also want to be able to use
 * this facet out of the context of a signature creation. This signature facet
 * assumes that the signature is already XAdES-BES/EPES compliant.
 * 
 * This implementation has been tested against an implementation that
 * participated multiple ETSI XAdES plugtests.
 * 
 * @author Frank Cornelis
 * @see XAdESSignatureFacet
 */
public class XAdESXLSignatureFacet implements SignatureFacet {

    private static final Log LOG = LogFactory.getLog(XAdESXLSignatureFacet.class);

    public static final String XADES_NAMESPACE = "http://uri.etsi.org/01903/v1.3.2#";

    public static final String XADES141_NAMESPACE = "http://uri.etsi.org/01903/v1.4.1#";

    private Element nsElement;

    private final ObjectFactory objectFactory;

    private final be.fedict.eid.applet.service.signer.jaxb.xades141.ObjectFactory xades141ObjectFactory;

    private final be.fedict.eid.applet.service.signer.jaxb.xmldsig.ObjectFactory xmldsigObjectFactory;

    private final TimeStampService timeStampService;

    private String c14nAlgoId;

    private final Marshaller marshaller;

    private final RevocationDataService revocationDataService;

    private final CertificateFactory certificateFactory;

    private final DatatypeFactory datatypeFactory;

    private final DigestAlgo digestAlgorithm;

    static {
        Init.init();
    }

    /**
     * Convenience constructor.
     * 
     * @param timeStampService
     *            the time-stamp service used for XAdES-T and XAdES-X.
     * @param revocationDataService
     *            the optional revocation data service used for XAdES-C and
     *            XAdES-X-L. When <code>null</code> the signature will be
     *            limited to XAdES-T only.
     */
    public XAdESXLSignatureFacet(TimeStampService timeStampService, RevocationDataService revocationDataService) {
        this(timeStampService, revocationDataService, DigestAlgo.SHA1);
    }

    /**
     * Main constructor.
     * 
     * @param timeStampService
     *            the time-stamp service used for XAdES-T and XAdES-X.
     * @param revocationDataService
     *            the optional revocation data service used for XAdES-C and
     *            XAdES-X-L. When <code>null</code> the signature will be
     *            limited to XAdES-T only.
     * @param digestAlgorithm
     *            the digest algorithm to be used for construction of the
     *            XAdES-X-L elements.
     */
    public XAdESXLSignatureFacet(TimeStampService timeStampService, RevocationDataService revocationDataService,
            DigestAlgo digestAlgorithm) {
        this.objectFactory = new ObjectFactory();
        this.c14nAlgoId = CanonicalizationMethod.EXCLUSIVE;
        this.digestAlgorithm = digestAlgorithm;
        this.timeStampService = timeStampService;
        this.revocationDataService = revocationDataService;
        this.xmldsigObjectFactory = new be.fedict.eid.applet.service.signer.jaxb.xmldsig.ObjectFactory();
        this.xades141ObjectFactory = new be.fedict.eid.applet.service.signer.jaxb.xades141.ObjectFactory();

        try {
            JAXBContext context = JAXBContext
                    .newInstance(be.fedict.eid.applet.service.signer.jaxb.xades141.ObjectFactory.class);
            this.marshaller = context.createMarshaller();
            this.marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            this.marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new XAdESNamespacePrefixMapper());
        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);
        }

        try {
            this.certificateFactory = CertificateFactory.getInstance("X.509");
        } catch (CertificateException e) {
            throw new RuntimeException("X509 JCA error: " + e.getMessage(), e);
        }

        try {
            this.datatypeFactory = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException e) {
            throw new RuntimeException("datatype config error: " + e.getMessage(), e);
        }
    }

    public void setCanonicalizerAlgorithm(String c14nAlgoId) {
        this.c14nAlgoId = c14nAlgoId;
    }

    private Node findSingleNode(Node baseNode, String xpathExpression) {
        if (null == this.nsElement) {
            this.nsElement = createNamespaceElement(baseNode);
        }
        return findSingleNode(baseNode, xpathExpression, this.nsElement);
    }

    public static Node findSingleNode(Node baseNode, String xpathExpression, Element nsElement) {
        try {
            Node node = XPathAPI.selectSingleNode(baseNode, xpathExpression, nsElement);
            return node;
        } catch (TransformerException e) {
            throw new RuntimeException("XPath error: " + e.getMessage(), e);
        }
    }

    public static NodeList getNodes(Node baseNode, String xpathExpression, Element nsElement) {
        try {
            NodeList nodeList = XPathAPI.selectNodeList(baseNode, xpathExpression, nsElement);
            return nodeList;
        } catch (TransformerException e) {
            throw new RuntimeException("XPath error: " + e.getMessage(), e);
        }
    }

    public void postSign(Element signatureElement, List<X509Certificate> signingCertificateChain) {
        LOG.debug("XAdES-X-L post sign phase");
        for (X509Certificate xCert : signingCertificateChain) {
            LOG.debug("Cert chain: " + xCert.getSubjectX500Principal());
        }

        // check for XAdES-BES
        Element qualifyingPropertiesElement = (Element) findSingleNode(signatureElement,
                "ds:Object/xades:QualifyingProperties");
        if (null == qualifyingPropertiesElement) {
            throw new IllegalArgumentException("no XAdES-BES extension present");
        }

        // create basic XML container structure
        Document document = signatureElement.getOwnerDocument();
        String xadesNamespacePrefix;
        if (null != qualifyingPropertiesElement.getPrefix()) {
            xadesNamespacePrefix = qualifyingPropertiesElement.getPrefix() + ":";
        } else {
            xadesNamespacePrefix = "";
        }
        Element unsignedPropertiesElement = (Element) findSingleNode(qualifyingPropertiesElement,
                "xades:UnsignedProperties");
        if (null == unsignedPropertiesElement) {
            unsignedPropertiesElement = document.createElementNS(XADES_NAMESPACE,
                    xadesNamespacePrefix + "UnsignedProperties");
            qualifyingPropertiesElement.appendChild(unsignedPropertiesElement);
        }
        Element unsignedSignaturePropertiesElement = (Element) findSingleNode(unsignedPropertiesElement,
                "xades:UnsignedSignatureProperties");
        if (null == unsignedSignaturePropertiesElement) {
            unsignedSignaturePropertiesElement = document.createElementNS(XADES_NAMESPACE,
                    xadesNamespacePrefix + "UnsignedSignatureProperties");
            unsignedPropertiesElement.appendChild(unsignedSignaturePropertiesElement);
        }

        // create the XAdES-T time-stamp
        Node signatureValueNode = findSingleNode(signatureElement, "ds:SignatureValue");
        RevocationData tsaRevocationDataXadesT = new RevocationData();
        LOG.debug("creating XAdES-T time-stamp");
        XAdESTimeStampType signatureTimeStamp = createXAdESTimeStamp(Collections.singletonList(signatureValueNode),
                tsaRevocationDataXadesT, this.c14nAlgoId, this.timeStampService, this.objectFactory,
                this.xmldsigObjectFactory);

        // marshal the XAdES-T extension
        try {
            this.marshaller.marshal(this.objectFactory.createSignatureTimeStamp(signatureTimeStamp),
                    unsignedSignaturePropertiesElement);
        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);
        }

        // xadesv141::TimeStampValidationData
        if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
            ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
            try {
                this.marshaller.marshal(this.xades141ObjectFactory.createTimeStampValidationData(validationData),
                        unsignedSignaturePropertiesElement);
            } catch (JAXBException e) {
                throw new RuntimeException("JAXB error: " + e.getMessage(), e);
            }
        }

        if (null == this.revocationDataService) {
            /*
             * Without revocation data service we cannot construct the XAdES-C
             * extension.
             */
            return;
        }

        // XAdES-C: complete certificate refs
        CompleteCertificateRefsType completeCertificateRefs = this.objectFactory
                .createCompleteCertificateRefsType();
        CertIDListType certIdList = this.objectFactory.createCertIDListType();
        completeCertificateRefs.setCertRefs(certIdList);
        List<CertIDType> certIds = certIdList.getCert();
        for (int certIdx = 1; certIdx < signingCertificateChain.size(); certIdx++) {
            /*
             * We skip the signing certificate itself according to section
             * 4.4.3.2 of the XAdES 1.4.1 specification.
             */
            X509Certificate certificate = signingCertificateChain.get(certIdx);
            CertIDType certId = XAdESSignatureFacet.getCertID(certificate, this.objectFactory,
                    this.xmldsigObjectFactory, this.digestAlgorithm, false);
            certIds.add(certId);
        }

        // XAdES-C: complete revocation refs
        CompleteRevocationRefsType completeRevocationRefs = this.objectFactory.createCompleteRevocationRefsType();
        RevocationData revocationData = this.revocationDataService.getRevocationData(signingCertificateChain);
        if (revocationData.hasCRLs()) {
            CRLRefsType crlRefs = this.objectFactory.createCRLRefsType();
            completeRevocationRefs.setCRLRefs(crlRefs);
            List<CRLRefType> crlRefList = crlRefs.getCRLRef();

            List<byte[]> crls = revocationData.getCRLs();
            for (byte[] encodedCrl : crls) {
                CRLRefType crlRef = this.objectFactory.createCRLRefType();
                crlRefList.add(crlRef);
                X509CRL crl;
                try {
                    crl = (X509CRL) this.certificateFactory.generateCRL(new ByteArrayInputStream(encodedCrl));
                } catch (CRLException e) {
                    throw new RuntimeException("CRL parse error: " + e.getMessage(), e);
                }

                CRLIdentifierType crlIdentifier = this.objectFactory.createCRLIdentifierType();
                crlRef.setCRLIdentifier(crlIdentifier);
                String issuerName;
                try {
                    issuerName = PrincipalUtil.getIssuerX509Principal(crl).getName().replace(",", ", ");
                } catch (CRLException e) {
                    throw new RuntimeException("CRL encoding error: " + e.getMessage(), e);
                }
                crlIdentifier.setIssuer(issuerName);
                crlIdentifier.setIssueTime(this.datatypeFactory
                        .newXMLGregorianCalendar(new DateTime(crl.getThisUpdate()).toGregorianCalendar()));
                crlIdentifier.setNumber(getCrlNumber(crl));

                DigestAlgAndValueType digestAlgAndValue = XAdESSignatureFacet.getDigestAlgAndValue(encodedCrl,
                        this.objectFactory, this.xmldsigObjectFactory, this.digestAlgorithm);
                crlRef.setDigestAlgAndValue(digestAlgAndValue);
            }
        }
        if (revocationData.hasOCSPs()) {
            OCSPRefsType ocspRefs = this.objectFactory.createOCSPRefsType();
            completeRevocationRefs.setOCSPRefs(ocspRefs);
            List<OCSPRefType> ocspRefList = ocspRefs.getOCSPRef();
            List<byte[]> ocsps = revocationData.getOCSPs();
            for (byte[] ocsp : ocsps) {
                OCSPRefType ocspRef = this.objectFactory.createOCSPRefType();
                ocspRefList.add(ocspRef);

                DigestAlgAndValueType digestAlgAndValue = XAdESSignatureFacet.getDigestAlgAndValue(ocsp,
                        this.objectFactory, this.xmldsigObjectFactory, this.digestAlgorithm);
                ocspRef.setDigestAlgAndValue(digestAlgAndValue);

                OCSPIdentifierType ocspIdentifier = this.objectFactory.createOCSPIdentifierType();
                ocspRef.setOCSPIdentifier(ocspIdentifier);
                OCSPResp ocspResp;
                try {
                    ocspResp = new OCSPResp(ocsp);
                } catch (IOException e) {
                    throw new RuntimeException("OCSP decoding error: " + e.getMessage(), e);
                }
                Object ocspResponseObject;
                try {
                    ocspResponseObject = ocspResp.getResponseObject();
                } catch (OCSPException e) {
                    throw new RuntimeException("OCSP error: " + e.getMessage(), e);
                }
                BasicOCSPResp basicOcspResp = (BasicOCSPResp) ocspResponseObject;
                Date producedAt = basicOcspResp.getProducedAt();
                ocspIdentifier.setProducedAt(this.datatypeFactory
                        .newXMLGregorianCalendar(new DateTime(producedAt).toGregorianCalendar()));

                ResponderIDType responderId = this.objectFactory.createResponderIDType();
                ocspIdentifier.setResponderID(responderId);
                RespID respId = basicOcspResp.getResponderId();
                ResponderID ocspResponderId = respId.toASN1Object();
                DERTaggedObject derTaggedObject = (DERTaggedObject) ocspResponderId.toASN1Object();
                if (2 == derTaggedObject.getTagNo()) {
                    ASN1OctetString keyHashOctetString = (ASN1OctetString) derTaggedObject.getObject();
                    responderId.setByKey(keyHashOctetString.getOctets());
                } else {
                    X509Name name = X509Name.getInstance(derTaggedObject.getObject());
                    responderId.setByName(name.toString());
                }
            }
        }

        // marshal XAdES-C
        NodeList unsignedSignaturePropertiesNodeList = ((Element) qualifyingPropertiesElement)
                .getElementsByTagNameNS(XADES_NAMESPACE, "UnsignedSignatureProperties");
        Node unsignedSignaturePropertiesNode = unsignedSignaturePropertiesNodeList.item(0);
        try {
            this.marshaller.marshal(this.objectFactory.createCompleteCertificateRefs(completeCertificateRefs),
                    unsignedSignaturePropertiesNode);
            this.marshaller.marshal(this.objectFactory.createCompleteRevocationRefs(completeRevocationRefs),
                    unsignedSignaturePropertiesNode);
        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);
        }

        // XAdES-X Type 1 timestamp
        List<Node> timeStampNodesXadesX1 = new LinkedList<Node>();
        timeStampNodesXadesX1.add(signatureValueNode);
        Node signatureTimeStampNode = findSingleNode(unsignedSignaturePropertiesNode, "xades:SignatureTimeStamp");
        timeStampNodesXadesX1.add(signatureTimeStampNode);
        Node completeCertificateRefsNode = findSingleNode(unsignedSignaturePropertiesNode,
                "xades:CompleteCertificateRefs");
        timeStampNodesXadesX1.add(completeCertificateRefsNode);
        Node completeRevocationRefsNode = findSingleNode(unsignedSignaturePropertiesNode,
                "xades:CompleteRevocationRefs");
        timeStampNodesXadesX1.add(completeRevocationRefsNode);

        RevocationData tsaRevocationDataXadesX1 = new RevocationData();
        LOG.debug("creating XAdES-X time-stamp");
        XAdESTimeStampType timeStampXadesX1 = createXAdESTimeStamp(timeStampNodesXadesX1, tsaRevocationDataXadesX1,
                this.c14nAlgoId, this.timeStampService, this.objectFactory, this.xmldsigObjectFactory);
        ValidationDataType timeStampXadesX1ValidationData;
        if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
            timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
        } else {
            timeStampXadesX1ValidationData = null;
        }

        // marshal XAdES-X
        try {
            this.marshaller.marshal(this.objectFactory.createSigAndRefsTimeStamp(timeStampXadesX1),
                    unsignedSignaturePropertiesNode);
            if (null != timeStampXadesX1ValidationData) {
                this.marshaller.marshal(
                        this.xades141ObjectFactory.createTimeStampValidationData(timeStampXadesX1ValidationData),
                        unsignedSignaturePropertiesNode);
            }
        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);
        }

        // XAdES-X-L
        CertificateValuesType certificateValues = this.objectFactory.createCertificateValuesType();
        List<Object> certificateValuesList = certificateValues.getEncapsulatedX509CertificateOrOtherCertificate();
        for (X509Certificate certificate : signingCertificateChain) {
            EncapsulatedPKIDataType encapsulatedPKIDataType = this.objectFactory.createEncapsulatedPKIDataType();
            try {
                encapsulatedPKIDataType.setValue(certificate.getEncoded());
            } catch (CertificateEncodingException e) {
                throw new RuntimeException("certificate encoding error: " + e.getMessage(), e);
            }
            certificateValuesList.add(encapsulatedPKIDataType);
        }
        RevocationValuesType revocationValues = createRevocationValues(revocationData);

        // marshal XAdES-X-L
        try {
            this.marshaller.marshal(this.objectFactory.createCertificateValues(certificateValues),
                    unsignedSignaturePropertiesNode);
            this.marshaller.marshal(this.objectFactory.createRevocationValues(revocationValues),
                    unsignedSignaturePropertiesNode);
        } catch (JAXBException e) {
            throw new RuntimeException("JAXB error: " + e.getMessage(), e);
        }
    }

    public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) {
        byte[] c14nValue = null;
        try {
            for (Node node : nodeList) {
                /*
                 * Re-initialize the c14n else the namespaces will get cached
                 * and will be missing from the c14n resulting nodes.
                 */
                Canonicalizer c14n;
                try {
                    c14n = Canonicalizer.getInstance(c14nAlgoId);
                } catch (InvalidCanonicalizerException e) {
                    throw new RuntimeException("c14n algo error: " + e.getMessage(), e);
                }
                c14nValue = ArrayUtils.addAll(c14nValue, c14n.canonicalizeSubtree(node));
            }
        } catch (CanonicalizationException e) {
            throw new RuntimeException("c14n error: " + e.getMessage(), e);
        }
        return c14nValue;
    }

    public static Element createNamespaceElement(Node documentNode) {
        Document document = documentNode.getOwnerDocument();
        Element nsElement = document.createElement("nsElement");
        nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS);
        nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:xades", XADES_NAMESPACE);
        return nsElement;
    }

    public void preSign(XMLSignatureFactory signatureFactory, Document document, String signatureId,
            List<X509Certificate> signingCertificateChain, List<Reference> references, List<XMLObject> objects)
            throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        // nothing to do here
    }

    private BigInteger getCrlNumber(X509CRL crl) {
        byte[] crlNumberExtensionValue = crl.getExtensionValue(X509Extensions.CRLNumber.getId());
        if (null == crlNumberExtensionValue) {
            return null;
        }
        try {
            ASN1InputStream asn1InputStream = new ASN1InputStream(crlNumberExtensionValue);
            ASN1OctetString octetString = (ASN1OctetString) asn1InputStream.readObject();
            byte[] octets = octetString.getOctets();
            DERInteger integer = (DERInteger) new ASN1InputStream(octets).readObject();
            BigInteger crlNumber = integer.getPositiveValue();
            return crlNumber;
        } catch (IOException e) {
            throw new RuntimeException("I/O error: " + e.getMessage(), e);
        }
    }

    public static XAdESTimeStampType createXAdESTimeStamp(List<Node> nodeList, RevocationData revocationData,
            String c14nAlgoId, TimeStampService timeStampService, ObjectFactory objectFactory,
            be.fedict.eid.applet.service.signer.jaxb.xmldsig.ObjectFactory xmldsigObjectFactory) {
        byte[] c14nSignatureValueElement = getC14nValue(nodeList, c14nAlgoId);

        return createXAdESTimeStamp(c14nSignatureValueElement, revocationData, c14nAlgoId, timeStampService,
                objectFactory, xmldsigObjectFactory);
    }

    public static XAdESTimeStampType createXAdESTimeStamp(byte[] data, RevocationData revocationData,
            String c14nAlgoId, TimeStampService timeStampService, ObjectFactory objectFactory,
            be.fedict.eid.applet.service.signer.jaxb.xmldsig.ObjectFactory xmldsigObjectFactory) {
        // create the time-stamp
        byte[] timeStampToken;
        try {
            timeStampToken = timeStampService.timeStamp(data, revocationData);
        } catch (Exception e) {
            throw new RuntimeException("error while creating a time-stamp: " + e.getMessage(), e);
        }

        // create a XAdES time-stamp container
        XAdESTimeStampType xadesTimeStamp = objectFactory.createXAdESTimeStampType();
        CanonicalizationMethodType c14nMethod = xmldsigObjectFactory.createCanonicalizationMethodType();
        c14nMethod.setAlgorithm(c14nAlgoId);
        xadesTimeStamp.setCanonicalizationMethod(c14nMethod);
        xadesTimeStamp.setId("time-stamp-" + UUID.randomUUID().toString());

        // embed the time-stamp
        EncapsulatedPKIDataType encapsulatedTimeStamp = objectFactory.createEncapsulatedPKIDataType();
        encapsulatedTimeStamp.setValue(timeStampToken);
        encapsulatedTimeStamp.setId("time-stamp-token-" + UUID.randomUUID().toString());
        List<Object> timeStampContent = xadesTimeStamp.getEncapsulatedTimeStampOrXMLTimeStamp();
        timeStampContent.add(encapsulatedTimeStamp);

        return xadesTimeStamp;
    }

    private ValidationDataType createValidationData(RevocationData revocationData) {
        ValidationDataType validationData = this.xades141ObjectFactory.createValidationDataType();
        RevocationValuesType revocationValues = createRevocationValues(revocationData);
        validationData.setRevocationValues(revocationValues);
        return validationData;
    }

    private RevocationValuesType createRevocationValues(RevocationData revocationData) {
        RevocationValuesType revocationValues = this.objectFactory.createRevocationValuesType();
        if (revocationData.hasCRLs()) {
            CRLValuesType crlValues = this.objectFactory.createCRLValuesType();
            revocationValues.setCRLValues(crlValues);
            List<EncapsulatedPKIDataType> encapsulatedCrlValues = crlValues.getEncapsulatedCRLValue();
            List<byte[]> crls = revocationData.getCRLs();
            for (byte[] crl : crls) {
                EncapsulatedPKIDataType encapsulatedCrlValue = this.objectFactory.createEncapsulatedPKIDataType();
                encapsulatedCrlValue.setValue(crl);
                encapsulatedCrlValues.add(encapsulatedCrlValue);
            }
        }
        if (revocationData.hasOCSPs()) {
            OCSPValuesType ocspValues = this.objectFactory.createOCSPValuesType();
            revocationValues.setOCSPValues(ocspValues);
            List<EncapsulatedPKIDataType> encapsulatedOcspValues = ocspValues.getEncapsulatedOCSPValue();
            List<byte[]> ocsps = revocationData.getOCSPs();
            for (byte[] ocsp : ocsps) {
                EncapsulatedPKIDataType encapsulatedOcspValue = this.objectFactory.createEncapsulatedPKIDataType();
                encapsulatedOcspValue.setValue(ocsp);
                encapsulatedOcspValues.add(encapsulatedOcspValue);
            }
        }
        return revocationValues;
    }
}