net.sf.assinafacil.AssinadorMSCAPI.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.assinafacil.AssinadorMSCAPI.java

Source

/*  This file is part of AssinaFacil.
    
AssinaFacil 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 3 of the License, or
(at your option) any later version.
    
AssinaFacil 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 AssinaFacil.  If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.assinafacil;

/***
 * Implementa o Assinador para o provider SunMSCAPI.
 * S\u00f3 funciona com a vers\u00e3o 32bits pois a biblioteca nativa n\u00e3o foi distribuida
 * pela oracle na versao 64bits.
 * 
 * author: ginglass
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.cms.CMSException;

import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class AssinadorMSCAPI implements Assinador {

    public KeyStore keyStore = null;
    public static final String KEYSTORE_STRING = "Windows-MY";
    public static final String PROVIDER_STRING = "SunMSCAPI";

    public AssinadorMSCAPI() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        this.initialize(null);
    }

    /**
     *    
     * @return true se suporta o provider SunMSCAPI
     */
    public boolean supportsMSCAPI() {
        if (Security.getProvider(PROVIDER_STRING) != null)
            return true;
        else {
            Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.INFO,
                    "Ambiente n\u00e3o disp\u00f5e do SunMSCAPI");
            return false;
        }
    }

    /**
     * 
     * @return Mapa de certificados relacionados com seu respectivo apelido no keystore.
     */
    @Override
    public Map<String, X509Certificate> getAllKeyCertificates() throws Exception {
        Map<String, X509Certificate> res = new HashMap<String, X509Certificate>();
        Enumeration<String> aliases = keyStore.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (keyStore.isKeyEntry(alias)) {
                res.put(alias, getCertificate(alias));
            }
        }
        return res;
    }

    @Override
    /**
     * @return KeyStore utilizado por esse assinador
     */
    public KeyStore getKeyStore() throws Exception {
        return keyStore;
    }

    @Override
    /***
     * Inicializa o assinador. verifica o suporte ao provider e instancia 
     * o keystore que ser\u00e1 utilizado pelo assinador.
     */
    public void initialize(String password) throws Exception {
        if (!this.supportsMSCAPI()) {
            throw new java.security.NoSuchProviderException(
                    "N\u00ef\u00bf\u00bdo suporta criptografia nativa Microsoft. \u00ef\u00bf\u00bd java 1.6 43 bits?");
        }
        keyStore = KeyStore.getInstance(KEYSTORE_STRING);
        keyStore.load(null, null);

    }

    @Override
    /***
     * @return true caso o Assinador j\u00e1 tenha sido inicializado
     */
    public boolean isInitialized() {
        if (this.keyStore != null)
            return true;
        else
            return false;
    }

    @Override
    /***
     * Assina digitalmente o arquivo de entrada e gera o arquivo de sa\u00edda.
     * nesse caso a senha n\u00e3o \u00e9 utilizada pois o keystore \u00e9 um token suja senha 
     * ser\u00e1 requerida pelo MSCAPI.
     * 
     * @return Mensagem de status que ser\u00e1 exibida na interface.
     */
    public String signFile(String fileInput, String signedFileName, String password, String certificateAlias)
            throws Exception {
        if (!isInitialized()) {
            throw new java.security.KeyException(
                    "Chaveiro n\u00c3\u00a3o inicializado ou erro ao acess\u00c3\u00a1-lo.");
        }

        PrivateKey priv = null;
        Certificate storecert = null;
        Certificate[] certChain = null;
        ArrayList<Certificate> certList = new ArrayList<Certificate>();
        CertStore certs = null;
        CMSSignedData signedData = null;
        CMSProcessable content = null;
        byte[] signeddata = null;

        String retorno;

        if (signedFileName == null)
            signedFileName = fileInput;

        certChain = keyStore.getCertificateChain(certificateAlias);

        if (certChain == null) {
            throw new GeneralSecurityException(
                    "Cadeia do certificado " + certificateAlias + " n\u00c3\u00a3o encontrada.");
        }
        certList.addAll(Arrays.asList(certChain));

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

        storecert = keyStore.getCertificate(certificateAlias);
        priv = (PrivateKey) (keyStore.getKey(certificateAlias, null));
        if (priv == null) {
            throw new java.security.AccessControlException(
                    "Acesso \u00c3\u00a0 chave foi negado... senha inv\u00c3\u00a1lida?");
        }

        CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
        signGen.addSigner(priv, (X509Certificate) storecert, CMSSignedDataGenerator.DIGEST_SHA1);
        signGen.addCertificatesAndCRLs(certs);

        try {
            signedData = new CMSSignedData(new FileInputStream(fileInput));
            content = signedData.getSignedContent();
            signGen.addSigners(signedData.getSignerInfos());
            signGen.addCertificatesAndCRLs(signedData.getCertificatesAndCRLs("Collection", "BC"));
            CMSSignedData signedData2 = signGen.generate(content, true, PROVIDER_STRING);
            signeddata = signedData2.getEncoded();

            retorno = "Arquivo " + signedFileName + " foi assinado novamente.";

        } catch (CMSException e) {
            content = new CMSProcessableFile(new File(fileInput));
            signedData = signGen.generate(content, true, PROVIDER_STRING);
            signeddata = signedData.getEncoded();

            retorno = "Arquivo " + signedFileName + " foi assinado.";
        }

        FileOutputStream fileOutput = new FileOutputStream(signedFileName);
        fileOutput.write(signeddata);
        fileOutput.close();

        Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.INFO, retorno);

        return retorno;
    }

    @Override
    /***
     * Pega do keystore o certificado com determinado apelido
     */
    public X509Certificate getCertificate(String alias) throws Exception {
        return (X509Certificate) keyStore.getCertificate(alias);
    }

    /***
     * Verifica se o arquivo j\u00e1 foi assinado... feio pois identifica via exception...
     * mas \u00e9 r\u00e1pido e sei que desenvolvedores mais cuidadosos me ajudar\u00e3o a melhorar
     * inclusive esse trecho do c\u00f3digo.
     */
    public boolean isSignedFile(String fileName) {
        CMSSignedData signedData = null;
        try {
            signedData = new CMSSignedData(new FileInputStream(fileName));
            return signedData.getContentInfo().getContentType()
                    .equals(org.bouncycastle.asn1.cms.CMSObjectIdentifiers.signedData);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.INFO,
                    "Arquivo " + fileName + " n\u00e3o encontrado", ex);
            return false;
        } catch (CMSException ex) {
            // Malformed content.
            // DEBUG? Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
}