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

Revision 3232, 27.8 KB checked in by rafaelraymundo, 14 years ago (diff)

Ticket #1237 - Atualizados componentes de suporte ao uso do Certificado Digital.

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