Java 不支持的记录版本 SSLv2Hello 使用 CloseableHttpClient

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26166121/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:56:28  来源:igfitidea点击:

Unsupported record version SSLv2Hello using CloseableHttpClient

javasslapache-httpcomponents

提问by Mark Jones

I am trying to make an https call and getting the following error: Unsupported record version SSLv2Hello

我正在尝试进行 https 调用并收到以下错误:不支持的记录版本 SSLv2Hello

Can anyone please shed some light on what I'm doing wrong? Thanks for your help.

任何人都可以请说明我做错了什么吗?谢谢你的帮助。

Here is the StackTrace:

这是堆栈跟踪:

    debug:
    Unsupported record version SSLv2Hello
    javax.net.ssl.SSLException: Unsupported record version SSLv2Hello
    at sun.security.ssl.InputRecord.readV3Record(Unknown Source)
    at sun.security.ssl.InputRecord.read(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:275)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:254)
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:123)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:318)
    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 httpcomponents.httpsTest.main(httpsTest.java:135)
    

Here is working example:

这是工作示例:

    import com.sun.net.ssl.internal.ssl.Provider;
    import java.io.IOException;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.Security;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.text.MessageFormat;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.TimeUnit;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLException;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import org.apache.http.Header;
    import org.apache.http.HttpHeaders;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpHead;
    import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLContexts;
    import org.apache.http.conn.ssl.X509HostnameVerifier;
    import org.apache.http.entity.ContentType;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.protocol.BasicHttpContext;
    import org.apache.http.protocol.HttpContext;
String audioURL = "https://mydata.com/webreports/audio.jsp?callID=338786512&authentication=98695279578B04166629C0"; RequestConfig requestConfig = null; requestConfig = RequestConfig .custom() .setConnectTimeout(5000) .setConnectionRequestTimeout(5000) .setSocketTimeout(5000) .build(); PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = null; poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); poolingHttpClientConnectionManager.setMaxTotal(5); CloseableHttpClient closeableHttpClient = null; HttpHead httpHead = new HttpHead(audioURL); try { Provider sslProvider = new Provider(); if (Security.getProvider(sslProvider.getName()) == null) { Security.addProvider(sslProvider); } TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { } } }; SSLContext sslContext = SSLContexts .custom() .useSSL() .build(); sslContext.init(null, trustAllCerts, new SecureRandom()); LayeredConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext); X509HostnameVerifier x509HostnameVerifier = new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException { //do nothing } @Override public void verify(String host, X509Certificate cert) throws SSLException { //do nothing //do nothing } @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { //do nothing } @Override public boolean verify(String string, SSLSession ssls) { return true; } }; closeableHttpClient = HttpClientBuilder .create() .setDefaultRequestConfig(requestConfig) .setConnectionManager(poolingHttpClientConnectionManager) .setSslcontext(sslContext) .setHostnameVerifier(x509HostnameVerifier) .setSSLSocketFactory(sslConnectionSocketFactory) .build(); } catch (NoSuchAlgorithmException noSuchAlgorithmException) { System.out.println(noSuchAlgorithmException.getMessage()); } catch (KeyManagementException keyManagementException) { System.out.println(keyManagementException.getMessage()); } HttpContext httpContext = new BasicHttpContext(); CloseableHttpResponse closeableHttpResponse = null; try { if (closeableHttpClient != null) { closeableHttpResponse = closeableHttpClient.execute(httpHead, httpContext); if (closeableHttpResponse != null) { int statusCode = closeableHttpResponse.getStatusLine().getStatusCode(); System.out.println(String.valueOf(statusCode)); } } } catch (IOException iOException) { System.out.println(iOException.getMessage()); } finally { if (closeableHttpResponse != null) { try { closeableHttpResponse.close(); } catch (IOException iOException) { System.out.println(iOException.getMessage()); } } if (closeableHttpClient != null) { try { closeableHttpClient.close(); } catch (IOException iOException) { System.out.println(iOException.getMessage()); } } }

采纳答案by Mark Jones

Figured it out. The two places I tripped over my shoelaces were:

弄清楚了。我被鞋带绊倒的两个地方是:

    Needed to register https with the PoolingHttpClientConnectionManager
    需要向 PoolingHttpClientConnectionManager 注册 https
    Set TLSv1 as the only supported protocol in the SSLConnectionSocketFactory
    将 TLSv1 设置为 SSLConnectionSocketFactory 中唯一支持的协议
package httpcomponents;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

/**
 *
 * @author mark.jones
 */
public class HttpTest3 {

    public static void main(String[] args) {
        CloseableHttpClient closeableHttpClient = null;
        CloseableHttpResponse closeableHttpResponse = null;
        try {
            HttpHead httpHead = null;
            TrustStrategy trustStrategy = null;
            SSLContext sslContext = null;
            X509HostnameVerifier x509HostnameVerifier = null;
            LayeredConnectionSocketFactory sslConnectionSocketFactory = null;
            Registry<ConnectionSocketFactory> registry = null;
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = null;
            RequestConfig requestConfig = null;
            String audioURL = "https://ancientserver.com/webreports/audio.jsp?callID=338786512&authentication=98695279578B04166629C0AC0ABB49F0";

            httpHead = new HttpHead(audioURL);

            trustStrategy = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] xcs, String authType) throws CertificateException {
                    return true;
                }
            };

            sslContext = SSLContexts
                    .custom()
                    .useSSL()
                    .loadTrustMaterial(null, trustStrategy)
                    .setSecureRandom(new SecureRandom())
                    .build();

            x509HostnameVerifier = new X509HostnameVerifier() {
                @Override
                public void verify(String host, SSLSocket ssl) throws IOException {
                    //do nothing
                }

                @Override
                public void verify(String host, X509Certificate cert) throws SSLException {
                    //do nothing                                                            //do nothing
                }

                @Override
                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                    //do nothing
                }

                @Override
                public boolean verify(String string, SSLSession ssls) {
                    return true;
                }
            };

            //either one works
            //LayeredConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, x509HostnameVerifier);

            registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", sslConnectionSocketFactory)
                    .build();

            poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);

            requestConfig = RequestConfig
                    .custom()
                    .setConnectTimeout(5000)            //5 seconds
                    .setConnectionRequestTimeout(5000)
                    .setSocketTimeout(5000)
                    .build();

            closeableHttpClient = HttpClientBuilder
                    .create()
                    .setDefaultRequestConfig(requestConfig)
                    .setSslcontext(sslContext)
                    .setHostnameVerifier(x509HostnameVerifier)
                    .setSSLSocketFactory(sslConnectionSocketFactory)
                    .setConnectionManager(poolingHttpClientConnectionManager)
                    .build();

            if (closeableHttpClient != null) {
                closeableHttpResponse = closeableHttpClient.execute(httpHead);
                if (closeableHttpResponse != null) {
                    int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
                    System.out.println(String.valueOf(statusCode));
                    System.out.println(closeableHttpResponse.getFirstHeader("Content-Type"));
                }
            }
        } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
            System.out.println(noSuchAlgorithmException.getMessage());
        } catch (KeyStoreException keyStoreException) {
            System.out.println(keyStoreException.getMessage());
        } catch (KeyManagementException keyManagementException) {
            System.out.println(keyManagementException.getMessage());
        } catch (IOException iOException) {
            System.out.println(iOException.getMessage());
        } finally {
            if (closeableHttpResponse != null) {
                try {
                    closeableHttpResponse.close();
                } catch (IOException iOException) {
                    System.out.println(iOException.getMessage());
                }
            }
            if (closeableHttpClient != null) {
                try {
                    closeableHttpClient.close();
                } catch (IOException iOException) {
                    System.out.println(iOException.getMessage());
                }
            }
        }
    }
}

回答by user207421

It looks like you're connecting to a server that incredibly enough in this day and age still supports SSLv2.

看起来您正在连接到一个在当今时代仍然支持 SSLv2 的服务器。

Try removing SSlv2ClientHellofrom the list of enabled protocols, however you do that with Apache HTTPClient.

尝试SSlv2ClientHello从启用的协议列表中删除,但是您可以使用 Apache HTTPClient 执行此操作。

NB You haven't needed that stuff with the com.sun.net.ssl.internal.ssl.Providersince 2004, and you need to be aware that your TrustManagerand HostnameVerifierare both radically insecure. Also TrustManager.getAcceptedIssuers()is not permitted to return null: see the Javadoc.

注意:com.sun.net.ssl.internal.ssl.Provider自 2004 年以来,您就不再需要那些东西了,您需要意识到您的TrustManagerHostnameVerifier都非常不安全。也TrustManager.getAcceptedIssuers()不允许返回 null:请参阅 Javadoc。

回答by dmatej

It seems the problem is that JDK8 clients cannot connect to SSL2 server with the default Provider (SSLv2 is deprecated for a long time). You have to force the server to use SSL3 (or another safer protocol) or you have to use another provider (BouncyCastle?). Currently we have a similar problem with SOAP webservice JDK8 client and JDK6 server.

问题似乎是 JDK8 客户端无法使用默认提供程序连接到 SSL2 服务器(SSLv2 已被弃用很长时间)。您必须强制服务器使用 SSL3(或其他更安全的协议),或者您必须使用其他提供商(BouncyCastle?)。目前我们在 SOAP webservice JDK8 客户端和 JDK6 服务器上也有类似的问题。

http://en.wikipedia.org/wiki/Transport_Layer_Security#SSL_2.0

http://en.wikipedia.org/wiki/Transport_Layer_Security#SSL_2.0

http://tools.ietf.org/html/rfc6176

http://tools.ietf.org/html/rfc6176