Java 如何在 android 中为 HttpsUrlConnection 禁用 SSLv3?

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

How to disable SSLv3 in android for HttpsUrlConnection?

javaandroidclienthttpsurlconnectionpoodle-attack

提问by user1375399

We wrote client application in android which connects with https servers using HttpsUrlConnection apis. Due to Poodle vulnerability, we need to disable SSLv3 from the list of enabled protocols while invoking any request.

我们在 android 中编写了客户端应用程序,它使用 HttpsUrlConnection apis 与 https 服务器连接。由于 Poodle 漏洞,我们需要在调用任何请求时从启用的协议列表中禁用 SSLv3。

We followed the guidelinescaptured by oracle

我们遵循了oracle 捕获的准则

and added following line before invoking url connection

并在调用 url 连接之前添加以下行

java.lang.System.setProperty("https.protocols", "TLSv1");

This solution works fine with normal java program.
We got SSLHandShakeExceptionwhen tried to connect with a server which only works on SSLv3 protocol.

此解决方案适用于普通的 java 程序。
我们SSLHandShakeException在尝试连接仅适用于 SSLv3 协议的服务器时遇到了问题。

But concern is: same fix does not work for android. Am I missing something or should I try another approach for android? Please suggest.

但令人担忧的是:同样的修复不适用于 android。我是不是遗漏了什么,还是应该为 android 尝试另一种方法?请建议。

回答by Amit Rathore

use this code snippet, if server is SSLv3 enable then it will fail handshaking.

使用此代码片段,如果服务器启用 SSLv3,则握手失败。

        SocketFactory sf = SSLSocketFactory.getDefault();
        SSLSocket socket = (SSLSocket) sf.createSocket("host-name", 443);
        socket.setEnabledProtocols(new String[] { "TLSv1"});
        socket.startHandshake();

回答by kamal_tech_view

 SSLContext sslContext = SSLContext.getInstance("TLSv1");
                sslContext.init(null, null, null);
                SSLSocketFactory socketFactory = sslContext.getSocketFactory();
                            httpURLConnection.setSSLSocketFactory(socketFactory);

HttpsURLConnection using TSL create a security failed, the Android implementation will fall back to SSLV3 to connection.

HttpsURLConnection 使用 TSL 创建安全失败,Android 实现将回退到 SSLV3 连接。

Please refer this http://code.google.com/p/android/issues/detail?id=78431

请参阅此http://code.google.com/p/android/issues/detail?id=78431

回答by Bhavit S. Sengar

I found the solution for it by analyzing the data packets using wireshark. What I found is that while making a secure connection, android was falling back to SSLv3from TLSv1. It is a bug in android versions < 4.4 , and it can be solved by removing the SSLv3 protocol from Enabled Protocols list. I made a custom socketFactory class called NoSSLv3SocketFactory.java. Use this to make a socketfactory.

我通过使用wireshark分析数据包找到了解决方案。我发现在建立安全连接时,android 正在从TLSv1回退到SSLv3。这是 android 版本 < 4.4 中的一个错误,可以通过从 Enabled Protocols 列表中删除 SSLv3 协议来解决。我创建了一个名为 NoSSLv3SocketFactory.java 的自定义 socketFactory 类。使用它来制作一个socketfactory。

/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class NoSSLv3SocketFactory extends SSLSocketFactory{
    private final SSLSocketFactory delegate;

public NoSSLv3SocketFactory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

private Socket makeSocketSafe(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new NoSSLv3SSLSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}

private class NoSSLv3SSLSocket extends DelegateSSLSocket {

    private NoSSLv3SSLSocket(SSLSocket delegate) {
        super(delegate);

    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {

            List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
            if (enabledProtocols.size() > 1) {
                enabledProtocols.remove("SSLv3");
                System.out.println("Removed SSLv3 from enabled protocols");
            } else {
                System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
            }
            protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
        }

        super.setEnabledProtocols(protocols);
    }
}

public class DelegateSSLSocket extends SSLSocket {

    protected final SSLSocket delegate;

    DelegateSSLSocket(SSLSocket delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        return delegate.getEnabledCipherSuites();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        delegate.setEnabledCipherSuites(suites);
    }

    @Override
    public String[] getSupportedProtocols() {
        return delegate.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return delegate.getEnabledProtocols();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        delegate.setEnabledProtocols(protocols);
    }

    @Override
    public SSLSession getSession() {
        return delegate.getSession();
    }

    @Override
    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.addHandshakeCompletedListener(listener);
    }

    @Override
    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.removeHandshakeCompletedListener(listener);
    }

    @Override
    public void startHandshake() throws IOException {
        delegate.startHandshake();
    }

    @Override
    public void setUseClientMode(boolean mode) {
        delegate.setUseClientMode(mode);
    }

    @Override
    public boolean getUseClientMode() {
        return delegate.getUseClientMode();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        delegate.setNeedClientAuth(need);
    }

    @Override
    public void setWantClientAuth(boolean want) {
        delegate.setWantClientAuth(want);
    }

    @Override
    public boolean getNeedClientAuth() {
        return delegate.getNeedClientAuth();
    }

    @Override
    public boolean getWantClientAuth() {
        return delegate.getWantClientAuth();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        delegate.setEnableSessionCreation(flag);
    }

    @Override
    public boolean getEnableSessionCreation() {
        return delegate.getEnableSessionCreation();
    }

    @Override
    public void bind(SocketAddress localAddr) throws IOException {
        delegate.bind(localAddr);
    }

    @Override
    public synchronized void close() throws IOException {
        delegate.close();
    }

    @Override
    public void connect(SocketAddress remoteAddr) throws IOException {
        delegate.connect(remoteAddr);
    }

    @Override
    public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
        delegate.connect(remoteAddr, timeout);
    }

    @Override
    public SocketChannel getChannel() {
        return delegate.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return delegate.getInetAddress();
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return delegate.getInputStream();
    }

    @Override
    public boolean getKeepAlive() throws SocketException {
        return delegate.getKeepAlive();
    }

    @Override
    public InetAddress getLocalAddress() {
        return delegate.getLocalAddress();
    }

    @Override
    public int getLocalPort() {
        return delegate.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return delegate.getLocalSocketAddress();
    }

    @Override
    public boolean getOOBInline() throws SocketException {
        return delegate.getOOBInline();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        return delegate.getOutputStream();
    }

    @Override
    public int getPort() {
        return delegate.getPort();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return delegate.getReceiveBufferSize();
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return delegate.getRemoteSocketAddress();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return delegate.getReuseAddress();
    }

    @Override
    public synchronized int getSendBufferSize() throws SocketException {
        return delegate.getSendBufferSize();
    }

    @Override
    public int getSoLinger() throws SocketException {
        return delegate.getSoLinger();
    }

    @Override
    public synchronized int getSoTimeout() throws SocketException {
        return delegate.getSoTimeout();
    }

    @Override
    public boolean getTcpNoDelay() throws SocketException {
        return delegate.getTcpNoDelay();
    }

    @Override
    public int getTrafficClass() throws SocketException {
        return delegate.getTrafficClass();
    }

    @Override
    public boolean isBound() {
        return delegate.isBound();
    }

    @Override
    public boolean isClosed() {
        return delegate.isClosed();
    }

    @Override
    public boolean isConnected() {
        return delegate.isConnected();
    }

    @Override
    public boolean isInputShutdown() {
        return delegate.isInputShutdown();
    }

    @Override
    public boolean isOutputShutdown() {
        return delegate.isOutputShutdown();
    }

    @Override
    public void sendUrgentData(int value) throws IOException {
        delegate.sendUrgentData(value);
    }

    @Override
    public void setKeepAlive(boolean keepAlive) throws SocketException {
        delegate.setKeepAlive(keepAlive);
    }

    @Override
    public void setOOBInline(boolean oobinline) throws SocketException {
        delegate.setOOBInline(oobinline);
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        delegate.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean reuse) throws SocketException {
        delegate.setReuseAddress(reuse);
    }

    @Override
    public synchronized void setSendBufferSize(int size) throws SocketException {
        delegate.setSendBufferSize(size);
    }

    @Override
    public void setSoLinger(boolean on, int timeout) throws SocketException {
        delegate.setSoLinger(on, timeout);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        delegate.setSoTimeout(timeout);
    }

    @Override
    public void setTcpNoDelay(boolean on) throws SocketException {
        delegate.setTcpNoDelay(on);
    }

    @Override
    public void setTrafficClass(int value) throws SocketException {
        delegate.setTrafficClass(value);
    }

    @Override
    public void shutdownInput() throws IOException {
        delegate.shutdownInput();
    }

    @Override
    public void shutdownOutput() throws IOException {
        delegate.shutdownOutput();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object o) {
        return delegate.equals(o);
    }
}
}

Use this class like this while connecting :

在连接时像这样使用这个类:

SSLContext sslcontext = SSLContext.getInstance("TLSv1");

            sslcontext.init(null,
                    null,
                    null);
            SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

            HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
            l_connection = (HttpsURLConnection) l_url.openConnection();
            l_connection.connect();

UPDATE :

更新 :

Now, correct solution would be to install a newer security provider using Google Play Services:

现在,正确的解决方案是使用Google Play Services安装更新的安全提供程序:

    ProviderInstaller.installIfNeeded(getApplicationContext());

This effectively gives your app access to a newer version of OpenSSL and Java Security Provider, which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:

这有效地使您的应用程序可以访问更新版本的 OpenSSL 和 Java 安全提供程序,其中包括对 SSLEngine 中的 TLSv1.2 的支持。安装新提供程序后,您可以按照通常的方式创建支持 SSLv3、TLSv1、TLSv1.1 和 TLSv1.2 的 SSLEngine:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

Or you can restrict the enabled protocols using engine.setEnabledProtocols.

或者您可以使用 限制启用的协议engine.setEnabledProtocols

Don't forget to add the following dependency (latest version found here):

不要忘记添加以下依赖项(在此处找到最新版本):

compile 'com.google.android.gms:play-services-auth:11.8.0'

For more info, checkout this link.

有关更多信息,请查看此链接

回答by Diederik

Using PlayService publisher client librariesrunning on Android I experienced the same problem when running the sample.

使用在 Android 上运行的PlayService 发布者客户端库,我在运行示例时遇到了同样的问题。

Fixed it with @bhavit-s-sengar's awnser above. Had to also change AndroidPublisherHelper.newTrustedTransport()to this:

用上面的@bhavit-s-sengar's awnser 修复了它。也不得不改成AndroidPublisherHelper.newTrustedTransport()这样:

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
//  NoSSLv3SocketFactory is @bhavit-s-sengar's http://stackoverflow.com/a/29946540/8524
SSLSocketFactory noSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

NetHttpTransport.Builder netTransportBuilder = new NetHttpTransport.Builder();
netTransportBuilder.setSslSocketFactory(noSSLv3Factory);
HTTP_TRANSPORT = netTransportBuilder.build();

回答by Hans-Christoph Steiner

Inspired by Bhavit S. Sengar's answer, it bundled that technique into a dead simple method call. You can use the NetCipherlibrary to get a modern TLS config when using Android's HttpsURLConnection. NetCipher configures the HttpsURLConnectioninstance to use the best supported TLS version, removes SSLv3 support, and configures the best suite of ciphers for that TLS version. First, add it to your build.gradle:

受到 Bhavit S. Sengar 的回答的启发,它将该技术捆绑到一个非常简单的方法调用中。您可以使用NetCipher库在使用 Android 的HttpsURLConnection. NetCipher 将HttpsURLConnection实例配置为使用受支持的最佳 TLS 版本,移除 SSLv3 支持,并为该 TLS 版本配置最佳密码套件。首先,将它添加到你的build.gradle

compile 'info.guardianproject.netcipher:netcipher:1.2'

Or you can download the netcipher-1.2.jarand include it directly in your app. Then instead of calling:

或者您可以下载netcipher-1.2.jar并将其直接包含在您的应用程序中。然后而不是调用:

HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();

Call this:

调用这个:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);

回答by jonastheis

At first I tried Bhavit S. Sengar's answerand it worked for most cases. But sometimes there where issues even when SSLv3 protocol was removed from Enabled Protocols on an Android 4.4.4 device. So the NetCipherlibrary by Hans-Christoph Steiner is perfect to solve that problem as far as I could test it.

起初我尝试了 Bhavit S. Sengar 的答案,它适用于大多数情况。但有时即使从 Android 4.4.4 设备上的启用协议中删除 SSLv3 协议也会出现问题。因此,就我的测试而言,Hans-Christoph Steiner的NetCipher库非常适合解决这个问题。

We use jsoup to make a bunch of web scraping on different servers, so we cannot set HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);. I assume that's the same problem if you use OkHttp.

我们使用jsoup在不同的服务器上做了一堆网页抓取,所以我们不能设置HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);. 如果您使用 OkHttp,我认为这也是同样的问题。

The best solution we've come to is to set the info.guardianproject.netcipher.client.TlsOnlySocketFactoryfrom NetCipher as DefaultSSLSocketFactoryin a static block. So it's set for the whole runtime of our app:

我们找到的最佳解决方案是将info.guardianproject.netcipher.client.TlsOnlySocketFactoryfrom NetCipher设置为DefaultSSLSocketFactory静态块。所以它是为我们应用程序的整个运行时设置的:

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);

If you like to inspect the full details (with trustAllCertificates) you can do it here.

如果您想检查全部详细信息(使用trustAllCertificates),您可以在此处进行

回答by Mostafizur Rahman

Connects with https server we need certificate in handshaking from client side. 1 year back I solved a similar issue using self sign certificate in the following way-

与 https 服务器连接,我们需要从客户端握手的证书。1 年前,我通过以下方式使用自签名证书解决了类似的问题 -

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HttpsTrustManager implements X509TrustManager {

private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

@Override
public void checkClientTrusted(
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

}

@Override
public void checkServerTrusted(
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

}

public boolean isClientTrusted(X509Certificate[] chain) {
    return true;
}

public boolean isServerTrusted(X509Certificate[] chain) {
    return true;
}

@Override
public X509Certificate[] getAcceptedIssuers() {
    return _AcceptedIssuers;
}

public static void allowAllSSL() {
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

        @Override
        public boolean verify(String arg0, SSLSession arg1) {
            return true;
        }

    });

    SSLContext context = null;
    if (trustManagers == null) {
        trustManagers = new TrustManager[]{new HttpsTrustManager()};
    }

    try {
        context = SSLContext.getInstance("TLS");
        context.init(null, trustManagers, new SecureRandom());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }

    HttpsURLConnection.setDefaultSSLSocketFactory(context
            .getSocketFactory());
}

}

Usage in client side before HttpsUrlConnection

HttpsUrlConnection 之前在客户端的使用

HttpsTrustManager.allowAllSSL();

hopefully it will work :)

希望它会起作用:)

回答by Bajrang Hudda

Actually we don't need to disable the SSLV3 or TLSV1.0, What we just need to enable TLSV1.1 or TLSv1.2 in android < 5 devices.

实际上我们不需要禁用 SSLV3 或 TLSV1.0,我们只需要在 android < 5 设备中启用 TLSV1.1 或 TLSv1.2。

The problem is TLSv1.1 and TLSv1.2 not enabled on Android <5 by default and to connect using these latest secure protocol we must have to enable in Android <5 devices.

问题是默认情况下未在 Android <5 上启用 TLSv1.1 和 TLSv1.2,要使用这些最新的安全协议进行连接,我们必须在 Android <5 设备中启用。

This solution fixed my problem : https://stackoverflow.com/a/45853669/3448003

此解决方案解决了我的问题:https: //stackoverflow.com/a/45853669/3448003