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

Revision 3394, 28.0 KB checked in by rafaelraymundo, 14 years ago (diff)

Ticket #1265 - Tratamento de mensagens encapsuladas do Outlook.

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