【发布时间】:2014-12-25 08:01:28
【问题描述】:
由于POODLE 漏洞,我的托管在 Amazon AWS 中的服务器不再支持 SSLv3。
因此,我的 Android 应用对服务器进行的第一次 HTTPS 连接会在建立连接时导致错误。
Error reading server response: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x77d8ab68: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x7339ad74:0x00000000)
[....]
Caused by: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x77d8ab68: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x7339ad74:0x00000000)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:448)
at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
at com.android.okhttp.Connection.connect(Connection.java:107)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
错误仅发生在第一个请求中。后续请求会工作一段时间。
为了解决这个问题,我尝试从 Android 客户端接受的协议列表中删除 SSL,并确保我只使用 TLS。 为此,我设置了一个自定义 SSLSocketFactory,它将 SSL 从启用的协议和支持的密码套件列表中删除。
/**
* SSLSocketFactory that wraps one existing SSLSocketFactory and delegetes into it adding
* a new cipher suite
*/
public class TLSOnlySocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public TLSOnlySocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return getPreferredDefaultCipherSuites(this.delegate);
}
@Override
public String[] getSupportedCipherSuites() {
return getPreferredSupportedCipherSuites(this.delegate);
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
final Socket socket = this.delegate.createSocket(s, host, port, autoClose);
((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
((SSLSocket)socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));
return socket;
}
[.....]
((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
final Socket socket = this.delegate.createSocket(host, port);
((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));
return socket;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
final Socket socket = this.delegate.createSocket(address, port, localAddress, localPort);
((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));
return socket;
}
private String[] getPreferredDefaultCipherSuites(SSLSocketFactory sslSocketFactory) {
return getCipherSuites(sslSocketFactory.getDefaultCipherSuites());
}
private String[] getPreferredSupportedCipherSuites(SSLSocketFactory sslSocketFactory) {
return getCipherSuites(sslSocketFactory.getSupportedCipherSuites());
}
private String[] getCipherSuites(String[] cipherSuites) {
final ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(cipherSuites));
final Iterator<String> iterator = suitesList.iterator();
while (iterator.hasNext()) {
final String cipherSuite = iterator.next();
if (cipherSuite.contains("SSL")) {
iterator.remove();
}
}
return suitesList.toArray(new String[suitesList.size()]);
}
private String[] getEnabledProtocols(SSLSocket socket) {
final ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(socket.getSupportedProtocols()));
final Iterator<String> iterator = protocolList.iterator();
while (iterator.hasNext()) {
final String protocl = iterator.next();
if (protocl.contains("SSL")) {
iterator.remove();
}
}
return protocolList.toArray(new String[protocolList.size()]);
}
}
如您所见,我的 SSLSocketFactory 委托给另一个 SSLSocketFactory,它所做的只是从启用的协议列表中删除 SSL。
我将这家工厂建立为
final TLSOnlySocketFactory tlsOnlySocketFactory = new TLSOnlySocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(tlsOnlySocketFactory);
这并不能解决问题。有时,我仍然会在建立连接时看到错误。奇怪的是,这并不能解决问题,但它显然可以最大限度地减少问题的发生。
如何强制我的 Android 客户端中的 HttpsUrlConnection 仅使用 TLS?
谢谢。
【问题讨论】:
-
你解决了错误的问题。你不必这样做。您的问题出在其他地方。
-
如果这不是正确的方法,我应该如何确保我的 HttpsUrlConnection 仅使用 TLS?
-
嗨,我也遇到了这个问题。你找到解决这个问题的方法了吗?我也需要在全球范围内解决它,因为我使用的一些罐子也受到了影响
-
@EJP 你知道如何解决这个问题吗?如果是这样,请给我们一些提示:D
-
我没有设法修复它。我很困惑。到目前为止,我发现的解决方法是: 1. 使用 Apache Http Client 而不是 HttpURLConnection(这很糟糕)。 2.使用HttpURLConnection,当我捕捉到以SSLHandshakeException为原因的IOException时,静默重试请求(这也很糟糕)。
标签: android ssl amazon-web-services poodle-attack sslsocketfactory