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

Revision 5898, 41.0 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2256 - Problemas com o token iKey 2032 / 4000.

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 java.util.logging.Level;
34import java.util.logging.Logger;
35import javax.crypto.Cipher;
36import javax.mail.Message;
37import javax.mail.MessagingException;
38import javax.mail.Session;
39import javax.mail.internet.MimeBodyPart;
40import javax.mail.internet.MimeMessage;
41import javax.mail.internet.MimeMultipart;
42import javax.net.ssl.SSLHandshakeException;
43import javax.security.auth.login.LoginException;
44
45import org.apache.commons.httpclient.HttpClient;
46import org.apache.commons.httpclient.HttpException;
47import org.apache.commons.httpclient.methods.PostMethod;
48import org.apache.commons.httpclient.protocol.Protocol;
49import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
50import org.bouncycastle.asn1.ASN1EncodableVector;
51import org.bouncycastle.asn1.cms.AttributeTable;
52import org.bouncycastle.asn1.smime.SMIMECapability;
53import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
54import org.bouncycastle.mail.smime.SMIMEException;
55import org.bouncycastle.mail.smime.SMIMESignedGenerator;
56
57import br.gov.serpro.ui.DialogBuilder;
58import br.gov.serpro.util.Base64Utils;
59import java.io.BufferedInputStream;
60import java.io.BufferedReader;
61import java.io.InputStreamReader;
62import java.io.OutputStream;
63import java.io.OutputStreamWriter;
64import java.io.StringReader;
65import java.security.AlgorithmParameters;
66import java.security.cert.CertificateEncodingException;
67import java.text.DateFormat;
68import java.util.HashMap;
69import java.util.Locale;
70import java.util.regex.Matcher;
71import java.util.regex.Pattern;
72import javax.activation.CommandMap;
73import javax.activation.MailcapCommandMap;
74import javax.mail.internet.ContentType;
75import javax.mail.internet.HeaderTokenizer;
76import javax.mail.internet.MimeUtility;
77import javax.mail.internet.PreencodedMimeBodyPart;
78import org.bouncycastle.cms.CMSException;
79import org.bouncycastle.cms.RecipientId;
80import org.bouncycastle.cms.RecipientInformation;
81import org.bouncycastle.cms.RecipientInformationStore;
82import org.bouncycastle.mail.smime.SMIMEEnvelopedParser;
83import org.bouncycastle.mail.smime.SMIMEUtil;
84
85/**
86 * Classe que realiza todo o trabalho realizado com o certificado
87 * @author Mário César Kolling - mario.kolling@serpro.gov.br
88 */
89//TODO: Criar exceções para serem lançadas, entre elas DigitalCertificateNotLoaded
90//TODO: Adicionar setup
91public class DigitalCertificate {
92
93    private TokenCollection tokens;
94    private String selectedCertificateAlias;
95    private Certificate cert; // Certificado extraído da KeyStore. Pode ser nulo.
96    private KeyStore keyStore; // KeyStore que guarda o certificado do usuário. Pode ser nulo.
97    private Frame parentFrame;
98    private Setup setup;
99    // TODO: Transformar pkcs12Input em uma string ou URL com o caminho para a KeyStore pkcs12
100    private FileInputStream pkcs12Input; // stream da KeyStore pkcs12. Pode ser nulo.
101    private String providerName; // Nome do SecurityProvider pkcs11 carregado. Pode ser nulo.
102    private URL pageAddress; // Endereço do host, onde a página principal do
103    private static final String HOME_SUBDIR; // Subdiretório dentro do diretório home do usuário. Dependente de SO.
104    private static final String EPASS_2000; // Caminho da biblioteca do token ePass2000. Dependente de SO.
105    private static final String CRLF = "\r\n"; // Separa campos na resposta do serviço de verificação de certificados
106    private static final String SUBJECT_ALTERNATIVE_NAME = "2.5.29.17"; // Não é mais utilizado.
107    private static final URL[] TRUST_STORES_URLS = new URL[3]; // URLs (file:/) das TrustStores, cacerts (jre),
108    // trusted.certs e trusted.jssecerts (home do usuário)
109    // Utilizadas para validação do certificado do servidor.
110    private static final String[] TRUST_STORES_PASSWORDS = null; // Senhas para cada uma das TrustStores,
111    // caso seja necessário.
112    private int keystoreStatus;
113    private static boolean useMSCapi = false;
114    public static final int KEYSTORE_DETECTED = 0;
115    public static final int KEYSTORE_NOT_DETECTED = 1;
116    public static final int KEYSTORE_ALREADY_LOADED = 2;
117
118    /*
119     * Bloco estático que define os caminhos padrões da instalação da jre,
120     * do diretório home do usuário, e da biblioteca de sistema do token ePass2000,
121     * de acordo com o sistema operacional.
122     */
123    static {
124
125        Properties systemProperties = System.getProperties();
126        Map<String, String> env = System.getenv();
127
128        /* TODO: Testar a existência de vários drivers de dispositivos. Determinar qual deve ser utilizado
129         * e guardar em uma property no subdiretório home do usuário.
130         */
131
132        if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")) {
133            HOME_SUBDIR = "/.java/deployment/security";
134            EPASS_2000 = "/usr/lib/libepsng_p11.so";
135        } else {
136            HOME_SUBDIR = "\\dados de aplicativos\\sun\\java\\deployment\\security";
137            EPASS_2000 = System.getenv("SystemRoot") + "\\system32\\ngp11v211.dll";
138            DigitalCertificate.useMSCapi = true;
139        }
140
141        try {
142            if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")) {
143                TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home") + "/lib/security/cacerts").toURI().toURL();
144                TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home") + HOME_SUBDIR + "/trusted.certs").toURI().toURL();
145                TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home") + HOME_SUBDIR + "/trusted.jssecerts").toURI().toURL();
146            } else {
147
148                TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home") +
149                        "\\lib\\security\\cacerts").toURI().toURL();
150                TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home") +
151                        HOME_SUBDIR + "\\trusted.certs").toURI().toURL();
152                TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home") +
153                        HOME_SUBDIR + "\\trusted.jssecerts").toURI().toURL();
154            }
155
156            // Define os tipos smime no mailcap
157            MailcapCommandMap mailcap = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
158
159            mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
160            mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
161            mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
162            mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
163            mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
164
165            CommandMap.setDefaultCommandMap(mailcap);
166
167
168
169        } catch (MalformedURLException e) {
170            e.printStackTrace();
171        }
172    }
173
174    /**
175     *
176     */
177    public DigitalCertificate() {
178        this.pageAddress = null;
179        this.parentFrame = null;
180    }
181
182    /**
183     * Construtor da classe. Recebe a {@link URL} da página em que a Applet está incluída.
184     * @param pageAddress URL da página em que a Applet está incluída
185     */
186    private DigitalCertificate(URL pageAddress) {
187        this.pageAddress = pageAddress;
188        this.parentFrame = null;
189    }
190
191    private DigitalCertificate(Frame parent) {
192        this.pageAddress = null;
193        this.parentFrame = parent;
194    }
195
196    public DigitalCertificate(Frame parent, Setup setup) {
197        this(parent);
198        this.setup = setup;
199    }
200
201    public DigitalCertificate(URL pageAddress, Setup setup) {
202        this(pageAddress);
203        this.setup = setup;
204    }
205
206    public static boolean isUseMSCapi() {
207        return useMSCapi;
208    }
209
210    public KeyStore getKeyStore() {
211        return keyStore;
212    }
213
214    public int getKeystoreStatus() {
215        return keystoreStatus;
216    }
217
218    public String getProviderName() {
219        return providerName;
220    }
221
222    /**
223     * Destrói a Applet, removendo o security provider inicializado se o atributo providerName
224     * for diferente de nulo.
225     */
226    public void destroy() {
227
228        AuthProvider ap = null;
229
230        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
231            System.out.println("logout no provider");
232        }
233       
234        if (this.keyStore != null && this.keyStore.getProvider() instanceof AuthProvider) {
235            ap = (AuthProvider) this.keyStore.getProvider();
236       
237            try {
238                ap.logout();
239            } catch (LoginException e) {
240                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
241                    e.printStackTrace();
242                }
243            }
244
245            if (this.providerName != null) {
246                Security.removeProvider(providerName);
247            }
248        }
249       
250        this.cert = null;
251        this.selectedCertificateAlias = null;
252        this.keyStore = null;
253        this.pkcs12Input = null;
254        this.providerName = null;
255    }
256
257    /**
258     * Procura pelo token nos locais padrões (Por enquanto só suporta o token ePass200),
259     * senão procura por um certificado A1 em System.getProperties().getProperty("user.home") +
260     * HOME_SUBDIR  + "/trusted.clientcerts" e retorna um inteiro de acordo com resultado desta procura.
261     *
262     * @author  Mário César Kolling
263     * @return  Retorna um destes valores inteiros DigitalCertificate.KEYSTORE_DETECTED,
264     *          DigitalCertificate.KEYSTORE_ALREADY_LOADED ou DigitalCertificate.KEYSTORE_NOT_DETECTED
265     * @see     DigitalCertificate
266     */
267    public int init() {
268
269        if (!DigitalCertificate.useMSCapi){
270            this.tokens = new TokenCollection(setup);
271        }
272
273        Provider[] providers = Security.getProviders();
274        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
275            for (Provider provider : providers) {
276                System.out.println(provider.getInfo());
277            }
278        }
279
280        int interfaceType = DigitalCertificate.KEYSTORE_NOT_DETECTED;
281
282        try {
283            // Tenta abrir o Token padrï¿œo (ePass2000).
284            loadKeyStore();
285
286        } catch (Exception e1) {
287
288            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
289                // Não conseguiu abrir o token (ePass2000).
290                System.out.println("Erro ao ler o token: " + e1.getMessage());
291            }
292
293            try {
294                // Tenta abrir a keyStore padrão
295                // USER_HOME/deployment/security/trusted.clientcerts
296
297                Properties props = System.getProperties();
298                pkcs12Input = new FileInputStream(props.getProperty("user.home") + HOME_SUBDIR + "/trusted.clientcerts");
299
300                // Se chegar aqui significa que arquivo de KeyStore existe.
301                keyStore = KeyStore.getInstance("JKS");
302
303            } catch (Exception ioe) {
304                // Não conseguiu abrir a KeyStore pkcs12
305                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
306                    System.out.println(ioe.getMessage());
307                }
308            }
309        }
310
311
312        if (keyStore == null) {
313            // Não conseguiu inicializar a KeyStore. Mostra tela de login com usuário e senha.
314            this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
315            //} else if (keyStore.getType().equalsIgnoreCase("pkcs11")){
316        } else {
317            // Usa certificado digital.
318            try {
319                // Testa se uma keystore já foi carregada previamente
320                if (keyStore.getType().equalsIgnoreCase("pkcs11")
321                        || keyStore.getType().equalsIgnoreCase("windows-my")) {
322                    keyStore.load(null, null);
323                } else {
324                    keyStore.load(pkcs12Input, null);
325                }
326
327                // Se chegou aqui KeyStore está liberada, mostrar tela de login sem pedir o pin.
328                this.keystoreStatus = DigitalCertificate.useMSCapi ? DigitalCertificate.KEYSTORE_DETECTED : DigitalCertificate.KEYSTORE_ALREADY_LOADED ;
329
330            } catch (ProviderException e) {
331                // Algum erro ocorreu, mostra  tela de login com usuário e senha.
332                this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
333                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
334                    e.printStackTrace();
335                }
336            } catch (IOException e) {
337                // KeyStore não está liberada, mostra tela de login com o pin.
338                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
339                    System.out.println(e.getMessage());
340                }
341                this.keystoreStatus = DigitalCertificate.KEYSTORE_DETECTED;
342            } catch (GeneralSecurityException e) {
343                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
344                    e.printStackTrace();
345                }
346            }
347        }
348
349        return keystoreStatus;
350
351    }
352
353    static public String verifyP7S(MimeMessage body){
354        try {
355
356            MimeMultipart mm = (MimeMultipart) body.getContent();
357            externalLoop:for (int i = mm.getCount() - 1; i >= 0 ; i--){
358                MimeBodyPart mbp = (MimeBodyPart) mm.getBodyPart(i);
359                if (mbp.getContentType().contains("application/pkcs7-signature")){
360                    String[] contentTypeArray = body.getHeader("Content-Type");
361                    HeaderTokenizer hTokenizer = new HeaderTokenizer(contentTypeArray[0]);
362
363                    String boundary = "";
364                    HeaderTokenizer.Token t = hTokenizer.next();
365                    for (; t.getType() != HeaderTokenizer.Token.EOF; t = hTokenizer.next()){
366                        if (t.getType() == HeaderTokenizer.Token.ATOM && t.getValue().equalsIgnoreCase("boundary=")){
367                            break;
368                        }
369                    }
370                    t = hTokenizer.next();
371                    if (t.getType() == HeaderTokenizer.Token.QUOTEDSTRING){
372                        boundary = t.getValue();
373                    }
374
375                    InputStreamReader rawStreamReader = new InputStreamReader(mbp.getRawInputStream(), "iso-8859-1");
376
377                    StringBuilder signature = new StringBuilder(256);
378                    while (rawStreamReader.ready()){
379                        char[] buffer = new char[256];
380                        rawStreamReader.read(buffer);
381                        signature.append(buffer);
382                    }
383                   
384                    String[] array = signature.toString().split("\\r\\n|(?<!\\r)\\n");
385
386                    boolean badFormat = false;
387                    for (int j = 0; j < array.length; j++){
388                        if (array[j].length() > array[0].length()){
389                            badFormat = true;
390                            break;
391                        }
392                    }
393
394                    signature = null;
395                    array = null;
396
397                    if (badFormat){
398                        BufferedInputStream parsedIS = new BufferedInputStream(mbp.getInputStream());
399                        ByteArrayOutputStream baos = new ByteArrayOutputStream(parsedIS.available());
400                       
401                        while (parsedIS.available() > 0){
402                            byte[] buffer = new byte[parsedIS.available()];
403                            parsedIS.read(buffer);
404                            baos.write(buffer);
405                        }
406
407                        Enumeration headers = headers = mbp.getAllHeaderLines();
408                        String headersString = "";
409                        while (headers.hasMoreElements()){
410                            String header = (String) headers.nextElement();
411                            headersString += header+"\r\n";
412                        }
413
414                        String base64Encoded = Base64Utils.der2pem(baos.toByteArray(), false);
415
416                        mm.removeBodyPart(i);
417                        body.saveChanges();
418       
419                        ByteArrayOutputStream oStream = new ByteArrayOutputStream();
420
421                        oStream = new ByteArrayOutputStream();
422                        body.writeTo(oStream);
423
424                        BufferedReader reader = new BufferedReader(new StringReader(oStream.toString()));
425                        OutputStream os = new ByteArrayOutputStream();
426
427                        String line = "";
428                        while ((line = reader.readLine()) != null ){
429                            if (!line.equals("--"+boundary+"--")){
430                                os.write((line+"\r\n").getBytes("iso-8859-1"));
431                            }
432                        }
433
434                        return os.toString()
435                                +"--"+boundary+"\r\n"+headersString
436                                +"\r\n"+base64Encoded
437                                +"--"+boundary+"--\r\n";
438
439                    }
440
441                    break externalLoop;
442                }
443            }
444
445        } catch (IOException ex) {
446            Logger.getLogger(DigitalCertificate.class.getName()).log(Level.SEVERE, null, ex);
447        } catch (MessagingException ex) {
448            Logger.getLogger(DigitalCertificate.class.getName()).log(Level.SEVERE, null, ex);
449        } catch (ClassCastException ex) {
450            Logger.getLogger(DigitalCertificate.class.getName()).log(Level.SEVERE, null, ex);
451        }
452
453        return null;
454    }
455
456    /**
457     * Usado para assinar digitalmente um e-mail.
458     * @param mime
459     * @return String vazia
460     */
461    public String signMail(Map<String, String> data) throws IOException, GeneralSecurityException, SMIMEException, MessagingException {
462
463        Key privateKey = null;
464        if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED) {
465            char[] pin = null;
466            if (!DigitalCertificate.useMSCapi) {
467                String sPin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
468                if (sPin != null) {
469                    pin = sPin.toCharArray();
470                }
471                else {
472                    return null;
473                }
474            }
475
476            try {
477                openKeyStore(pin);
478            }
479            catch (Exception e)
480            {
481                if (e instanceof IOException){
482                    throw new IOException(e);
483                }
484                else if (e instanceof GeneralSecurityException){
485                    throw new GeneralSecurityException(e);
486                }
487            }
488           
489            if (this.selectedCertificateAlias == null){
490                return null;
491            }
492            privateKey = this.keyStore.getKey(this.selectedCertificateAlias, pin);
493           
494        } /*
495        else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
496        if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
497        openKeyStore(null);
498        privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
499        }
500        else {
501        return null;
502        }
503        }
504         */ else {
505
506            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
507            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
508            return null;
509        }
510       
511        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
512
513        Certificate certificate = getCert();
514
515        KeyPair keypair = new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey);
516
517        // Cria a cadeia de certificados que a gente vai enviar
518        List certList = new ArrayList();
519
520        certList.add(certificate);
521
522        //
523        // create the base for our message
524        //
525        String fullMsg = data.get("body");
526
527        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
528            System.out.println("Corpo do e-mail:\n" + fullMsg + "\n");
529        }
530
531        //
532        // Get a Session object and create the mail message
533        //
534        Properties props = System.getProperties();
535        Session session = Session.getDefaultInstance(props, null);
536
537        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
538        MimeMessage unsignedMessage = new MimeMessage(session, is);
539
540        //
541        // create a CertStore containing the certificates we want carried
542        // in the signature
543        //
544        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
545            System.out.println("Provider: " + providerName);
546        }
547        CertStore certsAndcrls = CertStore.getInstance(
548                "Collection",
549                new CollectionCertStoreParameters(certList), "BC");
550
551        //
552        // create some smime capabilities in case someone wants to respond
553        //
554        ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
555
556        SMIMECapabilityVector caps = new SMIMECapabilityVector();
557
558        caps.addCapability(SMIMECapability.dES_EDE3_CBC);
559        caps.addCapability(SMIMECapability.rC2_CBC, 128);
560        caps.addCapability(SMIMECapability.dES_CBC);
561
562        SMIMESignedGenerator gen = new SMIMESignedGenerator(unsignedMessage.getEncoding());
563
564        //SMIMESignedGenerator gen = new SMIMESignedGenerator();
565
566        // TODO:  Verificar se este código é suficiente para cumprir a norma.
567        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
568            Provider.Service sha512 = this.keyStore.getProvider().getService("MessageDigest", "SHA-512");
569            Provider.Service sha256 = this.keyStore.getProvider().getService("MessageDigest", "SHA-256");
570
571            if (sha512 != null){
572                System.out.println("sha512: " + sha512.getType() + " : " + sha512.getAlgorithm());
573            } else {
574                    System.out.println("sha512: não suportado!");
575            }
576
577            if (sha256 != null){
578                    System.out.println("sha256: " + sha256.getType() + " : " + sha256.getAlgorithm());
579            } else {
580                    System.out.println("sha256: não suportado!");
581            }
582        }
583
584        // TODO: Verificar problema com MessageDigest SHA-512.
585//        if (this.keyStore.getProvider().getService("MessageDigest", "SHA-512") != null){
586//            gen.addSigner(keypair.getPrivate(), (X509Certificate) certificate, SMIMESignedGenerator.DIGEST_SHA512, new AttributeTable(signedAttrs), null);
587//        }
588//        else
589        if (this.keyStore.getProvider().getService("MessageDigest", "SHA-256") != null){
590            gen.addSigner(keypair.getPrivate(), (X509Certificate) certificate, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null);
591        }
592        else {
593            gen.addSigner(keypair.getPrivate(), (X509Certificate) certificate, SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(signedAttrs), null);
594        }
595
596        gen.addCertificatesAndCRLs(certsAndcrls);
597
598        //TODO: Extrair todos os headers de unsignedMessage
599
600        // Gera a assinatura
601        Object content = unsignedMessage.getContent();
602
603        //TODO: igualar unsignedMessage a null
604        //TODO: Pegar os headers do objeto que guardarï¿œ esses headers quando necessï¿œrio.
605
606        MimeMultipart mimeMultipartContent = null;
607        PreencodedMimeBodyPart mimeBodyPartContent = null;
608
609        if (content.getClass().getName().contains("MimeMultipart")) {
610            mimeMultipartContent = (MimeMultipart) content;
611        } else {
612            String encoding = MimeUtility.getEncoding(unsignedMessage.getDataHandler());
613            mimeBodyPartContent = new PreencodedMimeBodyPart(encoding);
614            if (encoding.equalsIgnoreCase("quoted-printable")) {
615                ByteArrayOutputStream os = new ByteArrayOutputStream();
616                OutputStream encode = MimeUtility.encode(os, encoding);
617                OutputStreamWriter writer = new OutputStreamWriter(encode, "iso-8859-1");
618                writer.write(content.toString());
619                writer.flush();
620                mimeBodyPartContent.setText(os.toString(), "iso-8859-1");
621                os = null;
622                encode = null;
623                writer = null;
624            } else {
625                mimeBodyPartContent.setText(content.toString(), "iso-8859-1");
626            }
627            mimeBodyPartContent.setHeader("Content-Type", unsignedMessage.getHeader("Content-Type", null));
628        }
629        content = null;
630
631        //
632        // extract the multipart object from the SMIMESigned object.
633        //
634        MimeMultipart mm = null;
635        if (mimeMultipartContent == null) {
636            mm = gen.generate(mimeBodyPartContent, providerName);
637            mimeBodyPartContent = null;
638        } else {
639            MimeBodyPart multipartMsg = new MimeBodyPart();
640            multipartMsg.setContent(mimeMultipartContent);
641            mm = gen.generate(multipartMsg, providerName);
642            multipartMsg = null;
643            mimeMultipartContent = null;
644        }
645
646        gen = null;
647
648        MimeMessage body = new MimeMessage(session);
649        body.setFrom(unsignedMessage.getFrom()[0]);
650        body.setRecipients(Message.RecipientType.TO, unsignedMessage.getRecipients(Message.RecipientType.TO));
651        body.setRecipients(Message.RecipientType.CC, unsignedMessage.getRecipients(Message.RecipientType.CC));
652        body.setRecipients(Message.RecipientType.BCC, unsignedMessage.getRecipients(Message.RecipientType.BCC));
653        body.setSubject(unsignedMessage.getSubject(), "iso-8859-1");
654
655        // Atrafuia o resto dos headers
656        body.setHeader("Return-Path", unsignedMessage.getHeader("Return-Path", null));
657        body.setHeader("Message-ID", unsignedMessage.getHeader("Message-ID", null));
658        body.setHeader("X-Priority", unsignedMessage.getHeader("X-Priority", null));
659        body.setHeader("X-Mailer", unsignedMessage.getHeader("X-Mailer", null));
660        body.setHeader("Importance", unsignedMessage.getHeader("Importance", null));
661        body.setHeader("Disposition-Notification-To", unsignedMessage.getHeader("Disposition-Notification-To", null));
662        body.setHeader("Date", unsignedMessage.getHeader("Date", null));
663        body.setContent(mm, mm.getContentType());
664        mm = null;
665
666        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
667            System.out.println("\nHeaders do e-mail original:\n");
668        }
669
670        body.saveChanges();
671
672        ByteArrayOutputStream oStream = new ByteArrayOutputStream();
673
674        oStream = new ByteArrayOutputStream();
675        body.writeTo(oStream);
676
677        String verified = DigitalCertificate.verifyP7S(body);
678        body = null;
679
680        if (verified != null){
681            return verified;
682        } else {
683            return oStream.toString("iso-8859-1");
684        }
685    }
686
687    /**
688     * Método utilizado para criptografar um e-mail
689     * @param source
690     * @return
691     */
692    public String cipherMail(Map<String, String> data) throws IOException, GeneralSecurityException, MessagingException, CMSException, SMIMEException {
693
694        //Pega certificado do usuário.
695
696        Key privateKey = null;
697        if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED) {
698            char[] pin = null;
699            if (!DigitalCertificate.useMSCapi) {
700               String sPin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
701               if (sPin != null) {
702                   pin = sPin.toCharArray();
703               }
704               else {
705                   return null;
706               }
707            }
708
709            openKeyStore(pin);
710            if (this.selectedCertificateAlias == null){
711                return null;
712            }
713            privateKey = this.keyStore.getKey(this.selectedCertificateAlias, pin);
714           
715        } /*
716        else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
717        if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
718        openKeyStore(null);
719        privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
720        }
721        else {
722        return null;
723        }
724        }
725         */ else {
726
727            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
728            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
729            return null;
730        }
731
732        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
733
734        X509Certificate cert = (X509Certificate) getCert();
735
736        RecipientId recId = new RecipientId();
737        recId.setSerialNumber(cert.getSerialNumber());
738        recId.setIssuer(cert.getIssuerX500Principal());
739
740        Properties props = System.getProperties();
741        Session session = Session.getDefaultInstance(props, null);
742
743        String fullMsg = data.get("body");
744        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
745        MimeMessage encriptedMsg = new MimeMessage(session, is);
746
747        Provider prov = Security.getProvider(providerName);
748        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
749            System.out.println("Serviços do provider " + providerName + ":\n" + prov.getInfo());
750            for (Provider.Service service : prov.getServices()) {
751                System.out.println(service.toString() + ": " + service.getAlgorithm());
752            }
753        }
754
755        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
756            System.out.println("Email criptografado:\n" + fullMsg);
757        }
758
759        SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(encriptedMsg);
760        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
761            System.out.println("Algoritmo de encriptação: " + m.getEncryptionAlgOID());
762        }
763
764        AlgorithmParameters algParams = m.getEncryptionAlgorithmParameters("BC");
765        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
766            System.out.println("Parâmetros do algoritmo: " + algParams.toString());
767        }
768
769        RecipientInformationStore recipients = m.getRecipientInfos();
770        RecipientInformation recipient = recipients.get(recId);
771
772        if (recipient != null) {
773            String retorno;
774
775            MimeBodyPart decriptedBodyPart = SMIMEUtil.toMimeBodyPart(recipient.getContent(privateKey, getProviderName()));
776
777            if ((new ContentType(decriptedBodyPart.getContentType())).getSubType().equalsIgnoreCase("x-pkcs7-mime")) {
778                StringBuffer sb = new StringBuffer(encriptedMsg.getSize());
779
780                for (Enumeration e = encriptedMsg.getAllHeaderLines(); e.hasMoreElements();) {
781                    String header = (String) e.nextElement();
782                    if (!header.contains("Content-Type") &&
783                            !header.contains("Content-Transfer-Encoding") &&
784                            !header.contains("Content-Disposition")) {
785                        sb.append(header);
786                        sb.append("\r\n");
787                    }
788                }
789                ByteArrayOutputStream oStream = new ByteArrayOutputStream();
790                decriptedBodyPart.writeTo(oStream);
791
792                decriptedBodyPart = null;
793                encriptedMsg = null;
794
795                sb.append(oStream.toString("iso-8859-1"));
796
797                retorno = sb.toString();
798
799            } else {
800               
801                encriptedMsg.setContent(decriptedBodyPart.getContent(), decriptedBodyPart.getContentType());
802                encriptedMsg.saveChanges();
803
804                ByteArrayOutputStream oStream = new ByteArrayOutputStream();
805                encriptedMsg.writeTo(oStream);
806                encriptedMsg = null;
807
808                retorno = oStream.toString("iso-8859-1");
809            }
810
811            // Corrige problemas com e-mails vindos do Outlook
812            // Corrige linhas que são terminadas por \n (\x0A) e deveriam ser terminadas por \r\n (\x0D\x0A)
813            Pattern p = Pattern.compile("(?<!\\r)\\n");
814            Matcher matcher = p.matcher(retorno);
815            retorno = matcher.replaceAll(CRLF);
816
817            return retorno;
818        } else {
819            //DialogBuilder.showMessageDialog(this.parentFrame, "Não é possível ler este e-mail com o Certificado Digital apresentado!\n" +
820            //        "Motivo: Este e-mail não foi cifrado com a chave pública deste Certificado Digital.");
821            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate002"), this.setup);
822            return null;
823        }
824    }
825
826    /**
827     * Pega as credenciais de login do dono do certificado do serviço de verificação de certificados
828     * @param   pin                     pin para acessar o token
829     * @param   where                   URL que será acessada para recuperar as credenciais
830     * @return  resposta        Array de Strings em que:
831     *                                          Indice 0: código de retorno;
832     *                                          Indice 1: username se código de retorno for 0, senão mensagem de erro;
833     *                                          Indice 2: senha decriptada se código de retorno for 0, senão não existe;
834     * @throws SSLHandshakeException
835     * @throws HttpException
836     * @throws IOException
837     * @throws GeneralSecurityException
838     */
839
840    public String[] getCredentials(String pin, URL where) throws SSLHandshakeException, HttpException, IOException, GeneralSecurityException {
841
842        String[] resposta = null;
843
844        if (this.selectedCertificateAlias == null){
845            return resposta;
846        }
847
848        if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
849            System.out.println("Proxy Configurado no browser: " + System.getProperty("http.proxyHost") + ":" + System.getProperty("http.proxyPort"));
850        }
851
852        // Registra novo protocolo https, utilizando nova implementação de AuthSSLProtocolSocketFactory
853        Protocol.registerProtocol("https", new Protocol("https",
854                (ProtocolSocketFactory) new AuthSSLProtocolSocketFactory(TRUST_STORES_URLS, TRUST_STORES_PASSWORDS, this.setup),
855                443));
856
857        HttpClient httpclient = new HttpClient();
858        // Define um método post para o link do serviço de verificação de certificados
859        if (System.getProperty("http.proxyHost") != null && System.getProperty("http.proxyPort") != null) {
860            httpclient.getHostConfiguration().setProxy(System.getProperty("http.proxyHost"),
861                    Integer.parseInt(System.getProperty("http.proxyPort")));
862        }
863
864        PostMethod httppost = new PostMethod(where.toExternalForm());
865       
866        try {
867            // Adiciona parâmetro certificado no método post, executa o método, pega a resposta do servidor
868            // como uma string com CRLF de separador entre os campos e gera um array de Strings
869
870            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
871                System.out.println("\nthis.cert: "+ this.getPEMCertificate()+"\n");
872            }
873
874            httppost.addParameter("certificado", this.getPEMCertificate());
875            httpclient.executeMethod(httppost);
876            resposta = httppost.getResponseBodyAsString().split(CRLF);
877
878            if (resposta.length > 2) {
879                if (Integer.parseInt(resposta[0].trim()) == 0) {
880                    // Se código da resposta for zero, decripta a senha criptografada do usuário
881                    resposta[2] = decryptPassword(resposta[2].trim(), pin);
882                }
883            }
884
885        } catch (IOException e) {
886            // Se for instância de SSLHandshakeException faz um cast para este tipo e lança a exceção novamente
887            // Isto é usado para diferenciar o tipo de falha, para que a mensagem para o usuário seja mostrada de
888            // acordo.
889            if (e instanceof SSLHandshakeException) {
890                throw (SSLHandshakeException) e;
891            }
892            // senão lança novamente a exceção do tipo IOException
893            throw e;
894        } finally {
895            // fecha a conexão
896            httppost.releaseConnection();
897        }
898
899        return resposta;
900    }
901
902    /**
903     * Decripta a senha criptografada
904     * @param encodedPassword senha criptografada e codificada em base64 para ser decriptada
905     * @param pin pin para acessar a KeyStore
906     * @return decodedPassword
907     * @throws GeneralSecurityException se algum problema ocorrer na decriptação da senha.
908     */
909    public String decryptPassword(String encodedPassword, String pin) throws GeneralSecurityException, IOException {
910
911        String decodedPassword = new String();
912
913        // Pega a chave privada do primeiro certificado armazenado na KeyStore
914        Key privateKey = pin == null ? this.keyStore.getKey(selectedCertificateAlias, null) :
915            this.keyStore.getKey(selectedCertificateAlias, pin.toCharArray());
916
917        Cipher dcipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
918        dcipher.init(Cipher.DECRYPT_MODE, privateKey);
919
920        // Inicializa os cipher com os parâmetros corretos para realizar a decriptação
921//      Cipher dcipher = DigitalCertificate.useMSCapi ?
922//            Cipher.getInstance("RSA/ECB/PKCS1Padding", Security.getProvider(this.providerName))
923//            : Cipher.getInstance("RSA/ECB/PKCS1Padding");
924//      dcipher.init(Cipher.DECRYPT_MODE, privateKey);
925
926        // Decodifica a senha em base64 e a decripta
927        decodedPassword = new String(dcipher.doFinal(Base64Utils.base64Decode(encodedPassword)));
928       
929        return decodedPassword.trim();
930
931    }
932
933    /**
934     * Carrega um novo SecurityProvider
935     * @param pkcs11Config Linha de configuração do SmartCard ou Token
936     * @throws KeyStoreException Quando não conseguir iniciar a KeyStore, ou a lib do Token
937     *                                                   ou Smartcard não foi encontrada, ou o usuário não inseriu o Token.
938     */
939    private void loadKeyStore() throws GeneralSecurityException {
940
941        try{
942            if (!DigitalCertificate.useMSCapi) {
943                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
944                    System.out.println("Carregando provider: PKCS11");
945                }
946                this.keyStore = KeyStore.getInstance("PKCS11");
947                this.providerName = keyStore.getProvider().getName();
948            }
949            else {
950                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
951                    System.out.println("Carregando provider: SunMSCAPI");
952                }
953                this.keyStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
954                this.providerName = this.keyStore.getProvider().getName();
955
956                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
957                    System.out.println(keyStore.getProvider().getName() +" carregado!");
958                }
959            }
960        }
961        catch (GeneralSecurityException kex){
962            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
963                System.out.println("Erro ao carregar provider: PKCS11");
964                Throwable cause = kex.getCause();
965                kex.printStackTrace();
966                if (cause != null){
967                    cause.printStackTrace();
968                }
969            }
970            throw kex;
971        }
972    }
973
974    Map<String, String> getAliasesList() throws IOException, KeyStoreException{
975
976        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
977            System.out.println("Getting Aliases");
978        }
979
980        Map<String, String> aliases = new HashMap<String, String>();
981     
982        for (Enumeration<String> al = this.keyStore.aliases(); al.hasMoreElements();){
983            String alias = al.nextElement();
984            X509Certificate certObj = (X509Certificate) this.keyStore.getCertificate(alias);
985           
986            StringBuilder selector = new StringBuilder();
987            // get more info to generate the value
988            // Subject's CN / Issuer's CN / Expiration Data
989            String subject = certObj.getSubjectX500Principal().getName();
990            int pInicial = subject.indexOf('=')+1;
991            int pFinal = subject.indexOf(',', pInicial);
992            selector.append(subject.substring(pInicial, pFinal)+" | ");
993
994            String issuer = certObj.getIssuerX500Principal().getName();
995            pInicial = issuer.indexOf('=')+1;
996            pFinal = issuer.indexOf(',', pInicial);
997            selector.append(issuer.substring(pInicial, pFinal)+" | ");
998
999            // TODO: get the system locale
1000            Locale locale = new Locale("pt", "BR");
1001            DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
1002            selector.append(df.format(certObj.getNotAfter())+" | ");
1003
1004            selector.append("("+certObj.getSerialNumber()+")");
1005
1006            aliases.put(alias, selector.toString());
1007           
1008        }
1009
1010        return aliases;
1011    }
1012
1013//    public void removeCertificate() throws IOException {
1014//        Token token = tokens.getRegisteredTokens().iterator().next();
1015//        token.removeCertificate();
1016//    }
1017//
1018//    public void writeCerts(char[] pin, LinkedHashMap<char[], Certificate> certs) throws IOException{
1019//        Token token = tokens.getRegisteredTokens().iterator().next();
1020//        token.getAliases();
1021//
1022//        for (Map.Entry<char[], Certificate> entry : certs.entrySet()) {
1023//            token.writeCert(pin, entry.getKey(), entry.getValue());
1024//        }
1025//    }
1026
1027    /**
1028     *  Abre a keystore passando o pin
1029     *  @param pin pin para acessar o Token
1030     */
1031    @SuppressWarnings("empty-statement")
1032    public void openKeyStore(char[] pin) throws IOException {
1033        // TODO:  Verify if object DigitalCertificate was initiated
1034        try {
1035
1036            if (this.keyStore.getType().equals("PKCS11")) {
1037                this.keyStore.load(null, pin);
1038            } else if (this.keyStore.getType().equals("Windows-MY")) {
1039                this.keyStore.load(null, null);
1040            } else {
1041                this.keyStore.load(this.pkcs12Input, pin);
1042            }
1043
1044            // selecionador de certificado
1045            this.selectedCertificateAlias = DialogBuilder.showCertificateSelector(this.parentFrame,
1046                    this.setup, this.getAliasesList());
1047            if (this.selectedCertificateAlias != null){
1048                this.cert = this.keyStore.getCertificate(this.selectedCertificateAlias);
1049           
1050                if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
1051                    System.out.println("Selected Alias: "+this.selectedCertificateAlias);
1052                    System.out.println("Aliases (" + this.keyStore.size() + "): ");
1053                    for (Enumeration alias = this.keyStore.aliases(); alias.hasMoreElements();) {
1054                        System.out.println(alias.nextElement());
1055                    }
1056                }
1057            }
1058
1059        } catch (GeneralSecurityException e) {
1060            if (this.setup.getParameter("debug").equalsIgnoreCase("true")) {
1061                e.printStackTrace();
1062            }
1063        }
1064
1065    }
1066
1067    /**
1068     * @return the cert
1069     */
1070    Certificate getCert() {
1071        return this.cert;
1072    }
1073
1074    /**
1075     * Get a PEM encoded instance of the user certificate
1076     * @return PEM encoded Certificate
1077     * @throws CertificateEncodingException
1078     */
1079    public String getPEMCertificate() throws CertificateEncodingException {
1080        if (this.cert != null){
1081            return Base64Utils.der2pem(this.cert.getEncoded(), true);
1082        }
1083        return null;
1084
1085    }
1086
1087    /**
1088     * @param cert the cert to set
1089     */
1090    void setCert(Certificate cert) {
1091        this.cert = cert;
1092    }
1093}
Note: See TracBrowser for help on using the repository browser.