Apache James TLS 困境

Apache James TLS 困境

我正在尝试使用 Apache James v2.3.2.1 设置一个小型电子邮件服务器。我安装的 Java 版本是 jre1.8.0_131。我的服务器运行的是 CentOS 6.9。服务器正在运行,但我无法让客户端通过 TLS 正确连接。

首先,我生成了一个这样的密钥:

keytool -genkey -alias james -keyalg RSA -keystore ./keystore

对于主题的通用名称,我提供了服务器的 FQDN,因为我将使用它,因此为“abc”

然后我更新了 James config.xmlserver-sockets部分以从所述密钥库加载:

<factory name="ssl" class="org.apache.avalon.cornerstone.blocks.sockets.TLSServerSocketFactory">
    <ssl-factory>
           <keystore>
              <file>conf/keystore</file>
              <password>?</password>
              <key-password>?</key-password>
              <type>JKS</type>
              <protocol>TLS</protocol>
              <algorithm>SunX509</algorithm>
              <authenticate-client>false</authenticate-client>
           </keystore>
        </ssl-factory>
     </factory>

服务器正常启动,打开调试输出后,我可以看到它正在读取密钥库:

Running Phoenix:

Phoenix 4.2

***
found key for : james
chain [0] = [
[
  Version: V3
  Subject: CN=a.b.c, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=US
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: ...
  public exponent: ...
  Validity: [From: Thu Apr 20 13:11:08 EDT 2017,
               To: Wed Jul 19 13:11:08 EDT 2017]
  Issuer: CN=a.b.c, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=US
  SerialNumber: [    51a402ba]

...

adding as trusted cert:
  Subject: CN=a.b.c, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=US
  Issuer: CN=a.b.c, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=US
  Algorithm: RSA; Serial number: 0x51a402ba
  Valid from Thu Apr 20 13:11:08 EDT 2017 until Wed Jul 19 13:11:08 EDT 2017

但是,我还收到大量(100+)不可用的密码消息,这似乎太多了:

....
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_anon_WITH_AES_128_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_RC4_128_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_NULL_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_RC4_128_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_RC4_128_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
...

我尝试了两种不同的方法来连接服务器。主要方法是通过 Mozilla Thunderbird 作为基础电子邮件客户端。它表明它无法连接到服务器,并且没有有用的附加信息。但是,在服务器控制台输出中(启用了 Java SSL 调试输出),我看到:

Allow unsafe renegotiation: true
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
default Worker #2, setSoTimeout(300000) called
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 for TLSv1.1
default Worker #6, READ: TLSv1 Handshake, length = 181
*** ClientHello, TLSv1.2
RandomCookie:  GMT: ... bytes = { ... }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, Unknown 0xcc:0xa9, Unknown 0xcc:0xa8, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension server_name, server_name: [type=host_name (0), value=a.b.c]
Unsupported extension type_23, data:
Extension renegotiation_info, renegotiated_connection: <empty>
Extension elliptic_curves, curve names: {unknown curve 29, secp256r1, secp384r1, secp521r1}
Extension ec_point_formats, formats: [uncompressed]
Unsupported extension type_35, data:
Unsupported extension status_request, data: 01:00:00:00:00
Unsupported extension type_18, data:
Unsupported extension type_65283, data:
Extension signature_algorithms, signature_algorithms: SHA256withECDSA, SHA384withECDSA, SHA512withECDSA, Unknown (hash:0x8, signature:0x4), Unknown (hash:0x8, signature:0x5), Unknown (hash:0x8, signature:0x6), SHA256withRSA, SHA384withRSA, SHA512withRSA, SHA1withECDSA, SHA1withRSA
***
%% Initialized:  [Session-2, SSL_NULL_WITH_NULL_NULL]
default Worker #6, handling exception: java.lang.RuntimeException: Could not generate DH keypair
%% Invalidated:  [Session-2, SSL_NULL_WITH_NULL_NULL]

在 POP3 日志中,有一个令人印象深刻的堆栈跟踪:

20/04/17 14:41:27 ERROR pop3server: Exception during connection from x.x.x.x (x.x.x.x) : Connection has been shutdown: javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
        at sun.security.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.java:1541)
        at sun.security.ssl.AppInputStream.read(AppInputStream.java:95)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
        at org.apache.james.util.CRLFTerminatedReader.read(CRLFTerminatedReader.java:153)
        at org.apache.james.util.CRLFTerminatedReader.readLine(CRLFTerminatedReader.java:113)
        at org.apache.james.pop3server.POP3Handler.readCommandLine(POP3Handler.java:424)
        at org.apache.james.pop3server.POP3Handler.handleConnection(POP3Handler.java:277)
        at org.apache.james.util.connection.ServerConnection$ClientConnectionRunner.run(ServerConnection.java:432)
        at org.apache.excalibur.thread.impl.ExecutableRunnable.execute(ExecutableRunnable.java:55)
        at org.apache.excalibur.thread.impl.WorkerThread.run(WorkerThread.java:116)
Caused by: javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1906)
        at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1889)
        at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1815)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:128)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297)
        at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
        at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
        at java.io.BufferedWriter.flush(BufferedWriter.java:254)
        at java.io.PrintWriter.flush(PrintWriter.java:320)
        at org.apache.james.util.InternetPrintWriter.println(InternetPrintWriter.java:92)
        at org.apache.james.util.InternetPrintWriter.println(InternetPrintWriter.java:189)
        at org.apache.james.pop3server.POP3Handler.handleConnection(POP3Handler.java:274)
        ... 3 more
Caused by: java.lang.RuntimeException: Could not generate DH keypair
        at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:142)
        at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:103)
        at sun.security.ssl.ServerHandshaker.setupEphemeralDHKeys(ServerHandshaker.java:1416)
        at sun.security.ssl.ServerHandshaker.trySetCipherSuite(ServerHandshaker.java:1206)
        at sun.security.ssl.ServerHandshaker.chooseCipherSuite(ServerHandshaker.java:1026)
        at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:741)
        at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:224)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:747)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
        ... 13 more
Caused by: java.security.NoSuchAlgorithmException: DiffieHellman KeyPairGenerator not available
        at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:218)
        at sun.security.ssl.JsseJce.getKeyPairGenerator(JsseJce.java:260)
        at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:126)
        ... 25 more

我做的第二个测试是尝试通过 openssl(OpenSSL 1.0.1e-fips 2013 年 2 月 11 日)客户端调用来验证 TLS 层:

[user@host ~]$ openssl s_client -msg -connect a.b.c:995 -msg -debug
CONNECTED(00000003)
write to ...
0000 - ...
0010 - ...
0020 - ...
0030 - ...
0040 - ...
0050 - ...
0060 - ...
0070 - ...
0080 - ...
0090 - ...
00a0 - ...
00b0 - ...
00c0 - ...
00d0 - ...
00e0 - ...
00f0 - ...
>>> TLS 1.2 Handshake [length 00f2], ClientHello
    ...
read from ...
0000 - ...
<<< TLS 1.2 Alert [length 0002], fatal internal_error
    02 50
140124392265544:error:14077438:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert internal error:s23_clnt.c:744:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 247 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---

同样的错误/堆栈跟踪也出现在 James 端。

在 James 的 POP3 日志中搜索异常链,会发现 JVM 可能无法处理正确的密钥大小,但它们都更老了,并且涉及 Java5/6 时间范围内的问题,表明 6+ 已修复该问题。基本异常声称 DH 交换不可用这一事实似乎很重要,但我在网上找到的其他来源表明,这实际上是一个包罗万象的握手失败异常,单独找出原因可能毫无意义。

这肯定是我忽略了或者手指笨拙造成的简单事情?

答案1

我仍然没有找到问题的答案,但我已经解决了问题:我将 James 设置为仅接受本地、不安全的连接,然后用于stunnel设置面向外部的 TLS 代理。电子邮件现在正在安全地流动!

相关内容