这是我的第一篇文章,我会尽力做到尽可能清楚(对不起我的英语)。
这是我的麻烦,我正在使用 retrofit:1.9.0 和 okhttp:2.7.5 来执行 API 调用。一切都很好,直到我的服务器提供商禁用了 SLLv2 和 SSLv3 导致安全问题的原因(在三月的第一天发现了 Drown 失败)。
现在我检查有关我的提供商的信息,他只允许来自 https://www.ssllabs.com/ 的 TLSv1 with cypher (TLS 1.0 TLS_RSA_WITH_3DES_EDE_CBC_SHA No FS)。
好的,这是我完成的所有测试和结果:
[更新问题已解决]
在我的第二个答案中找到解决这个问题的方法。
更新 看来问题来自谷歌API版本。当我在 API 18 上测试时,一切正常。当它在 Android 大于或等于 5.0.0 时失败。
第一次测试
会议。回顾:
- 编译SDK版本23
- buildToolsVersion ‘23.0.2’
- minSdkVersion 18
- targetSdk版本 21
- 改造:1.9.0
- 好的http:2.7.5
- Android 版本 > 5.0.0(但在每台设备上都一样……)
休息客户端(LoginRestClient):
public class LoginRestClient
{
private static final String BASE_URL = "";
private LoginApiService apiService;
public LoginRestClient()
{
Gson gson = new GsonBuilder()
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(ApiIntentService.getHostAddress())
.setConverter(new GsonConverter(gson))
.setClient(new OkClient(ApiIntentService.getConnectionHttpClient()))
.build();
apiService = restAdapter.create(LoginApiService.class);
}
public LoginApiService getApiService() {
return apiService;
}
}
创建客户端的函数 OkHttpClient getConnectionHttpClient()
public static OkHttpClient getConnectionHttpClient()
{
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(60, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(60, TimeUnit.SECONDS);
ConnectionSpec specs = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.cipherSuites(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
.build();
okHttpClient.setConnectionSpecs(Collections.singletonList(specs));
return okHttpClient;
}
从自定义 CallBack 导致 public void failure(RetrofitError error)
java.net.UnknownServiceException:无法找到可接受的协议。 isFallback=false,模式=[ConnectionSpec(cipherSuites=[TLS_RSA_WITH_3DES_EDE_CBC_SHA],tlsVersions=[TLS_1_0],supportsTlsExtensions=true)],支持的协议=[SSLv3,TLSv1,TLSv1.1,TLSv1.2]
第二次测试
我制作了一个自定义 SSLSocketFactory 来禁用 SSLv3 并强制使用 TLS:
/**
* @author fkrauthan
*/
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1"});
}
return socket;
}
}
我这样使用它:
public static OkHttpClient getConnectionHttpClient()
{
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(60, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(60, TimeUnit.SECONDS);
try {
TLSSocketFactory tlsSocketFactory = new TLSSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(tlsSocketFactory);
okHttpClient.setSslSocketFactory(tlsSocketFactory);
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return okHttpClient;
}
从自定义 CallBack 导致 public void failure(RetrofitError error)
javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7f87885280: Failure in SSL library, usually a protocol error error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert 握手失败(external/openssl/ssl/s23_clnt.c:770 0x7f87c2fdf0:0x00000000)
如果有人可以帮助我,那将非常酷。我所有的应用程序都关闭了,从昨天早上开始我就在与这个问题作斗争以恢复我的服务。我正在一根一根地脱头发…
提前致谢。
原文由 kevzzz 发布,翻译遵循 CC BY-SA 4.0 许可协议
问题解决了:
大家好,经过3天3夜的战斗,这里是最终的解决方案。所以感谢这里的解决方案:
如何在 android 中为 HttpsUrlConnection 禁用 SSLv3?
和这个图书馆: https ://guardianproject.info/code/netcipher
它允许在禁用 SSLv2 和 SSlv3 的情况下为 Android 提供更好的方式来使用密码和 TLS。
首先创建此类 NoSSLv3SocketFactory.java 并通过创建这样的构造函数将其与 CypherUrl 连接耦合
NoSSLv3SocketFactory.java(完整代码)
现在(在我的改造案例中)就像这样使用它:
因此,首先, 添加一个静态方法(或根据需要在使用时创建它)以使用我们之前创建的 NoSSLv3Factory.java 类创建一个 okHttpClient。
然后,在我的例子中,当你创建你的 RestAdapter 时,就像这样,并且不要忘记设置你的客户端。
有了这个它应该工作。我希望它对其他人有用。