net.sf.assinafacil.AssinaFacilApp.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.assinafacil.AssinaFacilApp.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;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRL;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLSelector;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
import java.util.Set;
import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;

/**
 * A classe principal da aplica\u00e7\u00e3o.
 */
public class AssinaFacilApp extends SingleFrameApplication {

    private Assinador signer;
    private String selectedFile = null;
    String signedFileName = null;
    private String certificateAlias = null;
    private AssinaFacilView mainWindow;

    private ConfigParams configProperties;

    public static final String CONFIG_FILE = "assinafacil.properties";

    /**
     * Inicializa o assinador e mostra a tela principal da aplica\u00e7\u00e3o.
     */
    @Override
    protected void startup() {

        mainWindow = new AssinaFacilView(this);
        show(mainWindow);
    }

    /***
     * utilizado para forcar a leitura a toda hora do keystore
     * O ideal \u00e9 implementar algum tipo de callback function para 
     * interceptar a inser\u00e7\u00e3o do token e apenas nesse caso for\u00e7ar a 
     * leitura dos certificados no dispositivo...
     * 
     * TODO: Implementar uma callback para evitar a enumera\u00e7\u00e3o dos certificados a 
     * cada evento relacionado com o objeto de interface que exibe a lista de certificados.
     */
    public void reinitialize() {
        try {
            signer.initialize(null);
        } catch (Exception e) {
            mainWindow.setStatusMessage(e.getMessage());
        }
    }

    /**
     * Pega os aliases (apelidos) disponiveis para o assinador.
     * @return lista de apelidos (aliases) de certificados.
     * @throws Exception
     */
    public Set<String> getAliases() throws Exception {
        return signer.getAllKeyCertificates().keySet();
    }

    /**
     * Controla o arquivo de entrada que ser\u00e1 assinado (ou - TODO - verificado)
     * @param arquivo de entrada.
     */
    public void setSelectedInputFile(String arquivo) {
        this.selectedFile = arquivo;
        mainWindow.setSelectedFile(arquivo);
    }

    /**
     * Controla o arquivo de saida...
     * @param arquivo
     */
    public void setSelectedOutputFile(String arquivo) {
        this.signedFileName = arquivo;
        mainWindow.setSelectedOutputFile(arquivo);
    }

    /**
     * Coisa do framework...
     */
    @Override
    protected void initialize(String[] args) {
        try {
            signer = new AssinadorMSCAPI();
            signer.initialize(null);
            signer.getAllKeyCertificates().keySet();

            String configFileName = null;

            if (args.length == 0) {
                configFileName = AssinaFacilApp.CONFIG_FILE;
            } else {
                configFileName = args[0];
            }

            if (new File(configFileName).canRead()) {
                configProperties = new ConfigParams("assinafacil", configFileName);
            } else {
                Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE,
                        "Arquivo de configurao no encontrado.");
                System.exit(1);
            }

        } catch (Exception ex) {

            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);

            if (mainWindow != null) {
                mainWindow.setStatusMessage(ex.getMessage());
            } else {
                System.exit(1);
            }
        }
    }

    /**
     * Coisa do framework...
     */
    /**
     * This method is to initialize the specified window by injecting resources.
     * Windows shown in our application come fully initialized from the GUI
     * builder, so this additional configuration is not needed.
     */
    @Override
    protected void configureWindow(java.awt.Window root) {
    }

    /**
     * A convenient static getter for the application instance.
     * @return the instance of AssinaFacilApp
     */
    public static AssinaFacilApp getApplication() {
        return Application.getInstance(AssinaFacilApp.class);
    }

    /**
     * Main method launching the application.
     */
    public static void main(String[] args) {
        launch(AssinaFacilApp.class, args);
    }

    /**
     * Fun\u00e7\u00e3o que verifica caso a arquivo de destino nao tenha sido escolhido, 
     * verifica se o arquivo de origem sufixado de .p7s existe para evitar sobrescrever 
     * algum arquivo existente sem o consentimento do usu\u00e1rio.
     * Caso o arquivo escolhido j\u00e1 possua alguma assinatura, o sistema incluir\u00e1 nesse
     * arquivo mais uma (co-assinatura). Pode ser um problema caso queia assinar um arquivo assinado e n\u00e3o seu conte\u00fado.
     * Caso o usu\u00e1rio escolha um arquivo de saida que exista, ele ser\u00e1 sobrescrito.
     */
    @Action
    public void appSignFile() {

        if (selectedFile == null) {
            mainWindow.setStatusMessage("Selecione o arquivo.");
            return;
        } else {
            /// Verifica se \u00e9 um arquivo e se j\u00e1 est\u00e1 assinado
            if (signer.isSignedFile(selectedFile)) {
                signedFileName = selectedFile;
            } else {
                File testFile = new File(selectedFile + ".p7s");
                if ((signedFileName == null) && (testFile.exists())) {
                    mainWindow.setStatusMessage(
                            "J\u00e1 existe um arquivo de saida com esse nome... escolha o arquivo de saida.");
                    return;
                } else {
                    if (!testFile.exists()) {
                        signedFileName = selectedFile + ".p7s";
                    }
                }

            }
        }

        if (certificateAlias != null) {
            try {
                String returnStatus = signer.signFile(this.selectedFile, signedFileName, null, certificateAlias);
                mainWindow.setStatusMessageOK(returnStatus);
                this.setSelectedInputFile(signedFileName);
                signedFileName = null;

            } catch (Exception ex) {
                mainWindow.setStatusMessage(ex.getMessage());
                signedFileName = null;
                Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, ex.getMessage());
            }
        } else {
            mainWindow.setStatusMessage("Escolha seu certificado...");
        }
    }

    @Action
    public void setCertificateAlias() {
        certificateAlias = mainWindow.getSelectedAlias();
    }

    /**
     * Action provis\u00c3\u00b3ria at\u00c3\u00a9 que alguma alma bonsoda implemente a verifica\u00c3\u00a7\u00c3\u00a3o e
     * a extra\u00c3\u00a7\u00c3\u00a3o do conte\u00c3\u00bado... por ora utilizarei uma chamada ao programa
     * CADIC do Banco Central do Brasil... Copyright?
     */
    @Action
    public void inicializaCADIC() {
        String selectedFileToCADIC = mainWindow.getSelectedFile();
        try {
            if ((selectedFileToCADIC != null) && (!selectedFileToCADIC.startsWith("Selecione"))
                    && (!selectedFileToCADIC.equals(""))) {
                Runtime.getRuntime().exec("CADIC.exe \"" + selectedFileToCADIC + "\"");
                Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.INFO, "Excecutando CADIC.exe \"{0}\"",
                        selectedFileToCADIC);
            } else {
                Runtime.getRuntime().exec("CADIC.exe");
                Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.INFO, "Excecutando CADIC.exe");
            }
            mainWindow.setStatusMessage("Executando o CADIC do BACEN que faz isso...");
        } catch (IOException ex) {
            mainWindow.setStatusMessage("Erro ao executar o CADIC... coloque no Path do usu\u00c3\u00a1rio");
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE,
                    "Erro ao executar o CADIC. Coloque no Path do usu\u00c3\u00a1rio.");
        }
    }

    @Action
    public void verifySignersAndShowInfo() {

        String selectedSignedFile = mainWindow.getSelectedFile();

        if ((selectedSignedFile == null) || (selectedSignedFile.startsWith("Selecione"))
                || (selectedSignedFile.equals(""))) {
            mainWindow.setStatusMessage("Selecione o arquivo para verificao...");
            return;
        }
        try {
            CertStore trustCertStore = this.buildTrustStore();

            CertStore crlCertStore = null;

            SignerInformationStore signerInformationStore = this.getSignatures(new File(selectedSignedFile));
            CertStore certificateStore = this.getCertificates(new File(selectedSignedFile));

            Collection<? extends Certificate> certificates = certificateStore.getCertificates(null);
            Collection colSign = signerInformationStore.getSigners();

            HashSet<X509CRL> crlCerts = new HashSet();

            HashSet<TrustAnchor> trustSet = new HashSet();

            // Pega os certificados sem repetio pois o CertBuilder erra na seleo...
            for (Certificate cert : certificates) {
                if (cert instanceof X509Certificate) {
                    X509Certificate x509Cert = (X509Certificate) cert;

                    // Pega s os root's CA para o trust e as outras CAs para o CRLStore
                    if (x509Cert.getBasicConstraints() != -1) {
                        if ((x509Cert.getIssuerDN().getName().equals(x509Cert.getSubjectDN().getName())))
                            trustSet.add(new TrustAnchor(x509Cert, null));

                        X509CRL crl = UtiICPBrasill.getCRLFromDP(x509Cert);
                        crlCerts.add(crl);
                    }
                }
            }
            // Cria um certstore com as CRLS da cadeia
            CollectionCertStoreParameters crlCertStoreParams = new CollectionCertStoreParameters(crlCerts);
            crlCertStore = CertStore.getInstance("Collection", crlCertStoreParams, "BC");

            Iterator iteradorSigners = colSign.iterator();

            Set<SignerData> signerTable = new HashSet();

            while (iteradorSigners.hasNext()) {
                Object signObject = iteradorSigners.next();
                if (signObject instanceof SignerInformation) {
                    SignerInformation signerInfo = (SignerInformation) signObject;
                    X509Certificate signerCertificate = null;
                    if (!certificateStore.getCertificates(signerInfo.getSID()).isEmpty())
                        signerCertificate = (X509Certificate) certificateStore.getCertificates(signerInfo.getSID())
                                .toArray()[0];

                    try {
                        PKIXBuilderParameters params = new PKIXBuilderParameters(trustSet, signerInfo.getSID());
                        params.addCertStore(certificateStore);
                        params.addCertStore(crlCertStore);

                        // TODO: Check CRL in validation process (do we have to include CRL in sign process?)
                        params.setRevocationEnabled(false);
                        // It's better check CRLs after path building process to be concluded

                        // Trata politica especifica da ICP
                        params.addCertPathChecker(new AssinaFacilExtPathChecker());

                        CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
                        PKIXCertPathBuilderResult certChainResult = (PKIXCertPathBuilderResult) builder
                                .build(params);
                        CertPath cp = certChainResult.getCertPath();

                        String revokeState = SignerData.OK;
                        // Valida se os certificados da cadeia ainda eso vlidos.

                        for (Certificate certTest : cp.getCertificates()) {
                            X509Certificate x509test = (X509Certificate) certTest;
                            revokeState = this.getRevokeState(x509test, crlCertStore, revokeState);
                        }
                        revokeState = this.getRevokeState(certChainResult.getTrustAnchor().getTrustedCert(),
                                crlCertStore, revokeState);
                        boolean isICP = this.checkKnowTrust(certChainResult.getTrustAnchor().getTrustedCert(),
                                trustCertStore);
                        String signatureState = this.signatureState(signerInfo, signerCertificate);
                        signerTable.add(
                                new SignerData(signerInfo, cp, certChainResult.getTrustAnchor().getTrustedCert(),
                                        isICP, signatureState, revokeState));

                    } catch (CertPathBuilderException ex) {
                        Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, "Nao Validado {0} {1}",
                                new Object[] { ex.getMessage(), ex });
                        String signatureState = this.signatureState(signerInfo, signerCertificate);
                        signerTable.add(new SignerData(signerInfo, signerCertificate, signatureState));

                    } catch (InvalidAlgorithmParameterException ex) {

                    } catch (Exception ex) {
                        Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, "Nao Validado {0} {1}",
                                new Object[] { ex.getMessage(), ex });
                    }
                }
            }

            AssinaFacilSignerDetail afsov = new AssinaFacilSignerDetail(mainWindow.getFrame(), true, signerTable);
            afsov.setVisible(true);

        } catch (InvalidAlgorithmParameterException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchProviderException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            mainWindow.setStatusMessage("No foi possvel extrair assinaturas do arquivo selecionado.");
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SignatureException ex) {
            mainWindow.setStatusMessage("No foi possvel extrair assinaturas do arquivo selecionado.");
            // Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (CertStoreException ex) {
            mainWindow.setStatusMessage("Ocorreu um erro ao acessar os certificados armazenados.");
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private String getRevokeState(X509Certificate x509test, CertStore crlCertStore, String lastState)
            throws CertStoreException {
        String revokeState = lastState;

        X509CRLSelector crlSelector = new X509CRLSelector();
        crlSelector.addIssuer(x509test.getIssuerX500Principal());
        Collection<X509CRL> crlTestList = (Collection<X509CRL>) crlCertStore.getCRLs(crlSelector);

        if ((crlTestList.isEmpty()) && (x509test.getBasicConstraints() != -1)) {
            // TODO: O certificado em questo no dispe de CRL (avisa o usuario?)
            revokeState = SignerData.NOCRL;
        }

        for (X509CRL x509crltest : crlTestList) {
            if (x509crltest.getRevokedCertificate(x509test.getSerialNumber()) != null) {
                revokeState = SignerData.REVOKED;
            }
        }

        return revokeState;
    }

    private String signatureState(SignerInformation signerInfo, X509Certificate signerCertificate) {
        try {
            if (signerInfo.verify(signerCertificate, "BC")) {
                return "Assinatura OK com contedo";
            } else
                return "Assinatura no confere com contedo";

        } catch (CMSException ex) {
            return "Assinatura no confere com contedo";
        } catch (GeneralSecurityException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return "Erro ao verificar assinatura";
        } catch (Exception ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return "Erro ao verificar assinatura";
        }
    }

    private CertStore buildTrustStore() {
        try {
            HashSet<X509Certificate> trustSetBuilder = new HashSet();

            // Monta um keystore com base numa lista de certificados raiz confiavis
            File dirAc = new File(configProperties.getICPRootCertificate());

            FilenameFilter filter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return !name.startsWith(".");
                }
            };

            InputStream inStream = null;

            String[] caFiles = dirAc.list(filter);

            for (String file : caFiles) {
                inStream = new FileInputStream(configProperties.getICPRootCertificate() + "/" + file);

                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(inStream);
                inStream.close();

                Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.INFO,
                        "IMPORT => " + x509Cert.getSubjectDN().getName());
                trustSetBuilder.add(x509Cert);
            }

            // Cria um certstore com os certificados unicos que sero utilizados para montar a cadeia
            CollectionCertStoreParameters certStoreParams = new CollectionCertStoreParameters(trustSetBuilder);
            CertStore trustCertStore = CertStore.getInstance("Collection", certStoreParams, "BC");

            return trustCertStore;
        } catch (IOException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (CertificateException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (InvalidAlgorithmParameterException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (NoSuchProviderException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    private Boolean checkKnowTrust(X509Certificate trustCertificate, CertStore trustStore) {
        try {
            X509CertSelector certSelector = new X509CertSelector();
            certSelector.setSubjectPublicKey(trustCertificate.getPublicKey());
            return !trustStore.getCertificates(certSelector).isEmpty();
        } catch (CertStoreException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }

    }

    public CertStore getCertificates(File fileInput)
            throws java.security.SignatureException, FileNotFoundException {
        CMSSignedData signedData = null;

        CertStore certs = null;

        try {
            signedData = new CMSSignedData(new FileInputStream(fileInput));
            certs = signedData.getCertificatesAndCRLs("Collection", "BC");
            return certs;

        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (NoSuchProviderException ex) {
            Logger.getLogger(AssinadorMSCAPI.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (CMSException e) {
            throw new SignatureException("Arquivo no assinado ou formato invlido");
        }
    }

    public byte[] getSignedContent(File fileInput) throws GeneralSecurityException, IOException {
        CMSSignedData signedData = null;
        CMSProcessable content = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            signedData = new CMSSignedData(new FileInputStream(fileInput));
            content = signedData.getSignedContent();
            content.write(baos);

            return baos.toByteArray();

        } catch (CMSException e) {
            throw new GeneralSecurityException("Arquivo no assinado ou formatao invlida.");
        }
    }

    public boolean extractSignedContent(File fileInput, File fileOutput)
            throws GeneralSecurityException, IOException {
        CMSSignedData signedData = null;
        CMSProcessable content = null;
        FileOutputStream fos = new FileOutputStream(fileOutput);

        try {
            signedData = new CMSSignedData(new FileInputStream(fileInput));
            content = signedData.getSignedContent();
            content.write(fos);
            fos.close();
            return true;

        } catch (CMSException e) {
            throw new GeneralSecurityException("Arquivo no assinado ou formatao invlida.");
        }
    }

    public SignerInformationStore getSignatures(File fileInput)
            throws java.security.SignatureException, FileNotFoundException {
        CMSSignedData signedData = null;

        SignerInformationStore signers = null;

        try {
            signedData = new CMSSignedData(new FileInputStream(fileInput));
            signers = signedData.getSignerInfos();

            return signers;

        } catch (CMSException e) {
            throw new SignatureException("Arquivo no assinado ou formato invlido");
        }
    }

    @Action
    public void showSignedContent() {
        try {
            byte[] content = this.getSignedContent(new File(selectedFile));
            AssinaFacilShowContent afsc = new AssinaFacilShowContent(mainWindow.getFrame(), true, content);
        } catch (GeneralSecurityException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(AssinaFacilApp.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Action
    public void extractContent() {
        if ((selectedFile == null) || (signedFileName == null)) {
            mainWindow.setStatusMessage("Selecione os arquivos de entrada e saida...");
            return;
        }
        try {
            if (this.extractSignedContent(new File(selectedFile), new File(signedFileName))) {
                this.mainWindow.setStatusMessageOK("Arquivo extrado...");
            }
        } catch (GeneralSecurityException ex) {
            this.mainWindow.setStatusMessage("Erro ao extrair o arquivo..." + ex.getMessage());
        } catch (IOException ex) {
            this.mainWindow.setStatusMessage("Erro ao extrair o arquivo..." + ex.getMessage());
        }
    }

}