前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unrecognized SSL message, plaintext connection?

Unrecognized SSL message, plaintext connection?

作者头像
高爽
发布2017-12-28 15:18:35
5.6K0
发布2017-12-28 15:18:35
举报
文章被收录于专栏:高爽的专栏

  以这个错误信息为文章标题是不是更醒目一点,这是JavaMail使用SSL的方式登录邮箱时抛出的异常。代码如:

代码语言:javascript
复制
public class JavaMailTest1 {
	public static void main(String[] args) throws MessagingException {
		Properties props = new Properties();
		props.setProperty("mail.debug", "true");
		props.setProperty("mail.smtp.auth", "true");
		props.setProperty("mail.transport.protocol", "smtp");
		
		// SSL
		props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		props.setProperty("mail.smtp.socketFactory.fallback", "false");
		props.setProperty("mail.smtp.port", "465");
		props.setProperty("mail.smtp.socketFactory.port", "465");
		
		Session session = Session.getInstance(props);
		
		Message msg = new MimeMessage(session);
		msg.setText("你好吗?");
		msg.setFrom(new InternetAddress("发件箱地址"));
		
		Transport transport = session.getTransport();
		transport.connect("smtp.sina.com", "用户名", "密码");
		transport.sendMessage(msg, new Address[] {new InternetAddress("收件箱地址")});
		transport.close();
	}
}

       解决该问题方式:将端口号修改成465。通常服务器的SSL端口是443,邮箱服务器中的是465。修改后,运行程序,又会出现一个新的异常:

代码语言:javascript
复制
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1972)
	at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:642)
	at javax.mail.Service.connect(Service.java:295)
	at javax.mail.Service.connect(Service.java:176)

       这通常意味着是该服务器使用的是测试证书(使用密钥工具可能产生的),而不是从一个著名的商业证书颁发机构如Verisign或GoDaddy的证书。 在此情况下,但由于JSSE(Java(TM)SecureSocketExtension)不能假定一个交互式的用户存在,它只是在默认情况下会抛出异常。解决该问题的思路就是将你要连接的SSL服务器的证书添加为JSSE受信任的证书。那如何产生证书呢?通过下面的代码(如果没有看到请刷新页面):

代码语言:javascript
复制
/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * http://blogs.sun.com/andreas/resource/InstallCert.java
 * Use:
 * java InstallCert hostname
 * Example:
 *% java InstallCert ecc.fedora.redhat.com
 */

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Class used to add the server's certificate to the KeyStore
 * with your trusted certificates.
 */
public class InstallCert {

    public static void main(String[] args) throws Exception {
        String host;
        int port;
        char[] passphrase;
        if ((args.length == 1) || (args.length == 2)) {
            String[] c = args[0].split(":");
            host = c[0];
            port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            String p = (args.length == 1) ? "changeit" : args[1];
            passphrase = p.toCharArray();
        } else {
            System.out.println("Usage: java InstallCert [:port] [passphrase]");
            return;
        }

        File file = new File("jssecacerts");
        if (file.isFile() == false) {
            char SEP = File.separatorChar;
            File dir = new File(System.getProperty("java.home") + SEP
                    + "lib" + SEP + "security");
            file = new File(dir, "jssecacerts");
            if (file.isFile() == false) {
                file = new File(dir, "cacerts");
            }
        }
        System.out.println("Loading KeyStore " + file + "...");
        InputStream in = new FileInputStream(file);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();

        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory factory = context.getSocketFactory();

        System.out.println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }

        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }

        BufferedReader reader =
                new BufferedReader(new InputStreamReader(System.in));

        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = chain[i];
            System.out.println
                    (" " + (i + 1) + " Subject " + cert.getSubjectDN());
            System.out.println("   Issuer  " + cert.getIssuerDN());
            sha1.update(cert.getEncoded());
            System.out.println("   sha1    " + toHexString(sha1.digest()));
            md5.update(cert.getEncoded());
            System.out.println("   md5     " + toHexString(md5.digest()));
            System.out.println();
        }

        System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
        String line = reader.readLine().trim();
        int k;
        try {
            k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
        } catch (NumberFormatException e) {
            System.out.println("KeyStore not changed");
            return;
        }

        X509Certificate cert = chain[k];
        String alias = host + "-" + (k + 1);
        ks.setCertificateEntry(alias, cert);

        OutputStream out = new FileOutputStream("jssecacerts");
        ks.store(out, passphrase);
        out.close();

        System.out.println();
        System.out.println(cert);
        System.out.println();
        System.out.println
                ("Added certificate to keystore 'jssecacerts' using alias '"
                        + alias + "'");
    }

    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();

    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }

    private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }

}

        编译该类并运行,运行时要传一个参数:SSL服务器域名:端口号,如:java InstallCert smtp.sina.com:465,不输端口的话默认为443。运行之后:

代码语言:javascript
复制
Loading KeyStore C:\Program Files\Java\jre7\lib\security\cacerts...
Opening connection to smtp.sina.com:465...
Starting SSL handshake...

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.ssl.Alerts.getSSLException(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
        at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
        at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
        at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
        at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
        at sun.security.ssl.Handshaker.processLoop(Unknown Source)
        at sun.security.ssl.Handshaker.process_record(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
        at InstallCert.main(InstallCert.java:97)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
        at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
        at sun.security.validator.Validator.validate(Unknown Source)
        at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
        at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
        at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:192)
        at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(Unknown Source)
        ... 9 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
        at java.security.cert.CertPathBuilder.build(Unknown Source)
        ... 17 more

Server sent 1 certificate(s):

 1 Subject CN=*.sina.com, O="Sina.com Technology(China)Co.,ltd", L=Beijing, ST=Beijing, C=CN, SERIALNUMBER=mL/iTnzl-0Pr1rH-6U2RZH/h3zFjZxoK
   Issuer  CN=GeoTrust SSL CA, O="GeoTrust, Inc.", C=US
   sha1    43 a9 5b bc 9b 86 85 99 e3 21 63 af b0 09 78 4a 67 25 d7 a1
   md5     90 c2 45 da 67 68 cd c2 44 56 21 ef ed c6 6b 5e

Enter certificate to add to trusted keystore or 'q' to quit: [1]

       抛出了与我们程序中同样的错误,这里我们输入1,回车。它又输出了一堆信息,这里不粘了,这时证书已经生成了,在哪呢?在InstallCert.java所在的目录中,有一个名为jssecacerts的文件,这就是我们要的证书。将其放到我们程序所在的JSSE中,$JAVA_HOME/jre/lib/security。这时,再次运行我们的发邮件程序,发送成功。

更新2014-01-04

       今天无意中看到了JavaMail有这样的协议支持描述:

代码语言:javascript
复制
Protocol	Store or	Uses	Supports
Name		Transport?	SSL?	STARTTLS?
-------------------------------------------------
imap		Store		No	    Yes
imaps		Store		Yes	    N/A
pop3		Store		No	    Yes
pop3s		Store		Yes	    N/A
smtp		Transport	No	    Yes
smtps		Transport	Yes	    N/A

       Transport使用SSL连接邮箱协议名称需要使用smtps,而不是smtp,那前面提到的程序只需这样:

代码语言:javascript
复制
public class JavaMailTest1 {
	public static void main(String[] args) throws MessagingException {
		Properties props = new Properties();
		props.setProperty("mail.debug", "true");
		props.setProperty("mail.smtp.auth", "true");
		
		// 协议名称设置为smtps,会使用SSL
		props.setProperty("mail.transport.protocol", "smtps");
		
		Session session = Session.getInstance(props);
		
		Message msg = new MimeMessage(session);
		msg.setText("你好吗?");
		msg.setFrom(new InternetAddress("发件箱地址"));
		
		Transport transport = session.getTransport();
		transport.connect("smtp.sina.com", "用户名", "密码");
		transport.sendMessage(msg, new Address[] {new InternetAddress("收件箱地址")});
		transport.close();
	}
}

       这样,JavaMail会自动使用SSL,并且使用465端口。

       参考:https://java.net/projects/javamail/pages/InstallCerthttp://infposs.blogspot.com/2013/06/installcert-and-java-7.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2014-01-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档