13

在 TLS 协商期间,客户端向服务器发送支持的密码列表,服务器选择一个,然后加密开始。当我HttpsURLConnection用于通信时,我想更改这个由 Android 发送到服务器的密码列表。

我知道我可以setSSLSocketFactoryHttpsURLConnection对象上使用来将其设置为使用SSLSocketFactory. 当我想更改SSLSocketSSLSocketFactory.

我知道通常可以使用对象编辑此密码套件列表,并使用它们提供的方法SSLParameters将其传递给SSlsocket对象。SSLEngine

但是SSLSocketFactory似乎没有公开这样的方法!

我找不到方法来更改由I 传递给创建SSLParameters的返回SSLSocket对象的.SSLSocketFactoryHttpsURLConnection

该怎么办?

我想这通常也与 Java 有关,而不仅仅是 Android。也许有一种面向对象的方式来做到这一点(例如,将其扩展SSLSocketFactory并提供给HttpsURLConnection?)

4

3 回答 3

18

这段代码有点原始。请小心使用。

public class PreferredCipherSuiteSSLSocketFactory extends SSLSocketFactory {


private static final String PREFERRED_CIPHER_SUITE = "TLS_RSA_WITH_AES_128_CBC_SHA";

private final SSLSocketFactory delegate;

public PreferredCipherSuiteSSLSocketFactory(SSLSocketFactory delegate) {

    this.delegate = delegate;
}

@Override
public String[] getDefaultCipherSuites() {

    return setupPreferredDefaultCipherSuites(this.delegate);
}

@Override
public String[] getSupportedCipherSuites() {

    return setupPreferredSupportedCipherSuites(this.delegate);
}

@Override
public Socket createSocket(String arg0, int arg1) throws IOException,
        UnknownHostException {

    Socket socket = this.delegate.createSocket(arg0, arg1);
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites);

    return socket;
}

@Override
public Socket createSocket(InetAddress arg0, int arg1) throws IOException {

    Socket socket = this.delegate.createSocket(arg0, arg1);
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites);

    return socket;
}

@Override
public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3)
        throws IOException {

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites);

    return socket;
}

@Override
public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
        throws IOException, UnknownHostException {

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites);

    return socket;
}

@Override
public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2,
        int arg3) throws IOException {

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3);
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate);
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites);

    return socket;
}

private static String[] setupPreferredDefaultCipherSuites(SSLSocketFactory sslSocketFactory) {

    String[] defaultCipherSuites = sslSocketFactory.getDefaultCipherSuites();

    ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(defaultCipherSuites));
    suitesList.remove(PREFERRED_CIPHER_SUITE);
    suitesList.add(0, PREFERRED_CIPHER_SUITE);

    return suitesList.toArray(new String[suitesList.size()]);
}

private static String[] setupPreferredSupportedCipherSuites(SSLSocketFactory sslSocketFactory) {

    String[] supportedCipherSuites = sslSocketFactory.getSupportedCipherSuites();

    ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(supportedCipherSuites));
    suitesList.remove(PREFERRED_CIPHER_SUITE);
    suitesList.add(0, PREFERRED_CIPHER_SUITE);

    return suitesList.toArray(new String[suitesList.size()]);
}
}

当你想使用它时。

            HttpsURLConnection connection = (HttpsURLConnection) (new URL(url))
                .openConnection();
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManager tm[] = {new SSLPinningTrustManager()};
        context.init(null, tm, null);
        SSLSocketFactory preferredCipherSuiteSSLSocketFactory = new PreferredCipherSuiteSSLSocketFactory(context.getSocketFactory());
        connection.setSSLSocketFactory(preferredCipherSuiteSSLSocketFactory);
                    connection.connect();

谢谢。

于 2013-05-23T12:33:55.113 回答
4

我将@ThinkChris 的答案1中的技术捆绑到一个简单的方法调用中。使用AndroidHttpsURLConnection. NetCipher 将HttpsURLConnection实例配置为使用受支持的最佳 TLS 版本,删除 SSLv3 支持,并为该 TLS 版本配置最佳密码套件。首先,将其添加到您的build.gradle

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

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

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

调用这个:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);

如果您想专门定制该密码列表,您可以查看那里的代码。但是大多数人不应该考虑密码列表,而是默认使用常见的最佳实践。

于 2015-11-06T15:27:05.987 回答
-1

这段代码出人意料地创造了奇迹javax.net.ssl.SSLHandshakeException

升级到jdk1.8.0_92Oracle JCE 无限强度策略文件并没有帮助,我尝试应用特定SSLParametersHttpsUrlConnection.

特别是,尝试使用HttpsUrlConnectionto 读取https://www.adrbnymellon.com会导致以下错误:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

该网站在 2016 年 4 月 15 日之前运行良好,然后开始失败。我认为失败是由于网站停止支持SSLv2HelloDROWNSSLv3漏洞造成的。请参阅this以获得很好的分析。

通过仅更改 2 个常量来修改代码,开始访问该网站:

private static final String PREFERRED_CIPHER_SUITE = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
SSLContext context = SSLContext.getInstance("TLSv1.2");

我希望这对其他人有帮助。

于 2016-04-22T17:57:22.637 回答