尝试使用 HttpClient 登录网站时出现 Java SSL 异常、协议版本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31684855/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java SSL Exception, protocol_version when trying to use HttpClient to log into a website
提问by user1813220
I have a Java program that I am trying to get to talk to a website that uses an HTTPS connection with TLSv1. (Checked with FireFox.)
我有一个 Java 程序,我试图与一个使用 HTTPS 连接和 TLSv1 的网站进行通信。(用 FireFox 检查。)
The problem is that I still get a protocol_version exception. Here is the stack trace:
问题是我仍然收到 protocol_version 异常。这是堆栈跟踪:
javax.net.ssl.SSLException: Received fatal alert: protocol_version
2015-07-28 14:48:38 [INFO ]
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2023)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.openhab.binding.honeywellwifithermostat.handler.honeywellWifiThermostatHandler.getHoneywellWebsiteAuthCookie(honeywellWifiThermostatHandler.java:91)
at org.openhab.binding.honeywellwifithermostat.handler.honeywellWifiThermostatHandler.initialize(honeywellWifiThermostatHandler.java:73)
at org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory.registerHandler(BaseThingHandlerFactory.java:116)
at org.eclipse.smarthome.core.thing.internal.ThingManager.call(ThingManager.java:480)
at org.eclipse.smarthome.core.thing.internal.ThingManager.call(ThingManager.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Here is the relevant Java source:
这是相关的 Java 源代码:
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link honeywellWifiThermostatHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author - Initial contribution
*/
public class honeywellWifiThermostatHandler extends BaseThingHandler {
private Logger logger = LoggerFactory.getLogger(honeywellWifiThermostatHandler.class);
private String userName = null;
private String passWord = null;
public honeywellWifiThermostatHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (channelUID.getId().equals(CHANNEL_1)) {
// TODO: handle command
}
}
@Override
public void initialize() {
// TODO Auto-generated method stub
Configuration conf = this.getConfig();
if (conf.get("userName") != null) {
userName = String.valueOf(conf.get("userName"));
}
if (conf.get("passWord") != null) {
passWord = String.valueOf(conf.get("passWord"));
}
logger.debug("Attempting to get auth cookie for Honeywell site.");
getHoneywellWebsiteAuthCookie(userName, passWord);
super.initialize();
}
public void getHoneywellWebsiteAuthCookie(String userName, String passWord) {
CloseableHttpClient httpclient;
try {
SSLContext ctx = SSLContexts.custom().useProtocol("TLSv1").build();
httpclient = HttpClientBuilder.create().setSslcontext(ctx).build();
HttpPost httpPost = new HttpPost("https://www.mytotalconnectcomfort.com/portal/");
List<NameValuePair> fields = new ArrayList<NameValuePair>();
fields.add(new BasicNameValuePair("UserName", userName));
fields.add(new BasicNameValuePair("Password", passWord));
httpPost.setEntity(new UrlEncodedFormEntity(fields));
CloseableHttpResponse resp = httpclient.execute(httpPost);
logger.debug("Status line: {}", resp.getStatusLine());
} catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Any ideas out there? Even an idea of how I could trace this down?
有什么想法吗?甚至是我如何追踪这一点的想法?
UPDATE
更新
I got the debug information from the Java VM. It gives a bunch of errors about unsupported cipher suites, then maybe tries to fallback to SSLv3? I'm not too strong with crypto stuff. Again, any help is much appreciated.
我从 Java VM 获得了调试信息。它给出了一堆关于不受支持的密码套件的错误,然后可能会尝试回退到 SSLv3?我不太擅长加密货币。再次,非常感谢任何帮助。
------------------------- NET DEBUG CRAP ---------------------------
trustStore is: /usr/lib/jvm/java-8-openjdk/jre/lib/security/cacerts
trustStore type is : jks
trustStore provider is :
init truststore
== REMOVED ADDING TRUSTED ROOT CERT MESSAGES ==
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1421269547 bytes = { 179, 68, 198, 137, 7, 197, 13, 106, 3, 187, 99, 160, 117, 164, 48, 226, 113, 136, 166, 199, 101, 82, 195, 192, 46, 52, 140, 181 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Extension server_name, server_name: [type=host_name (0), value=www.mytotalconnectcomfort.com]
***
[write] MD5 and SHA1 hashes: len = 219
0000: 01 00 00 D7 03 03 55 B7 DA 2B B3 44 C6 89 07 C5 ......U..+.D....
0010: 0D 6A 03 BB 63 A0 75 A4 30 E2 71 88 A6 C7 65 52 .j..c.u.0.q...eR
0020: C3 C0 2E 34 8C B5 00 00 2C C0 0A C0 14 00 35 C0 ...4....,.....5.
0030: 05 C0 0F 00 39 00 38 C0 09 C0 13 00 2F C0 04 C0 ....9.8...../...
0040: 0E 00 33 00 32 C0 08 C0 12 00 0A C0 03 C0 0D 00 ..3.2...........
0050: 16 00 13 00 FF 01 00 00 82 00 0A 00 34 00 32 00 ............4.2.
0060: 17 00 01 00 03 00 13 00 15 00 06 00 07 00 09 00 ................
0070: 0A 00 18 00 0B 00 0C 00 19 00 0D 00 0E 00 0F 00 ................
0080: 10 00 11 00 02 00 12 00 04 00 05 00 14 00 08 00 ................
0090: 16 00 0B 00 02 01 00 00 0D 00 1A 00 18 06 03 06 ................
00A0: 01 05 03 05 01 04 03 04 01 03 03 03 01 02 03 02 ................
00B0: 01 02 02 01 01 00 00 00 22 00 20 00 00 1D 77 77 ........". ...ww
00C0: 77 2E 6D 79 74 6F 74 61 6C 63 6F 6E 6E 65 63 74 w.mytotalconnect
00D0: 63 6F 6D 66 6F 72 74 2E 63 6F 6D comfort.com
ESH-safeCall-2, WRITE: TLSv1.2 Handshake, length = 219
[Raw write]: length = 224
0000: 16 03 03 00 DB 01 00 00 D7 03 03 55 B7 DA 2B B3 ...........U..+.
0010: 44 C6 89 07 C5 0D 6A 03 BB 63 A0 75 A4 30 E2 71 D.....j..c.u.0.q
0020: 88 A6 C7 65 52 C3 C0 2E 34 8C B5 00 00 2C C0 0A ...eR...4....,..
0030: C0 14 00 35 C0 05 C0 0F 00 39 00 38 C0 09 C0 13 ...5.....9.8....
0040: 00 2F C0 04 C0 0E 00 33 00 32 C0 08 C0 12 00 0A ./.....3.2......
0050: C0 03 C0 0D 00 16 00 13 00 FF 01 00 00 82 00 0A ................
0060: 00 34 00 32 00 17 00 01 00 03 00 13 00 15 00 06 .4.2............
0070: 00 07 00 09 00 0A 00 18 00 0B 00 0C 00 19 00 0D ................
0080: 00 0E 00 0F 00 10 00 11 00 02 00 12 00 04 00 05 ................
0090: 00 14 00 08 00 16 00 0B 00 02 01 00 00 0D 00 1A ................
00A0: 00 18 06 03 06 01 05 03 05 01 04 03 04 01 03 03 ................
00B0: 03 01 02 03 02 01 02 02 01 01 00 00 00 22 00 20 .............".
00C0: 00 00 1D 77 77 77 2E 6D 79 74 6F 74 61 6C 63 6F ...www.mytotalco
00D0: 6E 6E 65 63 74 63 6F 6D 66 6F 72 74 2E 63 6F 6D nnectcomfort.com
[Raw read]: length = 5
0000: 15 03 00 00 02 .....
[Raw read]: length = 2
0000: 02 46 .F
ESH-safeCall-2, READ: SSLv3 Alert, length = 2
ESH-safeCall-2, RECV TLSv1.2 ALERT: fatal, protocol_version
ESH-safeCall-2, called closeSocket()
ESH-safeCall-2, handling exception: javax.net.ssl.SSLException: Received fatal alert: protocol_version
回答by Steffen Ullrich
*** ClientHello, TLSv1.2
*** 客户端你好,TLSv1.2
You are using TLS 1.2. But unfortunately the server does not understand TLS 1.2 at all and will cause a handshake failure. Tests with my analyze-ssl.plshow:
您正在使用 TLS 1.2。但不幸的是,服务器根本不理解 TLS 1.2,会导致握手失败。使用我的analyze-ssl.pl 进行的测试显示:
* supported SSL versions with handshake used and preferred cipher(s):
* handshake protocols ciphers
* SSLv23 TLSv1 RC4-MD5
* TLSv1_2 FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
* TLSv1_1 FAILED: SSL connect attempt failed error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
* TLSv1 TLSv1 RC4-MD5
* SSLv3 FAILED: SSL connect attempt failed because of handshake problems error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version
Thus the server simply fails on TLSv1_2 handshakes.
因此,服务器只是在 TLSv1_2 握手时失败。
You are trying to set TLS 1.0 inside your code:
您正在尝试在代码中设置 TLS 1.0:
SSLContext ctx = SSLContexts.custom().useProtocol("TLSv1").build();
Unfortunately the documentationstates that 'TLSv1' is not only TLS 1.0 but
不幸的是,文档指出“TLSv1”不仅是 TLS 1.0,而且
TLSv1 Supports RFC 2246: TLS version 1.0 ; may support other versions
Thus it tries with the best version it can (TLS1.2) and fails because the server is broken. BTW, Chrome and Firefox only succeed because they retry with a lower protocol version on such errors. I don't do Java myself but based on the various documentation I've found on the net you probably need to do something like:
因此,它尝试使用最好的版本(TLS1.2),但由于服务器损坏而失败。顺便说一句,Chrome 和 Firefox 之所以成功,是因为它们在出现此类错误时使用较低的协议版本进行重试。我自己不使用 Java,但根据我在网上找到的各种文档,您可能需要执行以下操作:
sslSocket.setEnabledProtocols(new String[] {"SSLv2Hello", "TLSv1" })
which should remove the support for TLS1.1 and TLS1.2 .
这应该删除对 TLS1.1 和 TLS1.2 的支持。
回答by user1813220
Steffen,
史蒂芬,
That answer got me on the right track. For anyone else that has a future problem, here is some code that works to force an Apache HttpClient to use TLSv1:
这个答案让我走上了正轨。对于未来遇到问题的任何其他人,这里有一些代码可以强制 Apache HttpClient 使用 TLSv1:
CloseableHttpClient httpclient;
try {
SSLContext ctx = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory fac = new SSLConnectionSocketFactory(ctx, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
httpclient = HttpClientBuilder.create().setSSLSocketFactory(fac).build();
HttpPost httpPost = new HttpPost("https://mytotalconnectcomfort.com/portal/");
List<NameValuePair> fields = new ArrayList<NameValuePair>();
fields.add(new BasicNameValuePair("UserName", userName));
fields.add(new BasicNameValuePair("Password", passWord));
httpPost.setEntity(new UrlEncodedFormEntity(fields));
CloseableHttpResponse resp = httpclient.execute(httpPost);
logger.debug("Status line: {}", resp.getStatusLine());
// } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
回答by Daniel
Quick Solution(Java 8): -Djdk.tls.client.protocols=TLSv1
快速解决方案(Java 8): -Djdk.tls.client.protocols=TLSv1
Details: I had the same problem as in my application. This is the client view using -Djavax.net.debug=ssl:handshake
详细信息:我遇到了与我的应用程序相同的问题。这是使用的客户端视图-Djavax.net.debug=ssl:handshake
myScheduler-1, WRITE: TLSv1.2 Handshake, length = 189
myScheduler-1, READ: TLSv1 Alert, length = 2
myScheduler-1, RECV TLSv1.2 ALERT: fatal, protocol_version
myScheduler-1, called closeSocket()
myScheduler-1, handling exception: javax.net.ssl.SSLException: Received fatal alert: protocol_version
My WebLogic server (10.3.6) only supports TLSv1. After upgrading the client to Java 8, it would no longer authenticate using SSL(as seen above). The projects web client is an older version of the Apache http client(4.1.3). Java 8 adds the new system property jdk.tls.client.protocols
that allows a user to set the available JSSE protocols. For details, see http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomizationAfter the Java 8 upgrade, the client would no longer accept the TLSv1 handshake that the server was responding with and would fail as shown. I added -Djdk.tls.client.protocols=TLSv1
to the client startup, restarted the client, and the problem was immediately resolved. Note that I did try the https.protocols system property per https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_httpsbut it did not work for me. I am assuming it failed because I am using Apache and, as the page notes, it pertains to
我的 WebLogic 服务器 (10.3.6) 仅支持 TLSv1。将客户端升级到 Java 8 后,它将不再使用 SSL 进行身份验证(如上所示)。项目 Web 客户端是 Apache http 客户端 (4.1.3) 的旧版本。Java 8 添加了新的系统属性jdk.tls.client.protocols
,允许用户设置可用的 JSSE 协议。详情见http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomizationJava 8升级后,客户端将不再接受TLSv1握手服务器正在响应并且会失败,如图所示。我添加-Djdk.tls.client.protocols=TLSv1
到客户端启动,重新启动客户端,问题立即解决。请注意,我确实尝试了每个 https.protocols 系统属性https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_https但它对我不起作用。我假设它失败了,因为我使用的是 Apache,并且如页面所述,它与
https connections through use of the HttpsURLConnection class or via URL.openStream() operations
通过使用 HttpsURLConnection 类或通过 URL.openStream() 操作的 https 连接
but I did not test it extensively.
但我没有对其进行广泛的测试。