Как переопределить список шифров, отправленный на сервер Android при использовании HttpsURLConnection?

Во время согласования TLS клиенты отправляют на сервер список поддерживаемых шифров, сервер выбирает один и начинается шифрование. Я хочу изменить этот шифр-лист, отправленный на сервер Android, когда я использую HttpsURLConnection для связи.

Я знаю, что я могу использовать setSSLSocketFactory на HttpsURLConnection объект, чтобы настроить его использовать SSLSocketFactory, Это полезно, когда я хочу изменить доверенный менеджер и т. Д., Используемые SSLSocket вернулся SSLSocketFactory,

Я знаю, что в целом этот список шифров можно редактировать, используя SSLParameters объект и передать его SSlsocket или же SSLEngine объекты с использованием методов, которые они предоставляют.

Но SSLSocketFactory похоже не выставляет такие методы!

Я не могу найти способ изменить SSLParameters из возвращенных SSLSocket объекты, созданные SSLSocketFactory Я перехожу к HttpsURLConnection,

Что делать?

Я думаю, это относится и к Java в целом, а не только к Android. Может быть, есть ОО способ сделать это (например, расширить SSLSocketFactory и предоставить это HttpsURLConnection?)

3 ответа

Этот кусок кода немного сырой. пожалуйста, используйте с осторожностью.

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

Спасибо вам.

Я объединил технику в ответе @ThinkChris 1 в простой простой вызов метода. Вы можете использовать библиотеку NetCipher, чтобы получить современную конфигурацию TLS при использовании Android 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);

Если вы хотите специально настроить этот список шифров, вы можете проверить код там. Но большинству людей не нужно думать о списке шифров, вместо этого он должен использовать общие рекомендации по умолчанию.

Этот код творил чудеса для неожиданного 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

Этот сайт работал нормально до 15.04.2016, а затем начал работать. Я считаю, что сбой вызван прекращением поддержки веб-сайта. SSLv2Hello а также SSLv3 из-за уязвимости DROWN. Смотрите это для хорошего анализа.

Доступ к веб-сайту начал работать с изменения кода с изменениями только двух констант:

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

Я надеюсь, что это помогает кому-то еще.

Другие вопросы по тегам