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

Java 如何在android中为HttpsUrlConnection禁用SSLv3?,java,android,client,httpsurlconnection,poodle-attack,Java,Android,Client,Httpsurlconnection,Poodle Attack,我们在android中编写了客户端应用程序,它使用HttpSurlConnectionAPI与https服务器连接。由于Poodle漏洞,我们需要在调用任何请求时从启用的协议列表中禁用SSLv3 我们跟踪了被甲骨文捕获的目标 并在调用url连接之前添加了以下行 java.lang.System.setProperty("https.protocols", "TLSv1"); 此解决方案与普通java程序配合使用效果良好 当我们尝试连接一个只在SSLv3协议上工作的服务器时,我们得到了SSLHa

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

我们跟踪了被甲骨文捕获的目标

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

java.lang.System.setProperty("https.protocols", "TLSv1");
此解决方案与普通java程序配合使用效果良好
当我们尝试连接一个只在SSLv3协议上工作的服务器时,我们得到了
SSLHandShakeException


但令人担忧的是:相同的修复程序不适用于android。我是否遗漏了一些东西,还是应该尝试另一种android方法?请建议。

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

        SocketFactory sf = SSLSocketFactory.getDefault();
        SSLSocket socket = (SSLSocket) sf.createSocket("host-name", 443);
        socket.setEnabledProtocols(new String[] { "TLSv1"});
        socket.startHandshake();
使用TSL创建安全性的HttpsURLConnection失败,Android实现将退回到SSLV3进行连接。


请参阅此

我通过使用wireshark分析数据包找到了解决方案。我发现,在建立安全连接时,android从TLSv1退回到SSLv3。这是android版本<4.4中的一个bug,可以通过从启用的协议列表中删除SSLv3协议来解决。我创建了一个名为NoSSLv3SocketFactory.java的定制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);
    }
}
}
更新:

现在,正确的解决方案是使用以下方法安装较新的安全提供程序:

这有效地使您的应用程序能够访问更新版本的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();
或者,您可以使用
引擎.setEnabledProtocols
限制启用的协议

不要忘记添加以下依赖项():

有关更多信息,请查看此项。

在Android上运行时,我在运行时遇到了相同的问题

用上面@bhavit-s-sengar的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();

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

或者,您可以下载netcipher-1.2.jar并直接将其包含在应用程序中。然后,不要打电话:

HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
称之为:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);

起初我试过Bhavit S.Sengar'S,它在大多数情况下都有效。但有时甚至在Android 4.4.4设备上从启用的协议中删除SSLv3协议时也会出现问题。因此,汉斯·克里斯托夫·施泰纳(Hans Christoph Steiner)的图书馆就我所能测试的而言,是解决这个问题的完美之选

我们使用jsoup在不同的服务器上进行大量的web抓取,因此无法设置
HttpsURLConnection connection=NetCipher.getHttpsURLConnection(sourceUrl)。如果使用OkHttp,我假设这是相同的问题

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


如果您想查看完整的详细信息(使用
trustAllCertificates
),您可以这样做。

连接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());
}

}
HttpsUrlConnection之前在客户端的使用

HttpsTrustManager.allowAllSSL();

希望它能工作:)

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



问题是Android上未启用TLSv1.1和TLSv1.2。您是否尝试创建自己的SSLContext并将其SocketFactory传递给HttpsUrlConnection?您是否可以共享仅支持SSLv3连接的服务url?Hi-Selvin,未尝试使用SSLContext。我会尝试和更新你。任何快速的指针/代码片段都会很棒。谢谢!您的解决方案非常完美,可以在低于版本20的手机上使用okhttp 2.3.0启用TLS v1.2。您确实救了我一天!我面对这个问题,尝试了一些完全不同的解决方案,但没有一个有效(比如指定TLSv1.1等)。您的解决方案,从支持的协议中删除SSLv3,运行良好,在任何其他设备上都没有中断。有趣的是,它以前在所有设备上都能工作,但三星的Galaxy S3 Mini却不行……作为参考,问题的bug是:我真希望几天前就能找到你的答案!谢谢我刚刚使用了ProviderInstaller.installIfNeeded(getApplicationContext());它解决了这个问题。谢谢。你为什么要用所有空值初始化sslContext?我仍然得到一个
javax.net.ssl.SSLProtocolException:ssl握手失败
错误。应该是TLSv1.2如果我想使用OKHttp怎么办?我尝试了这个,我得到了网络密码sslsocketfactory,但是,在调试器中检查SSLv3协议时仍然显示出来。这是安卓5.1上的。我尝试过的一切,包括我自己的自定义sslsocketfactory类,在该类中我禁用SSLv3或仅启用TLS。。。是什么导致了这一点;SSLv3协议仍然显示。该库与所选答案中提到的使用NoSSLv3套接字相同,还是有任何不同?是的,它使用了与
NoSSLv3SocketFactory
非常相似的方法,我们正在努力添加对OkHTTP、Volley、,和ApacheHttpClient for Android连接到同一个NetCipher库。这不应该是
TLSv1.2
?非常感谢兄弟!你让我开心!仍然是SslhandshakeEx
compile 'info.guardianproject.netcipher:netcipher:1.2'
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);
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());
}

}
HttpsTrustManager.allowAllSSL();