问题 如何使用HttpsURLConnection覆盖Android发送到服务器的密码列表?


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

我知道我可以使用 setSSLSocketFactory 在...上 HttpsURLConnection 将对象设置为使用a SSLSocketFactory。当我想要更改使用的trustmanager等时,这很有用 SSLSocket 由...返回 SSLSocketFactory

我知道一般来说这个密码列表可以用。编辑 SSLParameters 对象并将其传递给 SSlsocket 要么 SSLEngine 对象使用它们提供的方法。

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

我找不到改变方法 SSLParameters 返回的 SSLSocket 由...创建的对象 SSLSocketFactory 我转到了 HttpsURLConnection

该怎么办?

我想这也与Java有关,不仅仅是Android。也许有一种OO方式可以做到(例如扩展 SSLSocketFactory 并提供给 HttpsURLConnection?)


7370
2018-04-30 12:06


起源



答案:


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

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();

谢谢。


13
2018-05-23 12:33



小精度:这是javax.net.ssl.SSLSocketFactory而不是apatche SSLSocketFactory。 - Sergey Vakulenko
什么是SSLPinningTrustManager?是“SSLPinningTrustManager”是用户定义的类,因为Android Studio无法解析,如果是,那么可以替换什么? - Amritesh
@Amritesh也许吧 SSLPinningTrustManager 是指 这个实现 - Roman Vottner


答案:


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

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();

谢谢。


13
2018-05-23 12:33



小精度:这是javax.net.ssl.SSLSocketFactory而不是apatche SSLSocketFactory。 - Sergey Vakulenko
什么是SSLPinningTrustManager?是“SSLPinningTrustManager”是用户定义的类,因为Android Studio无法解析,如果是,那么可以替换什么? - Amritesh
@Amritesh也许吧 SSLPinningTrustManager 是指 这个实现 - Roman Vottner


我在@ ThinkChris的答案中捆绑了这项技术1 进入一个死的简单方法调用。你可以使用 NetCipher 库使用Android时获得现代TLS配置 HttpsURLConnection。 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);

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


3
2017-11-06 15:27





这段代码出乎意料的奇迹 javax.net.ssl.SSLHandshakeException

升级到 jdk1.8.0_92 和Oracle JCE无限强度策略文件没有帮助,我试图申请具体是不成功的 SSLParameters 到了 HttpsUrlConnection

特别是,试图使用 HttpsUrlConnection 读书 https://www.adrbnymellon.com 导致以下错误:

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

这个网站在大约2016年4月15日之前工作正常,然后开始失败。我认为失败是由于网站停止支持 SSLv2Hello 和 SSLv3 由于DROWN漏洞。看到 这个 进行了很好的分析。

访问网站开始工作,修改代码只需更改2个常量:

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

我希望这有助于其他人。


-1
2018-04-22 17:57