eu.europa.ec.markt.tlmanager.core.signature.SignatureManager.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.ec.markt.tlmanager.core.signature.SignatureManager.java

Source

/*
 * DSS - Digital Signature Services
 *
 * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
 *
 * Developed by: 2011 ARHS Developments S.A. (rue Nicolas Bov 2B, L-1253 Luxembourg) http://www.arhs-developments.com
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * "DSS - Digital Signature Services" 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.
 *
 * DSS 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
 * "DSS - Digital Signature Services".  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.europa.ec.markt.tlmanager.core.signature;

import eu.europa.ec.markt.dss.DigestAlgorithm;
import eu.europa.ec.markt.dss.SignatureAlgorithm;
import eu.europa.ec.markt.dss.common.JavaPreferencesDAO;
import eu.europa.ec.markt.dss.common.SignatureTokenType;
import eu.europa.ec.markt.dss.common.UserPreferencesDAO;
import eu.europa.ec.markt.dss.signature.Document;
import eu.europa.ec.markt.dss.signature.InMemoryDocument;
import eu.europa.ec.markt.dss.signature.SignatureFormat;
import eu.europa.ec.markt.dss.signature.SignaturePackaging;
import eu.europa.ec.markt.dss.signature.SignatureParameters;
import eu.europa.ec.markt.dss.signature.token.RFC3370Pkcs11SignatureToken;
import eu.europa.ec.markt.dss.signature.token.DSSPrivateKeyEntry;
import eu.europa.ec.markt.dss.signature.token.MSCAPISignatureToken;
import eu.europa.ec.markt.dss.signature.token.PasswordInputCallback;
import eu.europa.ec.markt.dss.signature.token.RFC3370Pkcs12SignatureToken;
import eu.europa.ec.markt.dss.signature.token.SignatureTokenConnection;
import eu.europa.ec.markt.dss.signature.xades.XAdESService;
import eu.europa.ec.markt.tlmanager.core.exception.SignatureException;
import eu.europa.ec.markt.tlmanager.core.validation.ValidationLogger;
import eu.europa.ec.markt.tlmanager.util.Util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;

/**
 * SignatureManager deals with everything related to the creation of a signature for a given tsl.
 * 
 * 
 * @version $Revision: 1867 $ - $Date: 2013-04-08 13:44:56 +0200 (Mon, 08 Apr 2013) $
 */

public class SignatureManager {
    private static final Logger LOG = Logger.getLogger(SignatureManager.class.getName());

    private UserPreferencesDAO userPreferencesDAO = new JavaPreferencesDAO();
    private static final SignatureFormat SIGNATURE_FORMAT = SignatureFormat.XAdES_BES;
    private static final SignatureAlgorithm SIGNATURE_ALGO = SignatureAlgorithm.RSA;
    private static final DigestAlgorithm SIGNATURE_DIGEST = DigestAlgorithm.SHA1;

    private ValidationLogger validationLogger;
    private InMemoryDocument document;
    private SignatureTokenConnection signatureTokenConnection;
    private XAdESService xadesService;
    private SignatureTokenType provider = SignatureTokenType.PKCS11;
    private SignatureTokenType lastProvider;
    private File pkcs11Library;
    private File pkcs12File;

    private File target;
    private char[] password;
    private PasswordInputCallback pwCallback;

    private List<DSSPrivateKeyEntry> keys;
    private Certificate selectedCertificate;

    /**
     * The default constructor for SignatureManager.
     * 
     * @param validationLogger the <code>ValidationLogger</code> object
     */
    public SignatureManager(ValidationLogger validationLogger) {
        this.validationLogger = validationLogger;

        xadesService = new XAdESService();
    }

    /**
     * Initialises the <code>InMemoryDocument</code> from a provided <code>Document</code>.
     */
    public void initInMemoryDocument(org.w3c.dom.Document document) {
        if (document == null) {
            LOG.log(Level.SEVERE, ">>> Document is null!");
        }
        try {
            ByteArrayOutputStream outputDoc = new ByteArrayOutputStream();
            Result output = new StreamResult(outputDoc);
            Transformer transformer = Util.createPrettyTransformer(3);
            Source source = new DOMSource(document);
            transformer.transform(source, output);
            this.document = new InMemoryDocument(outputDoc.toByteArray());

            outputDoc.close();
        } catch (TransformerConfigurationException tce) {
            LOG.log(Level.SEVERE, ">>>" + tce.getMessage());
        } catch (TransformerFactoryConfigurationError tfce) {
            LOG.log(Level.SEVERE, ">>>" + tfce.getMessage());
        } catch (TransformerException te) {
            LOG.log(Level.SEVERE, ">>>" + te.getMessage());
        } catch (IOException ioe) {
            LOG.log(Level.SEVERE, ">>>" + ioe.getMessage());
        }
    }

    private void initializeTokenCon(SignatureTokenType provider) {
        if (signatureTokenConnection != null) {
            signatureTokenConnection.close();
            selectedCertificate = null;
        }
        if (SignatureTokenType.PKCS11.equals(provider)) {
            signatureTokenConnection = new RFC3370Pkcs11SignatureToken(pkcs11Library.getAbsolutePath(), pwCallback);
            lastProvider = SignatureTokenType.PKCS11;
        } else if (SignatureTokenType.PKCS12.equals(provider)) {
            signatureTokenConnection = new RFC3370Pkcs12SignatureToken(password, pkcs12File);
            lastProvider = SignatureTokenType.PKCS12;
        } else if (SignatureTokenType.MSCAPI.equals(provider)) {
            signatureTokenConnection = new MSCAPISignatureToken();
            lastProvider = SignatureTokenType.MSCAPI;
        }
    }

    /**
     * Retrieves the certificate from the respective source.
     * 
     * @throws SignatureException
     */
    public void retrieveCertificates() throws SignatureException {
        if (provider == null) {
            return;
        }
        if (signatureTokenConnection == null || !provider.equals(lastProvider)) { // provider was changed in ui
            initializeTokenCon(provider);
        }
        try {
            keys = signatureTokenConnection.getKeys();
        } catch (KeyStoreException kse) {
            signatureTokenConnection = null; // make sure that it is reinitialised next time!
            String msg = kse.getMessage();
            LOG.log(Level.SEVERE, ">>>Unable to get Keys: " + msg);
            throw new SignatureException(msg, kse);
        }
    }

    /**
     * Returns the matching source for the currently selected provider
     * 
     * @return the matching source file
     */
    public File getMatchingSource() {
        if (provider.equals(SignatureTokenType.PKCS11)) {
            return getPkcs11Library();
        } else if (provider.equals(SignatureTokenType.PKCS12)) {
            return getPkcs12File();
        }

        return null;
    }

    /**
     * @param provider the signature token provider
     */
    public void setProvider(SignatureTokenType provider) {
        if (provider != null) {
            userPreferencesDAO.setSignatureTokenType(provider);
        }
        this.provider = provider;
    }

    /**
     * @return the tokenType
     */
    public SignatureTokenType getProvider() {
        if (provider == null) {
            provider = userPreferencesDAO.getSignatureTokenType();
        }
        return provider;
    }

    /**
     * Gets the pkcs11 library.
     * 
     * @return the pkcs11 library.
     */
    public File getPkcs11Library() {
        if (pkcs11Library == null) {
            String pkcs11LibraryPath = userPreferencesDAO.getPKCS11LibraryPath();
            if (pkcs11LibraryPath != null && !pkcs11LibraryPath.isEmpty()) {
                pkcs11Library = new File(pkcs11LibraryPath);
            }
        }
        return pkcs11Library;
    }

    /**
     * Sets the pkcs11 library.
     * 
     * @param pkcs11LibraryPath the file
     */
    public void setPkcs11Library(File pkcs11LibraryPath) {
        if (pkcs11LibraryPath != null) {
            userPreferencesDAO.setPKCS11LibraryPath(pkcs11LibraryPath.getAbsolutePath());
        }
        this.pkcs11Library = pkcs11LibraryPath;
    }

    /**
     * Gets the pkcs12 library.
     * 
     * @return the pkcs12 library
     */
    public File getPkcs12File() {
        if (pkcs12File == null) {
            String pkcs12FilePath = userPreferencesDAO.getPKCS12FilePath();
            if (pkcs12FilePath != null && !pkcs12FilePath.isEmpty()) {
                pkcs12File = new File(pkcs12FilePath);
            }
        }
        return pkcs12File;
    }

    /**
     * Sets the pkcs12 library.
     * 
     * @param pkcs12FilePath the file
     */
    public void setPkcs12File(File pkcs12FilePath) {
        if (pkcs12FilePath != null) {
            userPreferencesDAO.setPKCS12FilePath(pkcs12FilePath.getAbsolutePath());
        }
        this.pkcs12File = pkcs12FilePath;
    }

    /**
     * Retrieves the validation message that will be displayed in the ui.
     * 
     * @return the list
     */
    public List<ValidationLogger.Message> retrieveValidationMessages() {
        return validationLogger.getValidationMessages();
    }

    /**
     * Checks if the validation logger has any errors.
     * 
     * @return true, if the validation contains errors
     */
    public boolean isValidationErroneous() {
        return validationLogger.hasErrors();
    }

    /**
     * Do the actual signing.
     * 
     * @throws IOException
     */
    public void sign() throws IOException {
        SignatureParameters parameters = new SignatureParameters();
        parameters.setSigningDate(new Date());
        DSSPrivateKeyEntry pk = determineCurrentPK();
        parameters.setSigningCertificate((X509Certificate) pk.getCertificate());
        parameters.setCertificateChain(Arrays.asList((X509Certificate[]) pk.getCertificateChain()));

        parameters.setSignatureFormat(SIGNATURE_FORMAT);
        parameters.setSignaturePackaging(SignaturePackaging.ENVELOPED);

        parameters.setClaimedSignerRole(null);

        parameters.setSignaturePolicyId(null);

        InputStream toBeSigned = xadesService.toBeSigned(document, parameters);

        byte[] signatureValue;
        try {
            signatureValue = signatureTokenConnection.sign(toBeSigned, SIGNATURE_DIGEST, pk);
            Document signedDocument = xadesService.signDocument(document, parameters, signatureValue);

            FileOutputStream output = new FileOutputStream(target);
            IOUtils.copy(signedDocument.openStream(), output);
            output.close();
        } catch (NoSuchAlgorithmException nsae) {
            LOG.log(Level.SEVERE, "No suited algorithm found for " + SIGNATURE_ALGO + " with " + SIGNATURE_DIGEST
                    + ": " + nsae.getMessage());
        }
    }

    /**
     * Extract the list of <code>Certificate</code> from the current list of <code>PrivateKeyEntry</code>
     * 
     * @return a list of certificates
     */
    public List<Certificate> getCertificates() throws SignatureException {
        if (keys == null || !provider.equals(lastProvider)) {
            retrieveCertificates();
        }
        List<Certificate> certificates = new ArrayList<Certificate>();
        for (DSSPrivateKeyEntry key : keys) {
            certificates.add(key.getCertificate());
        }

        return certificates;
    }

    private DSSPrivateKeyEntry determineCurrentPK() {
        DSSPrivateKeyEntry pk = null;
        if (keys != null && selectedCertificate != null) {
            for (DSSPrivateKeyEntry key : keys) {
                if (selectedCertificate.equals(key.getCertificate())) {
                    pk = key;
                    break;
                }
            }
        }

        return pk;
    }

    /**
     * Sets the password.
     * 
     * @param password the new password
     */
    public void setPassword(char[] password) {
        this.password = password;
    }

    /**
     * @return the password
     */
    public char[] getPassword() {
        return password;
    }

    /**
     * @param pwCallback the pwCallback to set
     */
    public void setPwCallback(PasswordInputCallback pwCallback) {
        this.pwCallback = pwCallback;
    }

    /**
     * @return the selectedCertificate
     */
    public Certificate getSelectedCertificate() {
        return selectedCertificate;
    }

    /**
     * @param selectedCertificate the selectedCertificate to set
     */
    public void setSelectedCertificate(Certificate selectedCertificate) {
        this.selectedCertificate = selectedCertificate;
    }

    /**
     * @return the document
     */
    public InMemoryDocument getDocument() {
        return document;
    }

    /**
     * @param document the document to set
     */
    public void setDocument(InMemoryDocument document) {
        this.document = document;
    }

    /**
     * @return the target
     */
    public File getTarget() {
        return target;
    }

    /**
     * @param target the target to set
     */
    public void setTarget(File target) {
        if (!target.isDirectory()) {
            this.target = target;
        }
    }

    /**
     * Returns true, if any source is set
     * 
     * @return true, if any source is set
     */
    public boolean isAnySource() {
        if (pkcs11Library == null && pkcs12File == null) {
            return false;
        }

        return true;
    }
}