コードは私のGenymotion Android 4.4.4エミュレーターで動作しますが、使用しているデバイス(4.4.2)では動作しません。
私は多くの「すべての証明書を信頼する」回避策を試しましたが、役に立ちませんでした(証明書はAOKなので、とにかくこれは問題ではないと思います)。
暗号を特定したと思います(デスクトップからのWiresharkトレースを使用)。 TLS 0x00 0x1Eはややまれに見えますか?
修正方法はありますか?
これが私のコードです
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
String baseURL = "https://mysite.co.uk/api/";
HttpGetHC4 request = new HttpGetHC4(baseURL + "/authenticate?user=abcd&password=1234");
CloseableHttpResponse response = client.execute(request);
そしてエラー;
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: 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:744 0x684dfce0:0x00000000)
at com.Android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:449)
at org.Apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.Java:340)
at org.Apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.Java:281)
at org.Apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.Java:124)
at org.Apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.Java:322)
at org.Apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.Java:373)
at org.Apache.http.impl.execchain.MainClientExec.execute(MainClientExec.Java:225)
at org.Apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.Java:195)
at org.Apache.http.impl.execchain.RetryExec.execute(RetryExec.Java:86)
at org.Apache.http.impl.execchain.RedirectExec.execute(RedirectExec.Java:108)
at org.Apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.Java:178)
at org.Apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.Java:82)
at org.Apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.Java:106)
at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.Java:239)
at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.Java:189)
at Android.os.AsyncTask.finish(AsyncTask.Java:632)
at Android.os.AsyncTask.access$600(AsyncTask.Java:177)
at Android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.Java:645)
at Android.os.Handler.dispatchMessage(Handler.Java:102)
at Android.os.Looper.loop(Looper.Java:136)
at Android.app.ActivityThread.main(ActivityThread.Java:5017)
at Java.lang.reflect.Method.invokeNative(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:515)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:785)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: 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:744 0x684dfce0:0x00000000)
at com.Android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.Android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:406)
... 25 more
[〜#〜] edit [〜#〜]
別のテクニック/ライブラリを使用してみてください。
RequestQueue queue = Volley.newRequestQueue(this);
String url ="https://mysite.co.uk/api/authenticate?user=abcd&password=1234";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
Log.d("response: ", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("response: ", error.toString());
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
戻り値;
D/response:: com.Android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ad51be0: Failure in SSL library, usually a protocol error
D/response:: error:140740B5:SSL routines:SSL23_CLIENT_HELLO:no ciphers available (external/openssl/ssl/s23_clnt.c:486 0x684dfce0:0x00000000)
HttpStack stack = new HurlStack(null, new NoSSLv3SocketFactory());
RequestQueue queue = Volley.newRequestQueue(this, stack);
戻り値;
D/response:: com.Android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ae51d30: Failure in SSL library, usually a protocol error
D/response:: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
Androidは、ネットワーク操作にさまざまなプロトコルを使用します。
異なるAndroidバージョン。
OkHttpClientのソリューションを見つけました。
Protocol Supported (API Levels) Enabled by default (API Levels)
SSLv3 1–25 1–22
TLSv1 1+ 1+
TLSv1.1 16+ 20+
TLSv1.2 16+ 20+
そのため、Android VERSION> = 16&VERSION <22で接続のプロトコルを変更する必要があります.
Create Java fileTls12SocketFactory.Java
/**
* Enables TLS v1.2 when creating SSLSockets.
* <p/>
* For some reason, Android supports TLS v1.2 from API 16, but enables it by
* default only from API 20.
* @link https://developer.Android.com/reference/javax/net/ssl/SSLSocket.html
* @see SSLSocketFactory
*/
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_V12_ONLY = {"TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory base) {
this.delegate = base;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return patch(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
}
return s;
}
}
このメソッドをコードのどこかに配置します。
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2).build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
client.connectionSpecs(specs);
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
return client;
}
public OkHttpClient getNewHttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder().followRedirects(true).followSslRedirects(true)
.retryOnConnectionFailure(true).cache(null).connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
}
以下のようなOkHttpインスタンスを作成します。
private OkHttpClient getNewHttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cache(null)
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
}
次のようなクライアントオブジェクトを使用します。
OkHttpClient client = getNewHttpClient();
Request.Builder requestBuilder = new Request.Builder();
URL url = new URL("YOUR_URL_LINK");
Request request = requestBuilder.url(url).build();
Call call = client.newCall(request);
Response response = call.execute();
これを試して:
public class ClientSSLSocketFactory {
public static SSLSocketFactory getSocketFactory(Context context) {
try {
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, new SecureRandom());
SSLSocketFactory ssf = SSLCertificateSocketFactory.getDefault(10000, new SSLSessionCache(context));
return ssf;
} catch (Exception ex) {
Log.e("ssl", "Error during the getSocketFactory");
ex.printStackTrace();
return null;
}
}
}
そして、キューを作成するとき:
sRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));
それが役に立てば幸い。
問題は、SSL v3を使用しようとしていることを示唆する次のような文字列に基づくSSL v3であると考えています:SSL23_GET_SERVER_HELLO:sslv3
SSL v3は安全でないと見なされているため、ほとんどの最新のソフトウェアでは無効になっています(そうでなければ動作すると予想される場所であっても、多くの企業が単純に削除しています)。また、スタックトレースから、コードがApache httpクライアントを使用していることもわかります。org.Apache.http.impl.client
そのため、Apache HTTPクライアントがssl v3を使用するのを何らかの形で防ぐ必要があります。 Apache httpクライアントは、標準のJava SSL/TLSライブラリとは別に存在します。
回答がなかった別のstackoverflow質問があります: Apache HttpClientでSSLv3を無効にする方法
このリンクは最も有望に見えます: https://discretemkt.wordpress.com/2014/11/16/commons-httpclient-can-disable-sslv3/
そこでは、キーラインは
Protocol.registerProtocol(scheme, customHttps);
その呼び出しは、既存のSSLファクトリをバイパスできるように見えます。そのコードを最初に実行すると、Apache HTTPクライアントのバージョンに互換性があると想定して動作する場合があります。
また、最近ではTLS 1.0も安全でないと見なされていることに注意してください。 TLS 1.2および1.3および現在の標準。