2016-07-06 559 views
0

我在理解TLS/SSL如何在电子邮件中工作时遇到一些问题。SMTP TLS证书

我有一些问题。

  1. 在我的机器,如果我调试下面的代码角落找寻未能在第一时间对“sslSocket.startHandshake()”行,但如果我再次尝试马上它工作正常。

我得到的错误消息是:“握手期间远程主机关闭连接”。

当我将相同的代码部署到暂存环境并发送电子邮件时,代码第一次正常工作。 开发和登台服务器都在同一个网络中,都没有防病毒程序运行。

我唯一能想到的就是为什么它不是第一次在开发环境中工作,是因为我正在使用调试器逐步执行代码,并且因为这样做速度较慢。

您对我为什么收到此错误有任何了解吗?

  1. 下面的代码创建一个SSL套接字。我很想知道这个代码是否足以与邮件服务器建立连接。这些SSLSocketFactory类是否自己处理证书?

2a)还是我仍然需要指定一个证书?

2b)或者是这个代码从服务器获取证书并使用证书来加密数据并将加密数据来回发送到电子邮件服务器?

我知道它应该像这里描述的那样工作: RFC 3207定义了SMTP连接如何使用加密。建立连接后,客户端发出STARTTLS命令。如果服务器接受这一点,则客户端和服务器协商加密机制。如果协商成功,随后在它们之间传递的数据将被加密。

2c)下面的代码是否在执行此操作?

socket.setKeepAlive(true); 

    SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(
      socket, 
      socket.getInetAddress().getHostAddress(), 
      socket.getPort(), 
      true);     

    sslSocket.setUseClientMode(true); 
    sslSocket.setEnableSessionCreation(true); 
    sslSocket.setEnabledProtocols(new String[]{"SSLv3", "TLSv1"}); 
    sslSocket.setKeepAlive(true); 

    // Force handshake. This can throw! 
    sslSocket.startHandshake();  

    socket = sslSocket; 

    in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
    out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 

回答

0
  1. 在我的机器,如果我调试下面的代码角落找寻未能在第一时间对“sslSocket.startHandshake()”行,但如果我再次尝试马上它工作正常。
    我得到的错误消息是:“握手期间远程主机关闭连接”。 []
    我唯一能想到的是为什么它不是第一次在开发环境中工作,是因为我正在使用调试器逐步完成代码,并且因为这个问题而变慢。

如果你只是做startHandshake()再次与底层套接字关闭它应该不会起作用。如果你回到做TCP连接(例如new Socket(host,port))和最初的SMTP交换和STARTTLS,那么是的,我希望它可以避免任何影响以前连接的问题。

是的,服务器因调试时延迟而超时很有可能,但要确定您需要检查服务器上的日志。

  1. 下面的代码创建一个SSL套接字。我很想知道这个代码是否足以与邮件服务器建立连接。这些SSLSocketFactory类是否自己处理证书?

间接,是的。 SSLSocketFactory会创建一个SSLSocket链接到SSLContext,其中包括通常从信任库文件加载的TrustManager。您的代码默认为缺省SSLContext,其中TrustManager从默认信任库(文件jssecacerts如果存在)加载,否则cacerts位于您正在运行的JRE中的lib/security目录中。如果您的JRE没有被修改(由您或您的系统授权的任何人),根据您的Java变体或Java包装,安装的JRE通常没有jssecacerts,并且包含或链接到cacerts文件(最初)包含根证书对于像赛门铁克,GoDaddy,Comodo等约一百个“知名”或已建立的证书颁发机构。

2a)或者我还需要以某种方式指定证书吗?

由于握手完成时很成功,显然不成功。

2b)或者是这个代码从服务器获取证书并使用证书加密数据并将加密数据来回发送到电子邮件服务器?

类型/排序/不完全。有些例外在这里不适用,在SSL/TLS握手中,服务器总是提供自己的证书,并且通常提供将其证书链接到受信任的根证书(例如上述赛门铁克等)的中间或“链式”证书。服务器证书通常用于验证服务器,有时是单独使用,但通常与其他机制(特别是Diffie-Hellman临时DHE或其椭圆曲线变体ECDHE)结合使用,用于建立一组多个对称密钥值,然后将其用于在两个方向上加密和认证数据。有关更完整的解释,请参阅规范问题和(多部分!)安全答案.SX https://security.stackexchange.com/questions/20803/how-does-ssl-work/

2c)下面的代码是否在执行此操作?

它正在现有套接字上启动SSLv3或TLSv1客户端会话。我不确定你在这里有什么其他问题。

你最好不要离开setEnabledProtocols()。 Sun/Oracle Java版本8是目前唯一支持的版本,默认支持TLS 1.0,1.1和1.2。 1.1,特别是1.2肯定比1.0好,并且一定要提供这样的服务器,如果服务器支持它们,它们就会被使用。 (Sun/Oracle 7的问题更多;它的实现了 1.1和1.2,但不是默认启用它们的客户端在那里我会看.getSupportedProtocols,如果1.1和1.2支持但未启用我会添加启用它们。但如果可能的话,我会升级到8.其他版本的Java,特别是IBM,在加密细节方面有很大不同。)

除非绝对必要,否则不应提供SSLv3;它现在被POODLE严重破坏(在security.SX上搜索关于POODLE的几十个Q)。我会尝试没有它,只有当服务器坚持要暂时重新启用它时,尽可能使用TLS 1.0到1.2,并同时敦促服务器升级,以便我可以再次将其删除。

+0

非常感谢您对这个非常详细的解释 - 这非常有帮助。 对于第1点,基础套接字已打开,但在事务失败或成功后关闭。所以我不认为这会影响第二次尝试,因为这是一个全新的套接字。 – Leo

+0

也谢谢你提示删除setEnabledProtocols()函数。我正在使用Java 8,所以我删除了它。在接下来的几个月里,我将创建一个电子邮件服务器,然后我可以更轻松地看到服务器端正在做什么,这样我就可以确定第一次握手时出了什么问题。有趣的是,代码正在第一次在我们的分段环境中工作。 再次感谢您的帮助! – Leo