我们最近将一个项目从Java 6更新为Java 8,现在我们在SSL握手方面遇到了麻烦。
服务层使用客户端来请求和接收来自第三方应用程序的呼叫。在服务层中,密钥库用初始化
System.setProperty("javax.net.ssl.trustStore", keyStoreFile); System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
并通过applicationContext.xml注入:
<property name="keyStoreFile" value="/keystore/keystore.keystore" /> <property name="keyStorePassword" value="password" />
如果发生错误,客户端应该信任所有证书:
private void trustHttpsCertificates() throws Exception { try { Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkServerTrusted(X509Certificate[] certs, String authType) { } public void checkClientTrusted(X509Certificate[] certs, String authType) { } } }; // Ignore differences between given hostname and certificate hostname HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(hv); } catch (KeyManagementException e) { String errorMsg = "client initialization error: " + e.getMessage(); log.error(errorMsg); throw new Exception(errorMsg, e); } }
我在升级过程中将上面的“ SSL”更改为“ TLSv1”,因为Java 8不支持SSL。这可能是个问题吗?
调试日志
Allow unsafe renegotiation: false Allow legacy hello messages: true Is initial handshake: true Is secure renegotiation: false http-nio-9080-exec-6, setSoTimeout(0) called Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1 Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1 Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1 Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1 Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1.1 Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1.1 Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1 Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1.1 Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1.1 Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1 Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1.1 %% No cached client session *** ClientHello, TLSv1.2 RandomCookie: GMT: 1423711122 bytes = { 237, 188, 53, 112, 79, 112, 248, 92, 164, 127, 178, 34, 205, 40, 245, 25, 77, 143, 116, 126, 203, 96, 61, 181, 114, 148, 66, 227 } Session ID: {} Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 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_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 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_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 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 *** [write] MD5 and SHA1 hashes: len = 237 // ... http-nio-9080-exec-6, READ: TLSv1 Alert, length = 2 http-nio-9080-exec-6, RECV TLSv1.2 ALERT: fatal, handshake_failure http-nio-9080-exec-6, called closeSocket() http-nio-9080-exec-6, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure // ... at java.lang.Thread.run(Thread.java:745) Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) 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 sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1512) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
思想
与Java 6的旧版本相比,主要有两点突出:
在旧版本的日志中,如果尝试成功,它会悄悄地提供正确的证书:
http-9080-1, READ: TLSv1 Handshake, length = 1375 *** Certificate chain chain [0] = [ [ Version: V3 Subject: xxx Signature Algorithm: MD5withRSA Key: Sun RSA public key, 1024 bits Validity: [From: Wed May 26 14:31:31 CEST 1999, To: Thu May 25 14:31:31 CEST 2000] Issuer: xxx SerialNumber: [ 01] ] Algorithm: [MD5withRSA]
在新的Java 8版本中不会发生这种情况。
另外,TLSv1与TLSv1.2?
http-nio-9080-exec-6, READ: TLSv1 Alert, length = 2 http-nio-9080-exec-6, RECV TLSv1.2 ALERT: fatal, handshake_failure
是否表示我正在尝试连接TLSv1.2?还是TLSv1?而且不接受吗?我不太了解 有没有办法找出服务器接受哪些TLS版本?
我尝试将标志添加到启动:
-Dhttps.protocols=TLSv1 -Ddeployment.security.TLSv1=true -Djavax.net.ssl.keyStore=C:\keystore\keystore.keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=C:\keystore\keystore.keystore -Djavax.net.ssl.trustStorePassword=password
还以编程方式添加密钥库管理器:
KeyStore ks = KeyStore.getInstance("JKS"); InputStream ksIs = new FileInputStream("/keystore/keystore.keystore"); try { ks.load(ksIs, password.toCharArray()); } catch (Exception e) { e.printStackTrace(); } finally { if (ksIs != null) { try { ksIs.close(); } catch (IOException e) { e.printStackTrace(); } } } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, password.toCharArray());
然后初始化SSLContext:
sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom());
但问题仍然存在。帮忙,有人吗?
好了,所以我们现在开始工作了。如果有人某天可能需要答案,我将在此处发布答案。
我们已经尝试了很多事情,所以我不能完全确定要使其正常运行需要实际做些什么,但是这是我们在此过程中进行了一些更改。
-Dhttps.protocols=SSLv3,TLSv1,SSLv2Hello ``` 添加此标志会导致证书出现在javax.net.debug日志中,但我们仍在获取SSLHandshakeException。似乎服务器接受的唯一密码是SSL_RSA_WITH_RC4_128_MD5。这不是我们的客户自动选择的。
-Dhttps.cipherSuites=SSL_RSA_WITH_RC4_128_MD5
我们添加了此标志以限制客户端的密码适合。通过编程设置相同的限制(不确定是否同时需要):
socket.setEnabledCipherSuites(new String[] {“SSL_RSA_WITH_RC4_128_MD5”}); ```
将可用的密码套件限制为客户端可以使用的唯一密码套件,使客户端选择该密码套件。
我们还jre/lib/security/java.security对启用文件SSLv3和SSL_RSA_WITH_RC4_128_MD5密码进行了以下更改:
jre/lib/security/java.security
SSLv3和SSL_RSA_WITH_RC4_128_MD5
jdk.tls.disabledAlgorithms
SSL_RSA_WITH_RC4_128_MD5
jdk.tls.legacyAlgorithms