source: branches/2.2.0.1/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java @ 4198

Revision 4198, 30.2 KB checked in by rafaelraymundo, 13 years ago (diff)

Ticket #1813 - Problema com mais de um driver de token configurado.

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