java Mail发邮件 smtp被TLS加密认证不了的解决方案

原创 2013年12月12日 11:06:37

开始测试前,要确保发邮件的服务器的smtp服务可用。

不然会抛出异常:

 Sending the email to the following server failed : m.xxx.com:25
Caused by: javax.mail.AuthenticationFailedException: 334 NTLM supported


然后介绍下我的开发环境(context):目前用的开发框架是playframework,它帮忙封装了apache的mail工具类,代码如下:

 public static void sendMail(SendMailDto sendMailDto){
        if(sendMailDto!=null){
            HtmlEmail email = new HtmlEmail();
            email.setCharset("UTF-8");// 编码格式
            try {
                email.addTo(sendMailDto.accepterEmail);// 接收者
                email.setFrom(sendMailDto.sender, sendMailDto.name);// 发送者,姓名
                email.setSubject(sendMailDto.title);// 邮件标题
                email.setMsg(sendMailDto.content);// 发送内容
                Mail.send(email);
                Logger.info("接收邮件: "+sendMailDto.accepterEmail+" 发送成功!");
                Logger.info("发送邮件服务器: "+sendMailDto.sender);
                Logger.info("发送邮件名: "+sendMailDto.name);

            }catch (Exception e) {
                Logger.info("邮件: "+sendMailDto.accepterEmail+" 发送失败!");
                e.printStackTrace();
            }
        }
    }

在配置文件中要申明:

mail.smtp.host=xxxx
mail.smtp.user=xxxx
mail.smtp.pass=xxxx

本质上还是用的apache的setMailSession()方法。

当一切都配置好了,开始执行,还是报上面的异常,后来发现还需要在配置文件加上 mail.smtp.protocol=smtps,分析下源码:

 public static Session getSession() {
        if (session == null) {
            Properties props = new Properties();
            // Put a bogus value even if we are on dev mode, otherwise JavaMail will complain
            props.put("mail.smtp.host", Play.configuration.getProperty("mail.smtp.host", "localhost"));

            String channelEncryption;
            if (Play.configuration.containsKey("mail.smtp.protocol") && Play.configuration.getProperty("mail.smtp.protocol", "smtp").equals("smtps")) {
                // Backward compatibility before stable5
                channelEncryption = "starttls";
            } else {
                channelEncryption = Play.configuration.getProperty("mail.smtp.channel", "clear");
            }

            if (channelEncryption.equals("clear")) {
                props.put("mail.smtp.port", "25");
            } else if (channelEncryption.equals("ssl")) {
                // port 465 + setup yes ssl socket factory (won't verify that the server certificate is signed with a root ca.)
                props.put("mail.smtp.port", "465");
                props.put("mail.smtp.socketFactory.port", "465");
                props.put("mail.smtp.socketFactory.class", "play.utils.YesSSLSocketFactory");
                props.put("mail.smtp.socketFactory.fallback", "false");
            } else if (channelEncryption.equals("starttls")) {
                // port 25 + enable starttls + ssl socket factory
                props.put("mail.smtp.port", "25");
                props.put("mail.smtp.starttls.enable", "true");
                // can't install our socket factory. will work only with server that has a signed certificate
                // story to be continued in javamail 1.4.2 : https://glassfish.dev.java.net/issues/show_bug.cgi?id=5189
            }

            if (Play.configuration.containsKey("mail.smtp.localhost")) {
                props.put("mail.smtp.localhost", Play.configuration.get("mail.smtp.localhost"));            //override defaults
            }
            if (Play.configuration.containsKey("mail.smtp.socketFactory.class")) {
                props.put("mail.smtp.socketFactory.class", Play.configuration.get("mail.smtp.socketFactory.class"));
            }
            if (Play.configuration.containsKey("mail.smtp.port")) {
                props.put("mail.smtp.port", Play.configuration.get("mail.smtp.port"));
            }
            String user = Play.configuration.getProperty("mail.smtp.user");
            String password = Play.configuration.getProperty("mail.smtp.pass");
            if (password == null) {
                // Fallback to old convention
                password = Play.configuration.getProperty("mail.smtp.password");
            }
            String authenticator = Play.configuration.getProperty("mail.smtp.authenticator");
            session = null;

            if (authenticator != null) {
                props.put("mail.smtp.auth", "true");
                try {
                    session = Session.getInstance(props, (Authenticator) Play.classloader.loadClass(authenticator).newInstance());
                } catch (Exception e) {
                    Logger.error(e, "Cannot instanciate custom SMTP authenticator (%s)", authenticator);
                }
            }

            if (session == null) {
                if (user != null && password != null) {
                    props.put("mail.smtp.auth", "true");
                    session = Session.getInstance(props, new SMTPAuthenticator(user, password));
                } else {
                    props.remove("mail.smtp.auth");
                    session = Session.getInstance(props);
                }
            }

            if (Boolean.parseBoolean(Play.configuration.getProperty("mail.debug", "false"))) {
                session.setDebug(true);
            }
        }
        return session;
    }

为了向后兼容,需要在session中设置:

props.put("mail.smtp.port", "25");
props.put("mail.smtp.starttls.enable", "true");

配置好了以后再来尝试下,发现还是有异常抛出:

javax.mail.MessagingException: Could not convert socket to TLS 
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

不能把socket解析为TLS(通过上面的截图,可以看到我的邮件服务器是TLS加密了的)

不能对访问的目标提供有效证书。

到这里有两种解决方案:

1.在网上找个生成安全证书的方法按要求放在指定位置:http://www.oschina.net/question/12_19249

2.把当前smtp host设为可信任 props.put("mail.smtp.ssl.trust", "smtp服务器地址")

到此 所有问题都解决了。



总结下:

产生第一个异常的原因有以下3个:

1. 邮件服务器的smtp没有开启。

2. 用户名密码错误。

3. mail工具类版本较低,不能有效生成socket factory


解决方案:登录邮箱,在设置里面开启smtp服务,验证程序中登录邮箱的用户名密码填写正确,用高版本的mail.jar 或者在session中设置props.put("mail.smtp.port", "25");   props.put("mail.smtp.starttls.enable", "true");


产生第二异常的原因:

smtp设置了加密,或者尝试访问不受信任地址


解决方案:用工具类生成安全证书,放在\jdk1.6.0_31\jre\lib\security下面


关于apache的mail还有很多不明白的地方,上面只是记录解决问题的过程,和大家共同学习。





javaMail 邮件发送的错误总结

1.发送MIME邮件遇到的错误          JavaMail Exception: javax.net.ssl.SSLException: Unrecognized SSL message, ...
  • huanghaijin
  • huanghaijin
  • 2012年07月28日 01:56
  • 10118

java mail tls方式发送邮件

最近项目中需要邮件报警,公司邮箱只能使用TLS方式登录,测试通过的代码如下: import java.util.Properties; import javax.mail.Message; i...
  • duan19056
  • duan19056
  • 2014年03月13日 16:33
  • 6359

JAVAMail TLS送信的注意点

JAVAMail TLS送信的注意点 1. javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection ...
  • hantiannan
  • hantiannan
  • 2014年03月03日 11:52
  • 11985

Spring封装的JavaMail配置及异常处理

编码过程遇到的问题: 1. 错误:javax.mail.AuthenticationFailedException: 错误码(400 ~ 599) 这是最经常看到的, 原因可能有: ...
  • wenniuwuren
  • wenniuwuren
  • 2014年12月24日 15:38
  • 2062

Java采用SMTP协议发送邮件

 示例中需要用到两个包:mail.jar和activation.jar,其下载地址为:http://java.sun.com/products/javamail/downloads/index.htm...
  • linyiteng1220
  • linyiteng1220
  • 2008年05月13日 11:12
  • 14995

邮件服务器问题总结

邮件服务器问题总结 一、 无配置TLS 1.1异常信息:Could not convert socket to TLS org.springframework.mail.MailSendExcepti...
  • xiaojiahao_kevin
  • xiaojiahao_kevin
  • 2017年01月04日 22:14
  • 2623

IOS SOCKET编程

iPhone的标准推荐是CFNetwork 库编程,其封装好的开源库是 cocoa AsyncSocket库,用它来简化CFNetwork的调用,它提供了异步操作 主要特性有: 队列...
  • qq_31683103
  • qq_31683103
  • 2016年03月16日 01:18
  • 397

SMTP starttls选项

在 FastMail里 对其的解释是: SSL vs TLS vs STARTTLS There's often quite a bit of confusion around the d...
  • MashiMaroJ
  • MashiMaroJ
  • 2014年11月10日 14:34
  • 928

SMTP发送邮件程序(支持SSL和TLS)

  • 2012年04月24日 13:46
  • 1.4MB
  • 下载

通过TLS发送邮件

  • 2012年05月30日 15:51
  • 28KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java Mail发邮件 smtp被TLS加密认证不了的解决方案
举报原因:
原因补充:

(最多只允许输入30个字)