私はAndroidで次のコードを実行しようとしています
URLConnection l_connection = null;
// Create connection
uzip=new UnZipData(mContext);
l_url = new URL(serverurl);
if ("https".equals(l_url.getProtocol())) {
System.out.println("<<<<<<<<<<<<< Before TLS >>>>>>>>>>>>");
sslcontext = SSLContext.getInstance("TLS");
System.out.println("<<<<<<<<<<<<< After TLS >>>>>>>>>>>>");
sslcontext.init(null,
new TrustManager[] { new CustomTrustManager()},
new Java.security.SecureRandom());
HttpsURLConnection
.setDefaultHostnameVerifier(new CustomHostnameVerifier());
HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext
.getSocketFactory());
l_connection = (HttpsURLConnection) l_url.openConnection();
((HttpsURLConnection) l_connection).setRequestMethod("POST");
} else {
l_connection = (HttpURLConnection) l_url.openConnection();
((HttpURLConnection) l_connection).setRequestMethod("POST");
}
/*System.setProperty("http.agent", "Android_Phone");*/
l_connection.setConnectTimeout(10000);
l_connection.setRequestProperty("Content-Language", "en-US");
l_connection.setUseCaches(false);
l_connection.setDoInput(true);
l_connection.setDoOutput(true);
System.out.println("<<<<<<<<<<<<< Before Connection >>>>>>>>>>>>");
l_connection.connect();
l_connection.connect()
では、このSSLhandshakeExceptionが発生しています。動作することもありますが、ほとんどの場合例外が発生します。 Android 4.0エミュレーターでのみ発生します。 Android 4.4および5.0でテストしましたが、正常に動作します。これの原因は何ですか?助けてください
スタックトレース
04-28 15:51:13.143: W/System.err(2915): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.143: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.143: W/System.err(2915): at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:460)
04-28 15:51:13.143: W/System.err(2915): at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:257)
04-28 15:51:13.143: W/System.err(2915): at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.Java:210)
04-28 15:51:13.143: W/System.err(2915): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.Java:477)
04-28 15:51:13.153: W/System.err(2915): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.Java:441)
04-28 15:51:13.153: W/System.err(2915): at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.Java:282)
04-28 15:51:13.153: W/System.err(2915): at libcore.net.http.HttpEngine.sendRequest(HttpEngine.Java:232)
04-28 15:51:13.153: W/System.err(2915): at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.Java:80)
04-28 15:51:13.153: W/System.err(2915): at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.Java:164)
04-28 15:51:13.153: W/System.err(2915): at com.ofss.fcdb.mobile.Android.rms.helpers.NetworkConnector.getConnection(NetworkConnector.Java:170)
04-28 15:51:13.153: W/System.err(2915): at com.ofss.fcdb.mobile.Android.rms.util.InitiateRMS$2.run(InitiateRMS.Java:221)
04-28 15:51:13.153: W/System.err(2915): at Java.lang.Thread.run(Thread.Java:856)
04-28 15:51:13.153: W/System.err(2915): Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.153: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.153: W/System.err(2915): at org.Apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
04-28 15:51:13.153: W/System.err(2915): at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:410)
04-28 15:51:13.153: W/System.err(2915): ... 11 more
04-28 16:42:44.139: W/ResourceType(3140): No package identifier when getting value for resource number 0x00000000
Wiresharkを使用してデータパケットを分析することで、その解決策を見つけました。私が見つけたのは、安全な接続を確立しているときに、AndroidがTLSv1からSSLv3にフォールバックしたことです。これはAndroidバージョン<4.4のバグであり、SSLv3プロトコルを[有効なプロトコル]リストから削除することで解決できます。 NoSSLv3SocketFactory.JavaというカスタムsocketFactoryクラスを作成しました。これを使用して、ソケットファクトリを作成します。
/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.Apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.OutputStream;
import Java.net.InetAddress;
import Java.net.Socket;
import Java.net.SocketAddress;
import Java.net.SocketException;
import Java.nio.channels.SocketChannel;
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory{
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
@Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
@Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
@Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
@Override
public SSLSession getSession() {
return delegate.getSession();
}
@Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
@Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
@Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
@Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
@Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
@Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
@Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
@Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
@Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
@Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
@Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
@Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
@Override
public synchronized void close() throws IOException {
delegate.close();
}
@Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
@Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
@Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
@Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
@Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
@Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
@Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
@Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
@Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
@Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
@Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
@Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
@Override
public boolean isBound() {
return delegate.isBound();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isConnected() {
return delegate.isConnected();
}
@Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
@Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
@Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
@Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
@Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
@Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
@Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
@Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
@Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
接続中にこのクラスを使用します:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();
更新:
現在、正しい解決策は、 Google Play Services を使用して新しいセキュリティプロバイダーをインストールすることです。
ProviderInstaller.installIfNeeded(getApplicationContext());
これにより、アプリケーションは、SSLEngineでのTLSv1.2のサポートを含むOpenSSLおよびJavaセキュリティプロバイダーの新しいバージョンに効果的にアクセスできます。新しいプロバイダーをインストールしたら、通常の方法でSSLv3、TLSv1、TLSv1.1およびTLSv1.2をサポートするSSLEngineを作成できます。
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
または、engine.setEnabledProtocols
を使用して、有効なプロトコルを制限できます。
次の依存関係を追加することを忘れないでください( 最新バージョンはここにあります ):
compile 'com.google.Android.gms:play-services-auth:11.8.0'
詳細については、こちらをご覧ください link 。
シナリオ
Android 5.0より前のバージョンのAndroidを実行しているデバイスでSSLHandshake例外が発生していました。ユースケースでは、クライアント証明書を信頼するTrustManagerを作成することも望んでいました。
クライアントのサポートされているプロトコルのリストからSSLv3を削除するために NoSSLv3SocketFactory および NoSSLv3Factory を実装しましたが、どちらのソリューションも機能しませんでした。
私が学んだこと:
私のために働いたもの
アプリの起動時にAndroidのセキュリティProvider
の更新を許可します。
5.0+より前のデフォルトのプロバイダーはSSLv3を無効にしません。 Google Play開発者サービスにアクセスできれば、アプリからAndroidのセキュリティプロバイダーにパッチを当てることは比較的簡単です。
private void updateAndroidSecurityProvider(Activity callingActivity) {
try {
ProviderInstaller.installIfNeeded(this);
} catch (GooglePlayServicesRepairableException e) {
// Thrown when Google Play Services is not installed, up-to-date, or enabled
// Show dialog to allow users to install, update, or otherwise enable Google Play services.
GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
} catch (GooglePlayServicesNotAvailableException e) {
Log.e("SecurityException", "Google Play Services not available.");
}
}
OkHttpClientまたはHttpURLConnectionを作成した場合、プロトコルとしてTLSv1.1およびTLSv1.2を使用でき、SSLv3を削除する必要があります。 ProviderInstaller.installIfNeeded(...)
を呼び出す前にクライアント/接続(より具体的にはSSLContext)が初期化された場合、再作成する必要があります。
次の依存関係を追加することを忘れないでください( 最新バージョンはここにあります ):
compile 'com.google.Android.gms:play-services-auth:16.0.1'
ソース:
脇
クライアントが使用する暗号アルゴリズムを明示的に設定する必要はありませんでしたが、執筆時点で最も安全と思われるものを推奨するSO投稿を見つけました: どの暗号スイートがSSLソケットを有効にするか?
また、デフォルトで有効になっていないAndroid 4.0デバイスに対してTLS v1.2を強制できることを知っておく必要があります。
このコードをアプリケーションファイルのonCreate()に挿入:
try {
ProviderInstaller.installIfNeeded(getApplicationContext());
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
sslContext.createSSLEngine();
} catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
| NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
以前、カスタムSSLFactory
実装でこの問題も解決しましたが、 OkHttp docs によると、ソリューションははるかに簡単です。
4.2以降のデバイスに必要なTLS
暗号を使用した最終的なソリューションは、次のようになります。
public UsersApi provideUsersApi() {
private ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
.supportsTlsExtensions(true)
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(spec))
.build();
return new Retrofit.Builder()
.baseUrl(USERS_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(UsersApi.class);
}
サポートされるプロトコルのセットは、サーバーで構成されていることに依存することに注意してください。
これは私のためにそれを解決しました:
SSLSocketのAndroidドキュメントには、TLS 1.1およびTLS 1.2がAndroid開始APIレベル16+(Android 4.1、Jelly bean)内でサポートされていると書かれています。ただし、デフォルトでは無効になっていますが、APIレベル20以降(Android 4.4 for watch、KitKat Watch、Android 5.0 for phone、Lollipop)で有効になっています。しかし、たとえば4.1を実行している電話でそれを有効にする方法に関するドキュメントを見つけるのは非常に困難です。 TLS 1.1および1.2を有効にするには、デフォルトのSSLSocketFactory実装へのすべての呼び出しをプロキシするカスタムSSLSocketFactoryを作成する必要があります。それに加えて、返されたSSLSocketでcreateSocketメソッドとcallsetEnabledProtocolsをすべてオーバーライドして、TLS 1.1およびTLS 1.2を有効にする必要があります。実装例については、以下のリンクをクリックしてください。
Genymotion(<4.4)でプロキシを使用する場合にのみ再現可能です。
[設定]-> [無線とネットワーク]-> [WiFi]->(長押しWiredSSID)-> [ネットワークの変更]でプロキシ設定を確認します。
[詳細オプションを表示]を選択します。プロキシ設定を[なし]に設定します。
私の答えは上記の答えに近いですが、何も変更せずに正確にクラスを書く必要があります。
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(delegate.createSocket());
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
httpsURLConnectionで使用する
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
int sdk = Android.os.Build.VERSION.SDK_INT;
if (sdk < Build.VERSION_CODES.Lollipop) {
if (url.toString().startsWith("https")) {
try {
TLSSocketFactory sc = new TLSSocketFactory();
conn.setSSLSocketFactory(sc);
} catch (Exception e) {
String sss = e.toString();
}
}
}
私はこれで問題を解決しました:NoSSLv3SocketFactory.Java
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.OutputStream;
import Java.net.InetAddress;
import Java.net.Socket;
import Java.net.SocketAddress;
import Java.net.SocketException;
import Java.nio.channels.SocketChannel;
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
@Override
public Socket createSocket(Socket s, String Host, int port,
boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost,
int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port, localHost,
localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port,
localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
@Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1
&& "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(
Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for "
+ String.valueOf(enabledProtocols));
}
protocols = enabledProtocols
.toArray(new String[enabledProtocols.size()]);
}
// super.setEnabledProtocols(protocols);
super.setEnabledProtocols(new String[]{"TLSv1.2"});
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
@Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
@Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
@Override
public SSLSession getSession() {
return delegate.getSession();
}
@Override
public void addHandshakeCompletedListener(
HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
@Override
public void removeHandshakeCompletedListener(
HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
@Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
@Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
@Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
@Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
@Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
@Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
@Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
@Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
@Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
@Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
@Override
public synchronized void close() throws IOException {
delegate.close();
}
@Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
@Override
public void connect(SocketAddress remoteAddr, int timeout)
throws IOException {
delegate.connect(remoteAddr, timeout);
}
@Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
@Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
@Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
@Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
@Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
@Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
@Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
@Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
@Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
@Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
@Override
public boolean isBound() {
return delegate.isBound();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isConnected() {
return delegate.isConnected();
}
@Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
@Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
@Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
@Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
@Override
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency,
bandwidth);
}
@Override
public synchronized void setReceiveBufferSize(int size)
throws SocketException {
delegate.setReceiveBufferSize(size);
}
@Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
@Override
public synchronized void setSendBufferSize(int size)
throws SocketException {
delegate.setSendBufferSize(size);
}
@Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
@Override
public synchronized void setSoTimeout(int timeout)
throws SocketException {
delegate.setSoTimeout(timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
@Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
メインクラス:
URL url = new URL("https://www.example.com/test.png");
URLConnection l_connection = null;
SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
このエラーが表示されたのは、サーバーでサポートされているプロトコル(TLSバージョン)や暗号スイートがデバイス上で有効になっていないためです(おそらくサポートされていません)。 API 16-19では、TLSv1.1とTLSv1.2がサポートされていますが、デフォルトでは有効になっていません。これらのバージョンでそれらを有効にした後、これらのバージョンはAWS CloudFrontのインスタンス上の暗号をサポートしていないため、エラーが発生しました。
Androidに暗号を追加することはできないため、CloudFrontバージョンをTLSv1.2_2018からTLSv1.1_2016(TLSv1.2を引き続きサポートします;必要としません)に切り替える必要がありました。以前のAndroidバージョンは、そのうち2つはまだ強力と考えられています。
その時点で、デバイスとサーバーが共有する少なくとも1つのプロトコルと少なくとも1つの暗号があったため、エラーは消え、呼び出しは(TLSv1.2で)通過しました。
このページの表 を参照して、どのプロトコルと暗号がAndroidのどのバージョンでサポートされ、有効になっているかを確認してください。
エラーメッセージの「sslv3アラートハンドシェイクエラー」の部分からわかるように、Androidは本当にSSLv3を使用しようとしていましたか?疑わしい;これはSSLライブラリの古いクモの巣で、まだきれいにされていないのではないかと疑っていますが、確実に言うことはできません。
TLSv1.2(およびTLSv1.1)を有効にするために、他で見られるもの(NoSSLv3SocketFactory
など)よりもはるかに単純なSSLSocketFactory
を使用することができました。有効なプロトコルにサポートされているすべてのプロトコルが含まれていること、および有効な暗号にサポートされているすべての暗号が含まれていることを確認するだけです(後者は私には必要ありませんが、他の暗号には必要な場合があります)-下部のconfigure()
を参照してください。最新のプロトコルのみを有効にする場合は、socket.supportedProtocols
をarrayOf("TLSv1.1", "TLSv1.2")
のようなものに置き換えることができます(暗号の場合も同様)。
class TLSSocketFactory : SSLSocketFactory() {
private val socketFactory: SSLSocketFactory
init {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, null, null)
socketFactory = sslContext.socketFactory
}
override fun getDefaultCipherSuites(): Array<String> {
return socketFactory.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return socketFactory.supportedCipherSuites
}
override fun createSocket(s: Socket, Host: String, port: Int, autoClose: Boolean): Socket {
return configure(socketFactory.createSocket(s, Host, port, autoClose) as SSLSocket)
}
override fun createSocket(Host: String, port: Int): Socket {
return configure(socketFactory.createSocket(Host, port) as SSLSocket)
}
override fun createSocket(Host: InetAddress, port: Int): Socket {
return configure(socketFactory.createSocket(Host, port) as SSLSocket)
}
override fun createSocket(Host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
return configure(socketFactory.createSocket(Host, port, localHost, localPort) as SSLSocket)
}
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
return configure(socketFactory.createSocket(address, port, localAddress, localPort) as SSLSocket)
}
private fun configure(socket: SSLSocket): SSLSocket {
socket.enabledProtocols = socket.supportedProtocols
socket.enabledCipherSuites = socket.supportedCipherSuites
return socket
}
}
私もこのエラー報告の問題を抱えています。私のコードは以下にあります。
public static void getShop() throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://10.0.2.2:8010/getShopInfo/aaa")
.build();
Response response = client.newCall(request).execute();
Log.d("response", response.body().string());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
Springbootをバックエンドとして使用し、Android OKHttpを使用して情報を取得します。私が犯した重大な間違いは、。url("https://10.0.2.2:8010/getShopInfo/aaa")Androidのコード。しかし、私のバックエンドはhttpsリクエストを許可されていません。 。url( "http://10.0.2.2:8010/getShopInfo/aaa")を使用した後、myコードはうまくいきました。だから、私の間違いはエミュレータのバージョンではなく、リクエストプロトコルに関するものです。私が言ったことをした後、別の問題に出会いましたが、それは別の問題であり、新しい問題の解決方法を添付します。
幸運を祈ります!