Как переопределить список шифров, отправленный на сервер 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");
Я надеюсь, что это помогает кому-то еще.