Busca no Blog

domingo, 11 de dezembro de 2011

Resolução: Problemas ao enviar email em java (javamail) depois de usar uma certificação digital (trustStore) na aplicação

Dúvidas em programação Java para Web? Formação Java Web.
Há alguns dias eu me deparei com um problema. Estava fazendo uma aplicação relativamente simples que precisava autentificar via um certificado digital (usando truststore e keystore) em java para chamar um serviço. Utilizei o axis para gerar o cliente do web service.Depois disso fui para parte da certificação digital. Tentei diversos meios, sofisticados e simples para usar a certificação digital, mas a única forma que eu consegui fazer isso funcionar foi colocando o código abaixo:



System.setProperty("javax.net.ssl.keyStoreType", "PKCS12"); System.setProperty("javax.net.ssl.keyStore", "prefeituras-pkcs12.pfx");
System.setProperty("javax.net.ssl.keyStorePassword",CertificacaoDigitalConstantes.PASSWORD_KEYSTORE);
System.setProperty("javax.net.ssl.trustStoreType", "JCEKS");
System.setProperty("javax.net.ssl.trustStore", "prefeituras_ts.jks");
System.setProperty("javax.net.ssl.trustStorePassword", CertificacaoDigitalConstantes.PASSWORD_TRUTSTORE);

Mas isso desencadeou um problema, pois após a chamada do serviço era necessário enviar e-mails. Já tinha feito um código utilizando javamail, que enviava os e-mails sem problemas. Ao enviar o e-mail, o programa autentificava no servidor de e-mail da empresa e enviava a mensagem. Mas depois de certificar ocorria o erro:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Descobri que esse erro se devia a seguinte linha do código:

System.setProperty("javax.net.ssl.trustStore", "certificao.jks"); 

Estava havendo um conflito de autentificação, pois ao autentificar no e-mail e ao certificar estava usando SSL.Tentei limpar essa propriedade, mas não adiantou, pois mesmo limpando, as propriedades do máquina virtual não é recarregada, e seria necessário reiniciar a máquina java para essa certificação sair do sistema.
Bem tendo em mente que que foi feita uma forma de autentificar "força-bruta". Tentei mudar essa forma de autentificação digital. Procurei em diversas páginas, pesquisei, descobri várias formas de não colocar o truststore no system, e somente na chamada do serviço, mas nenhuma funcionou. Aí parti para outro ponto. A autenficação do e-mail. Procurei em foruns a resolução desse problema. Vi várias
pessoas dizendo que havia passado pela mesma situação, que não conseguia enviar e-mail depois de usar o certificado da receita, por exemplo. Mas não havia nenhuma resposta sobre como resolver isso, seja uma forma simples ou complicada Não havia nada. È uma coisa simples não? Não poderia ser impossível, certo? Resolvi pesquisar sobre as Properties do javamail. Em meu código havia a seguinte linha:

props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory" );

Me pareceu que seria nesse ponto onde se colocava o SSL socket para autentificar no e-mail. Pensei, como eu crio um socketFactory para não precisar usar o padrão do System? Esse foi outro desafio.
Criei diversas classes que estendiam SSLSocketFactory, mas nenhuma funcionou, apresentava erros, as mensagens davam a entender que faltava coisas para implementar. Depois de muita tentativa e erro
descobri que já existia uma classe SSLSocket para e-mail: MailSSLSocketFactory . Assim eu substitui:

props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

Por

MailSSLSocketFactory sf=null;
try
{
     sf = new MailSSLSocketFactory();
}
catch (GeneralSecurityException e1)"
{
     e1.printStackTrace();
}
sf.setTrustAllHosts(true);
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.ssl.socketFactory", sf);

E funcionou!!!! E tão bom quando as coisas funcionam.

10 comentários:

  1. Parabéns... muito bom o seu artigo.

    ResponderExcluir
    Respostas
    1. Obrigada pelo elogio, só de você ter usado o termo artigo eu fiquei tão feliz. Como disse esse era um problema simples, que não encontrei uma solução em foruns, acabei descobrindo uma e coloquei aqui, pois achei que podia ajudar. Valeu pelo comentário.

      Excluir
  2. Apenas para constar!
    Salvou minha pele essa solucao tb !!
    Obrigado!

    ResponderExcluir
  3. Cintia, você está de parabéns! Testei e funcionou perfeitamente. Muito obrigado, ajudou muitoooooo.

    ResponderExcluir
  4. Obrigada Cíntia!! Já tinha tentado de tudo e nada deu certo, mas isso fez funcionar!!!
    Que Deus te abençoe muito!!

    ResponderExcluir
  5. De nada, Anônimo. É bom saber que foi útil ;)

    ResponderExcluir
  6. Boa tarde,
    Cíntia, seu artigo está muito bom, porém vou colocar abaixo dois parâmetros a mais que podem ajudar também. Ocorre que nem todos os servidores de email tem um certificado emitido por um uma entidade certificadora, vários são "auto assinados", ou seja geram seu próprio certificado.
    A sua solução apresentada, vai funcionar corretamente com SSL ou TLS do gmail por exemplo, porém os auto assinados que usam STARTTLS não.
    Para corrigir isso, uma alternativa (com certeza tem outras formas) é adicionar as linhas abaixo:

    props.put("mail.smtp.ssl.checkserveridentity", "false");
    props.put("mail.smtp.ssl.trust", "*");

    Ou seja, desativo SSL, e digo que qualquer certificado é confiável. Claro que nesse ponto não estou falando de segurança, mas funciona.. rs

    Sucesso e parabéns pelo artigo

    ResponderExcluir
  7. Parabéns Cintia. Já tinha desistido, quando encontrei esse seu post. Resolveu meu problema, obrigado.

    ResponderExcluir