我正在尝试使用 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 代理。电子邮件现在正在安全地流动!