source: branches/2.3/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java @ 5024

Revision 5024, 30.1 KB checked in by rafaelraymundo, 13 years ago (diff)

Ticket #2256 - Token iKey 2032 não consegue acessar certificado do usuário.

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