java 使用HttpsURLConnection时如何覆盖Android发送到服务器的密码列表?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16299531/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to override the cipherlist sent to the server by Android when using HttpsURLConnection?
提问by AndroidSec
During TLS negotiation, clients send a list of supported ciphers to the server, the server picks one, and encryption starts. I want to change this cipherlist sent to the server by Android, when I'm using HttpsURLConnection
for communication.
在 TLS 协商期间,客户端将支持的密码列表发送到服务器,服务器选择一个,然后开始加密。当我HttpsURLConnection
用于通信时,我想更改由 Android 发送到服务器的这个密码列表。
I know that I can use setSSLSocketFactory
on the HttpsURLConnection
object to set it up to use a SSLSocketFactory
. This is useful when I want to change the trustmanager etc used by the SSLSocket
returned by the SSLSocketFactory
.
我知道我可以setSSLSocketFactory
在HttpsURLConnection
对象上使用以将其设置为使用SSLSocketFactory
. 当我想改变由使用的TrustManager等,这是有用SSLSocket
的返回SSLSocketFactory
。
I know that in general this ciphersuite list can be edited using an SSLParameters
object and passing it to SSlsocket
or SSLEngine
objects using the methods they provide.
我知道通常可以使用SSLParameters
对象编辑此密码套件列表,并使用它们提供的方法将其传递给SSlsocket
或SSLEngine
对象。
BUT the SSLSocketFactory
does not seem to expose such methods!
但是SSLSocketFactory
似乎没有公开这样的方法!
I cannot find a way to change the SSLParameters
of the returned SSLSocket
objects created by the SSLSocketFactory
I pass to HttpsURLConnection
.
我找不到一种方法来更改由I 传递给SSLParameters
的返回SSLSocket
对象的。SSLSocketFactory
HttpsURLConnection
What to do?
该怎么办?
I guess this is also relevant to Java in general, not only Android. Maybe there's an OO way to do it (e.g. extend SSLSocketFactory
and provide that to HttpsURLConnection
?)
我想这通常也与 Java 相关,而不仅仅是 Android。也许有一种面向对象的方式来做到这一点(例如扩展SSLSocketFactory
并提供给HttpsURLConnection
?)
回答by ThinkChris
This piece of code is a bit raw. please use with care.
这段代码有点原始。请小心使用。
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()]);
}
}
When you want to use it.
当你想使用它时。
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();
Thanks you.
谢谢。
回答by Hans-Christoph Steiner
I bundled the technique in @ThinkChris's answer1into a dead simple method call. You can use the NetCipherlibrary to get a modern TLS config when using Android's HttpsURLConnection
. NetCipher configures the HttpsURLConnection
instance to use the best supported TLS version, removes SSLv3 support, and configures the best suite of ciphers for that TLS version. First, add it to your build.gradle:
我将@ThinkChris 的回答1 中的技术捆绑到一个简单的方法调用中。您可以使用NetCipher库获得现代TLS配置使用Android的时候HttpsURLConnection
。NetCipher 将HttpsURLConnection
实例配置为使用受支持的最佳 TLS 版本,移除 SSLv3 支持,并为该 TLS 版本配置最佳密码套件。首先,将它添加到你的build.gradle:
compile 'info.guardianproject.netcipher:netcipher:1.2'
Or you can download the netcipher-1.2.jarand include it directly in your app. Then instead of calling:
或者您可以下载netcipher-1.2.jar并将其直接包含在您的应用程序中。然后而不是调用:
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
Call this:
调用这个:
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
If you want to specifically customize that cipher list, you can check the code there. But most people should not have to think about the cipher list, instead it should use the common best practices by default.
如果你想专门定制那个密码列表,你可以在那里检查代码。但是大多数人不应该考虑密码列表,而是应该默认使用常见的最佳实践。
回答by Peter Costello
This code worked wonders for an unexpected javax.net.ssl.SSLHandshakeException
.
此代码为意外的javax.net.ssl.SSLHandshakeException
.
Upgrading to jdk1.8.0_92
and Oracle JCE unlimited strength policy files did not help, and I was unsuccessful trying to apply specific SSLParameters
to the HttpsUrlConnection
.
升级到jdk1.8.0_92
Oracle JCE 无限强度策略文件并没有帮助,我尝试将特定SSLParameters
于HttpsUrlConnection
.
In particular, attempting to use HttpsUrlConnection
to read https://www.adrbnymellon.com
results in the following error:
特别是,尝试使用HttpsUrlConnection
读取会https://www.adrbnymellon.com
导致以下错误:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
This website worked OK prior to about 4/15/2016, and then started failing. I believe the failure is caused by the website discontinuing support for SSLv2Hello
and SSLv3
due to the DROWN vulnerability. See thisfor a great analysis.
该网站在 2016 年 4 月 15 日之前运行正常,然后开始出现故障。我相信失败是由于该网站停止支持造成的SSLv2Hello
,并SSLv3
由于淹没漏洞。请参阅this以获得很好的分析。
Access to the website started working by modifying the code with changes to just 2 constants:
通过修改代码,仅更改 2 个常量,即可开始访问网站:
private static final String PREFERRED_CIPHER_SUITE = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
SSLContext context = SSLContext.getInstance("TLSv1.2");
I hope this helps someone else.
我希望这对其他人有帮助。