source: branches/2.2/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java @ 3633

Revision 3633, 29.0 KB checked in by rafaelraymundo, 14 years ago (diff)

Ticket #1430 - Mais de um certificados gravados no mesmo token.

RevLine 
[1174]1package br.gov.serpro.cert;
2
3import br.gov.serpro.setup.Setup;
4import java.awt.Frame;
5import java.io.ByteArrayInputStream;
6import java.io.ByteArrayOutputStream;
7import java.io.File;
8import java.io.FileInputStream;
9import java.io.IOException;
10import java.io.InputStream;
11import java.net.MalformedURLException;
12import java.net.URL;
13import java.security.AuthProvider;
14import java.security.GeneralSecurityException;
15import java.security.Key;
16import java.security.KeyPair;
17import java.security.KeyStore;
18import java.security.KeyStoreException;
19import java.security.PrivateKey;
20import java.security.Provider;
21import java.security.ProviderException;
22import java.security.Security;
23import java.security.cert.CertStore;
24import java.security.cert.Certificate;
25import java.security.cert.CollectionCertStoreParameters;
26import java.security.cert.X509Certificate;
27import java.util.ArrayList;
28import java.util.Enumeration;
29import java.util.List;
30import java.util.Map;
31import java.util.Properties;
32
33import javax.crypto.Cipher;
34import javax.mail.Message;
35import javax.mail.MessagingException;
36import javax.mail.Session;
37import javax.mail.internet.MimeBodyPart;
38import javax.mail.internet.MimeMessage;
39import javax.mail.internet.MimeMultipart;
40import javax.net.ssl.SSLHandshakeException;
41import javax.security.auth.login.LoginException;
42
43import org.apache.commons.httpclient.HttpClient;
44import org.apache.commons.httpclient.HttpException;
45import org.apache.commons.httpclient.methods.PostMethod;
46import org.apache.commons.httpclient.protocol.Protocol;
47import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
48import org.bouncycastle.asn1.ASN1EncodableVector;
49import org.bouncycastle.asn1.cms.AttributeTable;
50import org.bouncycastle.asn1.smime.SMIMECapability;
51import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
52import org.bouncycastle.mail.smime.SMIMEException;
53import org.bouncycastle.mail.smime.SMIMESignedGenerator;
54
55import br.gov.serpro.ui.DialogBuilder;
56import br.gov.serpro.util.Base64Utils;
[3232]57import java.io.OutputStream;
58import java.io.OutputStreamWriter;
[1174]59import java.security.AlgorithmParameters;
[3394]60import java.util.regex.Matcher;
61import java.util.regex.Pattern;
[3232]62import javax.activation.CommandMap;
63import javax.activation.MailcapCommandMap;
64import javax.mail.internet.ContentType;
65import javax.mail.internet.MimeUtility;
66import javax.mail.internet.PreencodedMimeBodyPart;
[1174]67import org.bouncycastle.cms.CMSException;
68import org.bouncycastle.cms.RecipientId;
69import org.bouncycastle.cms.RecipientInformation;
70import org.bouncycastle.cms.RecipientInformationStore;
71import org.bouncycastle.mail.smime.SMIMEEnvelopedParser;
72import org.bouncycastle.mail.smime.SMIMEUtil;
73
74/**
75 * Classe que realiza todo o trabalho realizado com o certificado
76 * @author Mário César Kolling - mario.kolling@serpro.gov.br
77 */
78//TODO: Criar exceções para serem lançadas, entre elas DigitalCertificateNotLoaded
79//TODO: Adicionar setup
80public class DigitalCertificate {
81
82    private TokenCollection tokens;
[3633]83    private String selectedCertificateAlias;
[3232]84    private Certificate cert; // Certificado extraído da KeyStore. Pode ser nulo.
85    private KeyStore keyStore; // KeyStore que guarda o certificado do usuário. Pode ser nulo.
86    private Frame parentFrame;
[1174]87    private Setup setup;
[3232]88    // TODO: Transformar pkcs12Input em uma string ou URL com o caminho para a KeyStore pkcs12
89    private FileInputStream pkcs12Input; // stream da KeyStore pkcs12. Pode ser nulo.
90    private String providerName; // Nome do SecurityProvider pkcs11 carregado. Pode ser nulo.
91    private String hostAddress; // Endereço do host, onde a página principal do
92    private static final String HOME_SUBDIR; // Subdiretório dentro do diretório home do usuário. Dependente de SO.
93    private static final String EPASS_2000; // Caminho da biblioteca do token ePass2000. Dependente de SO.
94    private static final String CRLF = "\r\n"; // Separa campos na resposta do serviço de verificação de certificados
95    private static final String SUBJECT_ALTERNATIVE_NAME = "2.5.29.17"; // Não é mais utilizado.
96    private static final URL[] TRUST_STORES_URLS = new URL[3]; // URLs (file:/) das TrustStores, cacerts (jre),
97    // trusted.certs e trusted.jssecerts (home do usuário)
98    // Utilizadas para validação do certificado do servidor.
99    private static final String[] TRUST_STORES_PASSWORDS = null; // Senhas para cada uma das TrustStores,
100    // caso seja necessário.
101    private int keystoreStatus;
102    public static final int KEYSTORE_DETECTED = 0;
103    public static final int KEYSTORE_NOT_DETECTED = 1;
104    public static final int KEYSTORE_ALREADY_LOADED = 2;
[1174]105
[3232]106    /*
107     * Bloco estático que define os caminhos padrões da instalação da jre,
108     * do diretório home do usuário, e da biblioteca de sistema do token ePass2000,
109     * de acordo com o sistema operacional.
110     */
111    static {
[1174]112
[3232]113        Properties systemProperties = System.getProperties();
114        Map<String, String> env = System.getenv();
[1174]115
[3232]116        /* TODO: Testar a existência de vários drivers de dispositivos. Determinar qual deve ser utilizado
117         * e guardar em uma property no subdiretório home do usuário.
[1174]118         */
119
[3232]120        if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")) {
121            HOME_SUBDIR = "/.java/deployment/security";
122            EPASS_2000 = "/usr/lib/libepsng_p11.so";
123        } else {
124            HOME_SUBDIR = "\\dados de aplicativos\\sun\\java\\deployment\\security";
125            EPASS_2000 = System.getenv("SystemRoot") + "\\system32\\ngp11v211.dll";
126            //EPASS_2000 = System.getenv("ProgramFiles")+"\\Gemplus\\GemSafe Libraries\\BIN\\gclib.dll";
127        }
[1174]128
[3232]129        try {
130            if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")) {
131                TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home") + "/lib/security/cacerts").toURI().toURL();
132                TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home") + HOME_SUBDIR + "/trusted.certs").toURI().toURL();
133                TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home") + HOME_SUBDIR + "/trusted.jssecerts").toURI().toURL();
134            } else {
[1174]135
[3232]136                TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home") +
137                        "\\lib\\security\\cacerts").toURI().toURL();
138                TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home") +
139                        HOME_SUBDIR + "\\trusted.certs").toURI().toURL();
140                TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home") +
141                        HOME_SUBDIR + "\\trusted.jssecerts").toURI().toURL();
142            }
[1174]143
[3232]144            // Define os tipos smime no mailcap
145            MailcapCommandMap mailcap = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
[1174]146
[3232]147            mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
148            mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
149            mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
150            mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
151            mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
[1174]152
[3232]153            CommandMap.setDefaultCommandMap(mailcap);
[1174]154
[3232]155
156
157        } catch (MalformedURLException e) {
158            e.printStackTrace();
[1174]159        }
[3232]160    }
[1174]161
[3232]162    /**
163     *
164     */
165    public DigitalCertificate() {
166        this.hostAddress = null;
167        this.parentFrame = null;
168    }
169
170    /**
171     * Construtor da classe. Recebe a {@link URL} da página em que a Applet está incluída.
172     * @param pageAddress URL da página em que a Applet está incluída
173     */
174    private DigitalCertificate(URL pageAddress) {
175        this.hostAddress = pageAddress.getHost();
176        this.parentFrame = null;
177    }
178
179    private DigitalCertificate(Frame parent) {
180        this.hostAddress = null;
181        this.parentFrame = parent;
182    }
183
184    public DigitalCertificate(Frame parent, Setup setup) {
185        this(parent);
186        this.setup = setup;
187    }
188
189    public DigitalCertificate(URL pageAddress, Setup setup) {
190        this(pageAddress);
191        this.setup = setup;
192    }
193
194    public KeyStore getKeyStore() {
195        return keyStore;
196    }
197
198    public int getKeystoreStatus() {
199        return keystoreStatus;
200    }
201
202    public String getProviderName() {
203        return providerName;
204    }
205
206    /**
207     * Destrói a Applet, removendo o security provider inicializado se o atributo providerName
208     * for diferente de nulo.
209     */
210    public void destroy() {
211
212        AuthProvider ap = null;
213
214        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
215            System.out.println("logout no provider");
[1174]216        }
[3232]217        if (keyStore != null) {
218            ap = (AuthProvider) this.keyStore.getProvider();
219        }
[1174]220
[3232]221        if (ap != null) {
222            try {
223                ap.logout();
224            } catch (LoginException e) {
225                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
226                    e.printStackTrace();
227                }
228            }
[1174]229        }
230
[3232]231        if (providerName != null) {
232            Security.removeProvider(providerName);
[1174]233        }
234
[3232]235        this.cert = null;
[3633]236        this.selectedCertificateAlias = null;
[3232]237        this.keyStore = null;
238        this.hostAddress = null;
239        this.pkcs12Input = null;
240        this.providerName = null;
[1174]241
[3232]242    }
[1174]243
[3232]244    /**
245     * Procura pelo token nos locais padrões (Por enquanto só suporta o token ePass200),
246     * senão procura por um certificado A1 em System.getProperties().getProperty("user.home") +
247     * HOME_SUBDIR  + "/trusted.clientcerts" e retorna um inteiro de acordo com resultado desta procura.
248     *
249     * @author  Mário César Kolling
250     * @return  Retorna um destes valores inteiros DigitalCertificate.KEYSTORE_DETECTED,
251     *          DigitalCertificate.KEYSTORE_ALREADY_LOADED ou DigitalCertificate.KEYSTORE_NOT_DETECTED
252     * @see     DigitalCertificate
253     */
254    public int init() {
[1174]255
[3232]256        // TODO: Usar dentro de um "loop" para testar outros modelos de tokens.
257        this.tokens = new TokenCollection(setup);
258        int interfaceType = DigitalCertificate.KEYSTORE_DETECTED;
[1174]259
[3232]260        try {
261            // Tenta abrir o Token padrï¿œo (ePass2000).
262            loadKeyStore();
[1174]263
[3232]264        } catch (Exception e1) {
[1174]265
[3232]266            Provider[] providers = Security.getProviders();
267            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
268                for (Provider provider : providers) {
269                    System.out.println(provider.getInfo());
[1174]270                }
271
[3232]272                // Não conseguiu abrir o token (ePass2000).
273                System.out.println("Erro ao ler o token: " + e1.getMessage());
274            }
[1174]275
[3232]276            try {
277                // Tenta abrir a keyStore padrão
278                // USER_HOME/deployment/security/trusted.clientcerts
[1174]279
[3232]280                Properties props = System.getProperties();
281                pkcs12Input = new FileInputStream(props.getProperty("user.home") + HOME_SUBDIR + "/trusted.clientcerts");
282
283                // Se chegar aqui significa que arquivo de KeyStore existe.
284                keyStore = KeyStore.getInstance("JKS");
285
286            } catch (Exception ioe) {
287                // Não conseguiu abrir a KeyStore pkcs12
288                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
289                    System.out.println(ioe.getMessage());
290                }
291            }
[1174]292        }
293
294
[3232]295        if (keyStore == null) {
296            // Não conseguiu inicializar a KeyStore. Mostra tela de login com usuário e senha.
297            this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
298            //} else if (keyStore.getType().equalsIgnoreCase("pkcs11")){
299        } else {
300            // Usa certificado digital.
301            try {
302                // Testa se uma keystore já foi carregada previamente
303                if (keyStore.getType().equalsIgnoreCase("pkcs11")) {
304                    keyStore.load(null, null);
305                } else {
306                    keyStore.load(pkcs12Input, null);
307                }
[1174]308
[3232]309                // Se chegou aqui KeyStore está liberada, mostrar tela de login sem pedir o pin.
310                this.keystoreStatus = DigitalCertificate.KEYSTORE_ALREADY_LOADED;
[1174]311
[3232]312            } catch (ProviderException e) {
313                // Algum erro ocorreu, mostra  tela de login com usuário e senha.
314                this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
315                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
316                    e.printStackTrace();
[1174]317                }
[3232]318            } catch (IOException e) {
319                // KeyStore não está liberada, mostra tela de login com o pin.
320                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
321                    System.out.println(e.getMessage());
322                }
323                this.keystoreStatus = DigitalCertificate.KEYSTORE_DETECTED;
324            } catch (GeneralSecurityException e) {
325                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
326                    e.printStackTrace();
327                }
328            }
329        }
[1174]330
[3232]331        return keystoreStatus;
[1174]332
[3232]333    }
[1174]334
[3232]335    /**
336     * Usado para assinar digitalmente um e-mail.
337     * @param mime
338     * @return String vazia
339     */
340    public String signMail(Map<String, String> data) throws IOException, GeneralSecurityException, SMIMEException, MessagingException {
[1174]341
[3232]342        Key privateKey = null;
343        if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED) {
344            String pin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
345            if (pin != null) {
346                openKeyStore(pin.toCharArray());
[3633]347                if (this.selectedCertificateAlias == null){
348                    return null;
349                }
350                privateKey = this.keyStore.getKey(this.selectedCertificateAlias, pin.toCharArray());
[3232]351            } else {
352                return null;
353            }
354        } /*
355        else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
356        if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
357        openKeyStore(null);
358        privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
359        }
360        else {
361        return null;
362        }
363        }
364         */ else {
[1174]365
[3232]366            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
367            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
368            return null;
369        }
[1174]370
[3232]371        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
[1174]372
[3232]373        Certificate certificate = getCert();
[1174]374
[3232]375        KeyPair keypair = new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey);
[1174]376
[3232]377        // Cria a cadeia de certificados que a gente vai enviar
378        List certList = new ArrayList();
[1174]379
[3232]380        certList.add(certificate);
[1174]381
[3232]382        //
383        // create the base for our message
384        //
385        String fullMsg = data.get("body");
[1174]386
[3232]387        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
388            System.out.println("Corpo do e-mail:\n" + fullMsg + "\n");
[1174]389        }
390
[3232]391        //
392        // Get a Session object and create the mail message
393        //
394        Properties props = System.getProperties();
395        Session session = Session.getDefaultInstance(props, null);
[1174]396
[3232]397        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
398        MimeMessage unsignedMessage = new MimeMessage(session, is);
[1174]399
[3232]400        //
401        // create a CertStore containing the certificates we want carried
402        // in the signature
403        //
404        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
405            System.out.println("Provider: " + providerName);
406        }
407        CertStore certsAndcrls = CertStore.getInstance(
408                "Collection",
409                new CollectionCertStoreParameters(certList), "BC");
[1174]410
[3232]411        //
412        // create some smime capabilities in case someone wants to respond
413        //
414        ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
[1174]415
[3232]416        SMIMECapabilityVector caps = new SMIMECapabilityVector();
[1174]417
[3232]418        caps.addCapability(SMIMECapability.dES_EDE3_CBC);
419        caps.addCapability(SMIMECapability.rC2_CBC, 128);
420        caps.addCapability(SMIMECapability.dES_CBC);
[1174]421
[3232]422        SMIMESignedGenerator gen = new SMIMESignedGenerator(unsignedMessage.getEncoding());
[1174]423
[3232]424        //SMIMESignedGenerator gen = new SMIMESignedGenerator();
[1174]425
[3232]426        gen.addSigner(keypair.getPrivate(), (X509Certificate) certificate, SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(signedAttrs), null);
[1174]427
[3232]428        gen.addCertificatesAndCRLs(certsAndcrls);
[1174]429
[3232]430        //TODO: Extrair todos os headers de unsignedMessage
[1174]431
[3232]432        // Gera a assinatura
433        Object content = unsignedMessage.getContent();
[1174]434
[3232]435        //TODO: igualar unsignedMessage a null
436        //TODO: Pegar os headers do objeto que guardarï¿œ esses headers quando necessï¿œrio.
[1174]437
[3232]438        MimeMultipart mimeMultipartContent = null;
439        PreencodedMimeBodyPart mimeBodyPartContent = null;
[1174]440
[3232]441        if (content.getClass().getName().contains("MimeMultipart")) {
442            mimeMultipartContent = (MimeMultipart) content;
443        } else {
444            String encoding = MimeUtility.getEncoding(unsignedMessage.getDataHandler());
445            mimeBodyPartContent = new PreencodedMimeBodyPart(encoding);
446            if (encoding.equalsIgnoreCase("quoted-printable")) {
447                ByteArrayOutputStream os = new ByteArrayOutputStream();
448                OutputStream encode = MimeUtility.encode(os, encoding);
449                OutputStreamWriter writer = new OutputStreamWriter(encode, "iso-8859-1");
450                writer.write(content.toString());
451                writer.flush();
452                mimeBodyPartContent.setText(os.toString(), "iso-8859-1");
453                os = null;
454                encode = null;
455                writer = null;
456            } else {
457                mimeBodyPartContent.setText(content.toString(), "iso-8859-1");
458            }
459            mimeBodyPartContent.setHeader("Content-Type", unsignedMessage.getHeader("Content-Type", null));
460        }
461        content = null;
[1174]462
[3232]463        //
464        // extract the multipart object from the SMIMESigned object.
465        //
466        MimeMultipart mm = null;
467        if (mimeMultipartContent == null) {
468            mm = gen.generate(mimeBodyPartContent, providerName);
469            mimeBodyPartContent = null;
470        } else {
471            MimeBodyPart multipartMsg = new MimeBodyPart();
472            multipartMsg.setContent(mimeMultipartContent);
473            mm = gen.generate(multipartMsg, providerName);
474            multipartMsg = null;
475            mimeMultipartContent = null;
476        }
[1174]477
[3232]478        gen = null;
[1174]479
[3232]480        MimeMessage body = new MimeMessage(session);
481        body.setFrom(unsignedMessage.getFrom()[0]);
482        body.setRecipients(Message.RecipientType.TO, unsignedMessage.getRecipients(Message.RecipientType.TO));
483        body.setRecipients(Message.RecipientType.CC, unsignedMessage.getRecipients(Message.RecipientType.CC));
484        body.setRecipients(Message.RecipientType.BCC, unsignedMessage.getRecipients(Message.RecipientType.BCC));
485        body.setSubject(unsignedMessage.getSubject(), "iso-8859-1");
[1174]486
[3232]487        // Atrafuia o resto dos headers
488        body.setHeader("Return-Path", unsignedMessage.getHeader("Return-Path", null));
489        body.setHeader("Message-ID", unsignedMessage.getHeader("Message-ID", null));
490        body.setHeader("X-Priority", unsignedMessage.getHeader("X-Priority", null));
491        body.setHeader("X-Mailer", unsignedMessage.getHeader("X-Mailer", null));
[3413]492        body.setHeader("Importance", unsignedMessage.getHeader("Importance", null));
[3232]493        body.setHeader("Disposition-Notification-To", unsignedMessage.getHeader("Disposition-Notification-To", null));
494        body.setHeader("Date", unsignedMessage.getHeader("Date", null));
495        body.setContent(mm, mm.getContentType());
496        mm = null;
[1174]497
[3232]498        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
499            System.out.println("\nHeaders do e-mail original:\n");
500        }
[1174]501
[3232]502        body.saveChanges();
[1174]503
[3232]504        ByteArrayOutputStream oStream = new ByteArrayOutputStream();
[1174]505
[3232]506        oStream = new ByteArrayOutputStream();
507        body.writeTo(oStream);
[1174]508
[3232]509        body = null;
510        return oStream.toString("iso-8859-1");
[1174]511
[3232]512    }
[1174]513
[3232]514    /**
515     * Método utilizado para criptografar um e-mail
516     * @param source
517     * @return
518     */
519    public String cipherMail(Map<String, String> data) throws IOException, GeneralSecurityException, MessagingException, CMSException, SMIMEException {
[1174]520
[3232]521        //Pega certificado do usuário.
[1174]522
[3232]523        Key privateKey = null;
524        if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED) {
525            String pin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
526            if (pin != null) {
527                openKeyStore(pin.toCharArray());
[3633]528                if (this.selectedCertificateAlias == null){
529                    return null;
530                }
531                privateKey = this.keyStore.getKey(this.selectedCertificateAlias, pin.toCharArray());
[3232]532            } else {
533                return null;
534            }
535        } /*
536        else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
537        if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
538        openKeyStore(null);
539        privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
540        }
541        else {
542        return null;
543        }
544        }
545         */ else {
[1174]546
[3232]547            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
548            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
549            return null;
550        }
[1174]551
[3232]552        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
[1174]553
[3232]554        X509Certificate cert = (X509Certificate) getCert();
555
556        RecipientId recId = new RecipientId();
557        recId.setSerialNumber(cert.getSerialNumber());
558        recId.setIssuer(cert.getIssuerX500Principal());
559
560        Properties props = System.getProperties();
561        Session session = Session.getDefaultInstance(props, null);
562
563        String fullMsg = data.get("body");
564        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
565        MimeMessage encriptedMsg = new MimeMessage(session, is);
566
567        Provider prov = Security.getProvider(providerName);
568        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
569            System.out.println("Serviços do provider " + providerName + ":\n" + prov.getInfo());
570            for (Provider.Service service : prov.getServices()) {
571                System.out.println(service.toString() + ": " + service.getAlgorithm());
572            }
[1174]573        }
574
[3232]575        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
576            System.out.println("Email criptografado:\n" + fullMsg);
577        }
[1174]578
[3232]579        SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(encriptedMsg);
580        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
581            System.out.println("Algoritmo de encriptação: " + m.getEncryptionAlgOID());
582        }
[1174]583
[3232]584        AlgorithmParameters algParams = m.getEncryptionAlgorithmParameters("BC");
585        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
586            System.out.println("Parâmetros do algoritmo: " + algParams.toString());
587        }
[1174]588
[3232]589        RecipientInformationStore recipients = m.getRecipientInfos();
590        RecipientInformation recipient = recipients.get(recId);
[1174]591
[3232]592        if (recipient != null) {
593            String retorno;
[1174]594
[3232]595            MimeBodyPart decriptedBodyPart = SMIMEUtil.toMimeBodyPart(recipient.getContent(privateKey, getProviderName()));
[1174]596
[3232]597            if ((new ContentType(decriptedBodyPart.getContentType())).getSubType().equalsIgnoreCase("x-pkcs7-mime")) {
598                StringBuffer sb = new StringBuffer(encriptedMsg.getSize());
[1174]599
[3232]600                for (Enumeration e = encriptedMsg.getAllHeaderLines(); e.hasMoreElements();) {
601                    String header = (String) e.nextElement();
602                    if (!header.contains("Content-Type") &&
603                            !header.contains("Content-Transfer-Encoding") &&
604                            !header.contains("Content-Disposition")) {
605                        sb.append(header);
606                        sb.append("\r\n");
607                    }
608                }
609                ByteArrayOutputStream oStream = new ByteArrayOutputStream();
610                decriptedBodyPart.writeTo(oStream);
[3633]611
612                decriptedBodyPart = null;
[3232]613                encriptedMsg = null;
[1174]614
[3232]615                sb.append(oStream.toString("iso-8859-1"));
[1174]616
[3232]617                retorno = sb.toString();
[1174]618
[3232]619            } else {
[3633]620               
[3232]621                encriptedMsg.setContent(decriptedBodyPart.getContent(), decriptedBodyPart.getContentType());
622                encriptedMsg.saveChanges();
623
624                ByteArrayOutputStream oStream = new ByteArrayOutputStream();
625                encriptedMsg.writeTo(oStream);
626                encriptedMsg = null;
627
628                retorno = oStream.toString("iso-8859-1");
629            }
630
[3394]631            // Corrige problemas com e-mails vindos do Outlook
632            // Corrige linhas que são terminadas por \n (\x0A) e deveriam ser terminadas por \r\n (\x0D\x0A)
633            Pattern p = Pattern.compile("(?<!\\r)\\n");
634            Matcher matcher = p.matcher(retorno);
635            retorno = matcher.replaceAll(CRLF);
636
[3232]637            return retorno;
638        } else {
639            //DialogBuilder.showMessageDialog(this.parentFrame, "Não é possível ler este e-mail com o Certificado Digital apresentado!\n" +
640            //        "Motivo: Este e-mail não foi cifrado com a chave pública deste Certificado Digital.");
641            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate002"), this.setup);
642            return null;
643        }
[1174]644    }
645
[3232]646    /**
647     * Pega as credenciais de login do dono do certificado do serviço de verificação de certificados
648     * @param   pin                     pin para acessar o token
649     * @return  resposta        Array de Strings em que:
650     *                                          Indice 0: código de retorno;
651     *                                          Indice 1: username se código de retorno for 0, senão mensagem de erro;
652     *                                          Indice 2: senha decriptada se código de retorno for 0, senão não existe;
653     * @throws SSLHandshakeException
654     * @throws HttpException
655     * @throws IOException
656     * @throws GeneralSecurityException
657     */
658    public String[] getCredentials(String pin) throws SSLHandshakeException, HttpException, IOException, GeneralSecurityException {
[1174]659
[3232]660        String[] resposta = null;
[1174]661
[3633]662        if (this.selectedCertificateAlias == null){
663            return resposta;
664        }
665
[3232]666        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
667            System.out.println("Proxy Configurado no browser: " + System.getProperty("http.proxyHost") + ":" + System.getProperty("http.proxyPort"));
668        }
[1174]669
[3232]670        // Registra novo protocolo https, utilizando nova implementação de AuthSSLProtocolSocketFactory
671        Protocol.registerProtocol("https", new Protocol("https",
672                (ProtocolSocketFactory) new AuthSSLProtocolSocketFactory(TRUST_STORES_URLS, TRUST_STORES_PASSWORDS, this.setup),
673                443));
[1174]674
[3232]675        HttpClient httpclient = new HttpClient();
676        // Define um método post para o link do serviço de verificação de certificados
677        if (System.getProperty("http.proxyHost") != null && System.getProperty("http.proxyPort") != null) {
678            httpclient.getHostConfiguration().setProxy(System.getProperty("http.proxyHost"),
679                    Integer.parseInt(System.getProperty("http.proxyPort")));
680        }
681        PostMethod httppost = new PostMethod("https://" + hostAddress + "/security/vercert.php");
[1174]682
[3232]683        try {
684            // Adiciona parâmetro certificado no método post, executa o método, pega a resposta do servidor
685            // como uma string com CRLF de separador entre os campos e gera um array de Strings
686            httppost.addParameter("certificado", Base64Utils.der2pem(cert.getEncoded()));
687            httpclient.executeMethod(httppost);
688            resposta = httppost.getResponseBodyAsString().split(CRLF);
[1174]689
[3232]690            if (resposta.length > 2) {
691                if (Integer.parseInt(resposta[0].trim()) == 0) {
692                    // Se código da resposta for zero, decripta a senha criptografada do usuário
693                    resposta[2] = decriptPassword(resposta[2].trim(), pin);
[1174]694                }
[3232]695            }
[1174]696
[3232]697        } catch (IOException e) {
698            // Se for instância de SSLHandshakeException faz um cast para este tipo e lança a exceção novamente
699            // Isto é usado para diferenciar o tipo de falha, para que a mensagem para o usuário seja mostrada de
700            // acordo.
701            if (e instanceof SSLHandshakeException) {
702                throw (SSLHandshakeException) e;
703            }
704            // senão lança novamente a exceção do tipo IOException
705            throw e;
706        } finally {
707            // fecha a conexão
708            httppost.releaseConnection();
[1174]709        }
710
[3232]711        return resposta;
712    }
[1174]713
[3232]714    /**
715     * Decripta a senha criptografada
716     * @param encodedPassword senha criptografada e codificada em base64 para ser decriptada
717     * @param pin pin para acessar a KeyStore
718     * @return decodedPassword
719     * @throws GeneralSecurityException se algum problema ocorrer na decriptação da senha.
720     */
721    public String decriptPassword(String encodedPassword, String pin) throws GeneralSecurityException {
[1174]722
[3232]723        String decodedPassword = new String();
[1174]724
[3232]725        // Pega a chave privada do primeiro certificado armazenado na KeyStore
[3633]726        Key privateKey = this.keyStore.getKey(selectedCertificateAlias, pin.toCharArray());
[1174]727
[3232]728        // Inicializa os cipher com os parâmetros corretos para realizar a decriptação
729        Cipher dcipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
730        dcipher.init(Cipher.DECRYPT_MODE, privateKey);
[1174]731
[3232]732        // Decodifica a senha em base64 e a decripta
733        decodedPassword = new String(dcipher.doFinal(Base64Utils.base64Decode(encodedPassword)));
[1174]734
[3232]735        return decodedPassword.trim();
[1174]736
[3232]737    }
[1174]738
[3232]739    /**
740     * Carrega um novo SecurityProvider
741     * @param pkcs11Config Linha de configuração do SmartCard ou Token
742     * @throws KeyStoreException Quando não conseguir iniciar a KeyStore, ou a lib do Token
743     *                                                   ou Smartcard não foi encontrada, ou o usuário não inseriu o Token.
744     */
745    private void loadKeyStore() throws KeyStoreException {
[1174]746
[3232]747        //Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(pkcs11Config.getBytes()));
748        //Security.addProvider(pkcs11Provider);
749        this.keyStore = KeyStore.getInstance("PKCS11");
750        this.providerName = keyStore.getProvider().getName();
[1174]751
[3232]752    }
[1174]753
[3232]754    /**
755     *  Abre a keystore passando o pin
756     *  @param pin pin para acessar o Token
757     */
758    public void openKeyStore(char[] pin) throws IOException {
[3633]759        // TODO:  Verify if object DigitalCertificate was initiated
[3232]760        try {
[1174]761
[3232]762            if (this.keyStore.getType().equals("PKCS11")) {
763                this.keyStore.load(null, pin);
764            } else {
765                this.keyStore.load(this.pkcs12Input, pin);
766            }
[1174]767
[3633]768            List<String> aliases = new ArrayList<String>();
769            for (Enumeration<String> certificateList = keyStore.aliases(); certificateList.hasMoreElements();){
770                aliases.add(certificateList.nextElement());
771            }
[3232]772
[3633]773            // selecionador de certificado
774            this.selectedCertificateAlias = DialogBuilder.showCertificateSelector(this.parentFrame, this.setup, aliases);
775            if (this.selectedCertificateAlias != null){
776                this.cert = this.keyStore.getCertificate(this.selectedCertificateAlias);
777           
778                System.out.println("Aliases (" + this.keyStore.size() + "): ");
779                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
780                    for (Enumeration alias = this.keyStore.aliases(); alias.hasMoreElements();) {
781                        System.out.println(alias.nextElement());
782                    }
783                }
784            }
[1174]785
[3232]786        } catch (GeneralSecurityException e) {
787            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
788                e.printStackTrace();
789            }
[1174]790        }
791
[3232]792    }
[1174]793
[3232]794    /**
795     * @return the cert
796     */
797    Certificate getCert() {
798        return this.cert;
799    }
[1174]800
[3232]801    /**
802     * @param cert the cert to set
803     */
804    void setCert(Certificate cert) {
805        this.cert = cert;
806    }
[1174]807}
Note: See TracBrowser for help on using the repository browser.