it.trento.comune.j4sign.examples.CMSServlet.java Source code

Java tutorial

Introduction

Here is the source code for it.trento.comune.j4sign.examples.CMSServlet.java

Source

/**
 *   j4sign - an open, multi-platform digital signature solution
 *   Copyright (c) 2004 Roberto Resoli - Servizio Sistema Informativo - Comune di Trento.
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
/*
 * $Header: /cvsroot/j4sign/j4sign/src/java/core/it/trento/comune/j4sign/examples/CMSServlet.java,v 1.8 2014/08/13 12:41:49 resoli Exp $
 * $Revision: 1.8 $
 * $Date: 2014/08/13 12:41:49 $
 */
package it.trento.comune.j4sign.examples;

import it.trento.comune.j4sign.cms.ExternalSignatureCMSSignedDataGenerator;
import it.trento.comune.j4sign.cms.ExternalSignatureSignerInfoGenerator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1StreamParser;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSetParser;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTags;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.util.ASN1Dump;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @deprecated This is old code not more maintained; see the <code>SigneServlet</code> 
 * inside the <b>firma-digitale</b> web application for replacement.<br/>
 * <br/>
 * <code>CMSServlet</code> is the server side part of the j4sign usage example
 * in a web environment.<br>
 * <code>CMSServlet</code> is a <code>HttpServlet</code> that takes care of
 * generating and sending to the web client the content to sign and the
 * corresponding bytes to digest and encrypt. After receiving the signature and
 * the signer certificate, it encapsulates them, along with the signed content,
 * in a CMS signed data message.<br>
 * <p>
 * The entire example, with the
 * {@link it.trento.comune.j4sign.examples.SimpleSignApplet} counterpart, is
 * designed to permit the use of the standard JDK tools. The applet can be
 * executed with applet viewer tool (no HttpSession in the servlet, nor HTML
 * forms on the client side are used).<br>
 * <p>
 * N.B.: IN A REAL WORLD WEB APPLICATION SCENARIO, YOU CAN (AND SHOULD) TAKE
 * ADVANTAGE OF THE FULL SERVLET API.
 * <p>
 * Here are the <code>CMSServlet</code> operations in detail:
 * <ol>
 * <li>Upon a GET request from the client specifiying a <code>retrieve</code>
 * parameter with value <code>DATA</code>:<br>
 * Generates and sends as HTTP response, the message to sign (a simple text),
 * base64 encoded;</li>
 * <li>Upon a GET request from the client specifiying a <code>retrieve</code>
 * parameter with value <code>ENCODED_AUTHENTICATED_ATTRIBUTES</code>:<br>
 * Builds an {@link ExternalSignatureSignerInfoGenerator} object, using the
 * message to sign, and specifying MD5 with RSA encryption as signature
 * algoritm;<br>
 * Uses this object to calculate <code>bytesToSign</code>, the bytes to digest
 * and encrypt (ASN.1 Authenticated attributes);<br>
 * Stores the <code>ExternalSignatureSignerInfoGenerator</code>, and a textual
 * dump of authenticates attributes,<br>
 * using the md5 digest of <code>bytesToSign</code> as a key.<br>
 * Sends the base64 encoding of <code>bytesToSign</code> as HTTP response.<br>
 * </li>
 * <li>Upon a GET request from the client specifiying a <code>retrieve</code>
 * parameter with value <code>AUTHENTICATED_ATTRIBUTES_PRINTOUT</code> and a
 * <code>encodedHash</code> parameter with base64 encoded value:<br>
 * Retrieves using <code>encodedHash</code> as a key the textual dump of
 * Authenticates attributes, and sends it as HTTP responses.<br>
 * </li>
 * <li>Upon a POST request from the client specifiying a <code>signature</code>
 * parameter and a <code>certificate</code> parameter, both with base64 encoded
 * values:<br>
 * Decodes <code>signature</code> and <code>certificate</code>;<br>
 * Extracts from <code>certificate</code> the public key of the signer, and uses
 * it to decrypt, using RSA algorithm, the <code>signature</code>.<br>
 * Uses the decrypted value as a key to retrieve the
 * <code>ExternalSignatureSignerInfoGenerator</code>. If such an object is
 * found, the signature is verified.<br>
 * The <code>ExternalSignatureSignerInfoGenerator</code>, completed with
 * <code>signature</code> and <code>certificate</code> informations, is passed
 * to a
 * {@link it.trento.comune.j4sign.cms.ExternalSignatureCMSSignedDataGenerator}
 * for creating the CMS message.<br>
 * The CMS message is then stored on the server file system.<br>
 * Finally, a textual status message is returned as HTTP response.</li>
 * 
 * @author Roberto Resoli
 */
public class CMSServlet extends HttpServlet {

    /**
     * <code>DATA</code> is the sample data contet to be signed; it's a text
     * shortly explaining what is going to happen.
     */
    private final static String DATA = "This text has been retrieved from cmsservlet via http GET.\n"
            + "The non-repudiation certificate extracted from your SmartCard has been sent\n"
            + "to cmsservlet with the same request.\n"
            + "The applet has also retrived from the cmsservlet the so called 'authenticated attributes',\n"
            + "a set of data comprising:\n" + "\t- The SHA-256 digest of this text.\n"
            + "\t- The ASN.1 ContentType of this text (pkcs7.data).\n"
            + "\t- A timestamp, taken from the server clock.\n"
            + "\t- A univoque reference to your non-repudiation certificate.\n"
            + "When you will type the pin, and press return, the following will happen:\n"
            + "The 'authenticated attributes', hashed with SHA-256, will be sent\n"
            + "to the Smart Card and there encrypted using your private key.\n"
            + "The encrypted hashing of 'authenticated attributes' are then sent to cmsservlet via http POST.\n"
            + "Finally, cmsservlet builds the CMS (PKCS7) object and saves it\n"
            + "on the server filesystem, if the signature is verified.";

    /**
     * Class encapsulating a SignerInfoGenerator-related informations to be
     * stored after a signature request.
     * 
     * @author Roberto Resoli
     */
    private class SignerInfoGeneratorItem {
        private String attrPrintout = null;

        private ExternalSignatureSignerInfoGenerator sig = null;

        /**
         * Constructor.
         * 
         * @param aSig
         *            the {@link ExternalSignatureSignerInfoGenerator}
         * @param aPrintOut
         *            the authenticated attributes textual dump at the time of
         *            the request.
         */
        public SignerInfoGeneratorItem(ExternalSignatureSignerInfoGenerator aSig, String aPrintOut) {
            sig = aSig;
            attrPrintout = aPrintOut;
        }

        /**
         * The attributes printout getter.
         * 
         * @return the authenticated attributes textual dump at the time of the
         *         request.
         */
        public String getAttrPrintout() {
            return attrPrintout;
        }

        /**
         * The ExternalSignatureSignerInfoGenerator getter.
         * 
         * @return the ExternalSignatureSignerInfoGenerator for encapsulating
         *         signer informations.
         */
        public ExternalSignatureSignerInfoGenerator getSig() {
            return sig;
        }
    }

    /**
     * The repository for {@link SignerInfoGeneratorItem} objects. Stores
     * SignerInfoGenerator-related informations between HTTP requests.
     * 
     */
    private Hashtable signerInfoGeneratorTable = new Hashtable();

    /**
     * Implementation of the GET method; returns informations to the client and
     * stores SignerInfoGenerator-related informations; see {@link CMSServlet}
     * for details.
     * 
     * @see CMSServlet
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        System.out.println("==================== DO GET METHOD START=========================");

        PrintWriter out = response.getWriter();

        String retrieve = (String) request.getParameter("retrieve");
        if (retrieve != null) {
            System.out.println("Retrieving: " + retrieve);
            if (retrieve.equals("DATA"))
                out.print(DATA);
            else if (retrieve.equals("ENCODED_AUTHENTICATED_ATTRIBUTES")) {
                ExternalSignatureSignerInfoGenerator gen = buildSignerInfoGenerator();

                // Patch from Alessandro (thanks!)
                // Decode and set certificate
                sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
                byte[] certificate = decoder.decodeBuffer(request.getParameter("certificate"));
                java.security.cert.CertificateFactory cf;
                try {
                    cf = java.security.cert.CertificateFactory.getInstance("X.509");
                    java.io.ByteArrayInputStream bais1 = new java.io.ByteArrayInputStream(certificate);
                    java.security.cert.X509Certificate javaCert = (java.security.cert.X509Certificate) cf
                            .generateCertificate(bais1);

                    gen.setCertificate(javaCert);

                } catch (CertificateException e1) {
                    e1.printStackTrace();
                }
                // s.setAttribute("signerInfo", infoGen);

                byte[] bytesToSign = getAuthenticatedAttributesBytes(gen);
                String attrPrintout = getAuthenticatedAttributesPrintout(bytesToSign);
                System.out.println("Authenticated Attributes printout follows:\n" + attrPrintout);

                byte[] digestBytes = null;
                // calculate digest
                java.security.MessageDigest md;
                try {
                    md = java.security.MessageDigest.getInstance(CMSSignedDataGenerator.DIGEST_SHA256);
                    md.update(bytesToSign);
                    digestBytes = md.digest();

                } catch (NoSuchAlgorithmException e) {
                    // TODO Auto-generated catch block
                    System.out.println(e);
                }

                System.out.println("Encapsulating digest in digestInfo ...");
                byte[] digestInfoBytes = encapsulateInDigestInfo(CMSSignedDataGenerator.DIGEST_SHA256, digestBytes);
                System.out.println(formatAsString(digestInfoBytes, " "));

                String storeKey = formatAsString(digestInfoBytes, "");
                System.out.println("Saving SignerInfoGenerator with key: " + storeKey);
                System.out.println("The key is the string representation of digestInfo!");

                SignerInfoGeneratorItem s = new SignerInfoGeneratorItem(gen, attrPrintout);
                // save generator and printout
                this.signerInfoGeneratorTable.put(storeKey, s);

                System.out.println("Returning digestInfo to  client ...");
                out.print(base64Encode(digestInfoBytes));

            } else if (retrieve.equals("AUTHENTICATED_ATTRIBUTES_PRINTOUT")) {
                String base64Hash = (String) request.getParameter("encodedhash");
                if (base64Hash != null) {
                    sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
                    byte[] hash = decoder.decodeBuffer(base64Hash);

                    SignerInfoGeneratorItem s = (SignerInfoGeneratorItem) this.signerInfoGeneratorTable
                            .get(formatAsString(hash, ""));

                    out.print(s.getAttrPrintout());
                }
            } else
                out.println("Error: value '" + retrieve + "' for required parameter 'retrive' not expected.");
        } else
            out.println("Error: required parameter 'retrive' not found.");
        out.flush();
        System.out.println("==================== DO GET METHOD END=========================");
    }

    /**
     * Implementation of the POST method; builds the CMS message; see
     * {@link CMSServlet} for details.
     * 
     * @see CMSServlet
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        System.out.println("==================== DO POST METHOD START =========================");

        String base64Certificate = (String) request.getParameter("certificate");
        String base64Signature = (String) request.getParameter("signature");

        PrintWriter out = response.getWriter();
        if ((base64Certificate != null) && (base64Signature != null)) {
            sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
            byte[] sigBytes = decoder.decodeBuffer(base64Signature);
            byte[] certBytes = decoder.decodeBuffer(base64Certificate);

            String storeKey = deriveStoreKey(sigBytes, certBytes);
            ExternalSignatureSignerInfoGenerator info = retriveSignerInfoGenerator(storeKey);

            if (info != null) {
                CMSSignedData signedData = buildCMSSignedData(info, sigBytes, certBytes);
                out.print("OK-SignedData built -");
                if (signedData != null) {
                    String filePath = System.getProperty("java.io.tmpdir") + System.getProperty("file.separator")
                            + storeKey + ".txt.p7m";
                    saveFile(signedData, filePath);
                    out.print(" saved to file: '" + filePath + "'");
                } else
                    out.print("signedData not verified, file NOT saved!");

            }
        } else
            out.print("ERROR-certificate or signature not available.");
        out.flush();
        System.out.println("==================== DO POST METHOD END =========================");
    }

    /**
     * DER decoding function for digest info data.
     * 
     * @param encoding
     *            der encoded bytes
     * @return the digest as byte[].
     * @throws IOException
     *             if encoding is not a DigestInfo
     */

    /*
    private byte[] derDecode(byte[] encoding) throws IOException {
       if (encoding[0] != (DERTags.CONSTRUCTED | DERTags.SEQUENCE)) {
     throw new IOException("not a digest info object");
       }
        
       ASN1StreamParser a1p = new ASN1StreamParser(encoding);
        
       return new DigestInfo((ASN1Sequence) a1p.readObject()).getDigest();
    }
    */

    /**
     * Formats a byte[] as an hexadecimal String, interleaving bytes with a
     * separator string.
     * 
     * @param bytes
     *            the byte[] to format.
     * @param byteSeparator
     *            the string to be used to separate bytes.
     * 
     * @return the formatted string.
     */
    public String formatAsString(byte[] bytes, String byteSeparator) {
        int n, x;
        String w = new String();
        String s = new String();

        for (n = 0; n < bytes.length; n++) {
            x = (int) (0x000000FF & bytes[n]);
            w = Integer.toHexString(x).toUpperCase();
            if (w.length() == 1)
                w = "0" + w;
            s = s + w + ((n + 1 == bytes.length) ? "" : byteSeparator);
        } // for
        return s;
    }

    /**
     * Converts the provided <code>certBytes</code> in a
     * <code>java.security.cert.X509Certificate</code>, gets from it the signer
     * public key, and uses it to decrypt <code>sigBytes</code>. The decryption
     * result is returned as a formatted exadecimal string; see
     * {@link CMSServlet} for details.
     * 
     * @param sigBytes
     *            signature bytes
     * @param certBytes
     *            certificate bytes
     * @return the decryption of sigBytes using the RSA/ECB/PKCS1PADDING
     *         Algorithm.
     */
    private String deriveStoreKey(byte[] sigBytes, byte[] certBytes) {
        String key = null;
        java.security.cert.CertificateFactory cf;
        try {
            cf = java.security.cert.CertificateFactory.getInstance("X.509");
            java.io.ByteArrayInputStream bais1 = new java.io.ByteArrayInputStream(certBytes);

            java.security.cert.X509Certificate javaCert = (java.security.cert.X509Certificate) cf
                    .generateCertificate(bais1);

            PublicKey pubKey = javaCert.getPublicKey();

            try {
                System.out.println("Deriving store key from signature and certificate.");
                System.out.println("N.B.:This serves also as signature verification!");

                Cipher c = Cipher.getInstance("RSA/ECB/PKCS1PADDING", "BC");

                c.init(Cipher.DECRYPT_MODE, pubKey);

                byte[] decBytes = null;

                /*
                if (false)
                   decBytes = derDecode(c.doFinal(sigBytes));
                else
                */

                decBytes = c.doFinal(sigBytes);

                key = formatAsString(decBytes, "");

            } catch (NoSuchAlgorithmException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (NoSuchPaddingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (InvalidKeyException e2) {
                // TODO Auto-generated catch block
                e2.printStackTrace();
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BadPaddingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchProviderException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return key;
    }

    /**
     * Gets the <code>ExternalSignatureSignerInfoGenerator</code> generator
     * which originally produced the given <code>storeKey</code>.
     * 
     * @param storeKey
     * @return the {@link ExternalSignatureSignerInfoGenerator} associated with
     *         the<code>storeKey</code>
     */
    private ExternalSignatureSignerInfoGenerator retriveSignerInfoGenerator(String storeKey) {

        System.out.println("Retrieving signerInfoGenerator using key: " + storeKey);

        ExternalSignatureSignerInfoGenerator info = ((SignerInfoGeneratorItem) this.signerInfoGeneratorTable
                .get(storeKey)).getSig();

        if (info != null)
            System.out.println("Generator found. Signature is verified.");
        else
            System.out.println("Generator not found! Signature is NOT verified!");
        // remove infos from store
        this.signerInfoGeneratorTable.remove(storeKey);

        return info;
    }

    /**
     * Builds the CMS signed data message.
     * 
     * @param infoGen
     *            the {@link ExternalSignatureSignerInfoGenerator} wrapping
     *            signer informations
     * @param sigBytes
     *            the digest encrypted with signer private key.
     * @param certBytes
     *            the signer certificate.
     * @return the {@link CMSSignedData} message.
     */
    private CMSSignedData buildCMSSignedData(ExternalSignatureSignerInfoGenerator infoGen, byte[] sigBytes,
            byte[] certBytes) {

        CMSSignedData result = null;

        System.out.println("building CMSSignedData.");
        CMSProcessable msg = new CMSProcessableByteArray(DATA.getBytes());

        // questa versione del generatore  priva della classe interna per
        // la generazione delle SignerInfo, che  stata promossa a classe a
        // s.
        ExternalSignatureCMSSignedDataGenerator gen = new ExternalSignatureCMSSignedDataGenerator();

        // Conterr la lista dei certificati; come minimo dovr
        // contenere i certificati dei firmatari; opzionale, ma
        // consigliabile,
        // l'aggiunta dei certificati root per completare le catene di
        // certificazione.
        ArrayList certList = new ArrayList();

        // get Certificate
        java.security.cert.CertificateFactory cf;
        try {
            cf = java.security.cert.CertificateFactory.getInstance("X.509");

            java.io.ByteArrayInputStream bais1 = new java.io.ByteArrayInputStream(certBytes);

            java.security.cert.X509Certificate javaCert = (java.security.cert.X509Certificate) cf
                    .generateCertificate(bais1);

            infoGen.setCertificate(javaCert);
            infoGen.setSignedBytes(sigBytes);

            certList.add(javaCert);

            gen.addSignerInf(infoGen);

            if (certList.size() != 0) {

                // Per passare i certificati al generatore li si incapsula in un
                // CertStore.
                CertStore store;

                store = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");

                System.out.println("Adding certificates ... ");

                gen.addCertificatesAndCRLs(store);

                // Finalmente, si pu creare il l'oggetto CMS.
                System.out.println("Generating CMSSignedData ");
                result = gen.generate(msg, true);

            }
        } catch (CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (NoSuchAlgorithmException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (NoSuchProviderException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (CertStoreException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        } catch (CMSException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }
        return result;
    }

    /**
     * Saves a CMS signed data file on the server file system; the extension
     * should be ".p7m" according to italian rules.
     * 
     * @param s
     *            the {@link CMSSignedData} object to save.
     * @param filePath
     *            full path of the file.
     * @return true if the file was correctly saved, false otherwise.
     */
    private boolean saveFile(CMSSignedData s, String filePath) {
        try {
            System.out.println("\nSAVING FILE TO: " + filePath);

            FileOutputStream fos = new FileOutputStream(filePath);
            fos.write(s.getEncoded());
            fos.flush();
            fos.close();

            return true;
        } catch (IOException e3) {
            System.out.println("IO Error: " + e3);
            return false;
        }
    }

    /**
     * Creates a {@link ExternalSignatureSignerInfoGenerator} with a
     * <code>MD5</code> digest algorithm and <code>RSA</code> encryption
     * algorithm.
     * 
     * @return the <code>ExternalSignatureSignerInfoGenerator</code> object
     */
    private ExternalSignatureSignerInfoGenerator buildSignerInfoGenerator() {
        System.out.println("Building SignerInfoGenerator.");

        ExternalSignatureSignerInfoGenerator gen = new ExternalSignatureSignerInfoGenerator(
                CMSSignedDataGenerator.DIGEST_SHA256, CMSSignedDataGenerator.ENCRYPTION_RSA);
        return gen;

    }

    /**
     * A BASE64 encoding function, using the <code>sun.misc.BASE64Encoder</code>
     * implementation.
     * 
     * @param bytes
     *            the bytes to encode.
     * @return the <code>BASE64</code> encoding of <code>bytes</code>.
     */
    private String base64Encode(byte[] bytes) {
        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        return encoder.encode(bytes);
    }

    /**
     * Uses the provided {@link ExternalSignatureSignerInfoGenerator} for
     * calculating the authenticated attributes bytes to be digested-encrypted
     * by the signer. Note that the attributes include a timestamp, so the
     * result is time-dependent!
     * 
     * @param signerGenerator
     *            the <code>ExternalSignatureSignerInfoGenerator</code> object
     *            that does the job.
     * @return the bytes to be signed.
     */
    private byte[] getAuthenticatedAttributesBytes(ExternalSignatureSignerInfoGenerator signerGenerator) {

        System.out.println("Building AuthenticatedAttributes.");
        byte[] bytesToSign = null;
        try {
            CMSProcessable msg = new CMSProcessableByteArray(DATA.getBytes());
            bytesToSign = signerGenerator.getBytesToSign(PKCSObjectIdentifiers.data, msg, "BC");
        } catch (Exception e) {
            System.out.println(e);
        }
        return bytesToSign;
    }

    /**
     * A text message resulting from a dump of provided authenticated attributes
     * data. Shows, among other things, the embedded timestamp attribute.
     * 
     * @param bytes
     *            the ASN.1 DER set of authenticated attributes.
     * @return the attributes textual dump.
     */
    private String getAuthenticatedAttributesPrintout(byte[] bytes) {
        StringWriter printout = new StringWriter();
        PrintWriter pw = new PrintWriter(printout);
        try {

            ASN1StreamParser a1p = new ASN1StreamParser(bytes);

            System.out.println("ASN1 parser built: " + a1p);

            DERSetParser signedAttributesParser = (DERSetParser) a1p.readObject();

            System.out.println("DERSetParser object read: " + signedAttributesParser);

            ASN1Set set = ASN1Set.getInstance(signedAttributesParser.getDERObject());

            AttributeTable attr = new AttributeTable(set);

            System.out.println("Attribute table created: " + attr);

            Iterator iter = attr.toHashtable().values().iterator();

            pw.println("Listing authenticated attributes:");
            int count = 1;
            while (iter.hasNext()) {
                Attribute a = (Attribute) iter.next();

                pw.println("Attribute " + count + ":");
                if (a.getAttrType().getId().equals(CMSAttributes.signingTime.getId())) {
                    Time time = Time.getInstance(a.getAttrValues().getObjectAt(0));
                    pw.println("Authenticated time (SERVER local time): " + time.getDate());
                }
                if (a.getAttrType().getId().equals(CMSAttributes.contentType.getId())) {
                    if (CMSObjectIdentifiers.data.getId()
                            .equals(DERObjectIdentifier.getInstance(a.getAttrValues().getObjectAt(0)).getId()))
                        pw.println("Content Type: PKCS7_DATA");
                }
                if (a.getAttrType().getId().equals(CMSAttributes.messageDigest.getId())) {
                    byte[] md = DEROctetString.getInstance(a.getAttrValues().getObjectAt(0)).getOctets();
                    pw.println("Message Digest (SHA-256 hash of data content): " + formatAsString(md, " "));
                }
                if (a.getAttrType().getId().equals(PKCSObjectIdentifiers.id_aa_signingCertificateV2.getId())) {
                    pw.println("Signing Certificate V2");
                }

                pw.println("\nAttribute dump follows:");
                pw.println(ASN1Dump.dumpAsString(a) + "\n");

                count++;
            }
        } catch (Exception e) {
            System.out.println(e);
            pw.println(e);
            return null;
        }
        pw.flush();

        return printout.toString();

    }

    private byte[] encapsulateInDigestInfo(String digestAlg, byte[] digestBytes) throws IOException {

        byte[] bcDigestInfoBytes = null;
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);

        DERObjectIdentifier digestObjId = new DERObjectIdentifier(digestAlg);
        AlgorithmIdentifier algId = new AlgorithmIdentifier(digestObjId, null);
        DigestInfo dInfo = new DigestInfo(algId, digestBytes);

        dOut.writeObject(dInfo);
        return bOut.toByteArray();

    }

    /**
     * Adds BouncyCastle provider at servlet initialization time.
     */
    public void init() throws ServletException {
        System.out.println("Insert BC provider...");
        Security.insertProviderAt(new BouncyCastleProvider(), 2);
    }
}