Javax.net.ssl.SSLHandshakeException:javax.net.ssl.SSLProtocolException:SSL握手中止:SSL库失败,通常是协议错误


102

我正在尝试在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

您是否在真实设备上检查过您的应用?
Paresh Mayani 2015年

它给什么例外?堆栈跟踪?
罗恩侯爵,2015年

@PareshMayani是的,即使在具有Android 4.0的真实设备上,它也显示异常。
Bhavit S. Sengar

@EJP我已经更新了问题,添加了stacktrace。
Bhavit S. Sengar

这是标记豆形软糖的唯一问题。您是说android-4.2-jelly-bean吗?
丹尼尔·达拉纳斯

Answers:


120

通过使用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服务安装更新的安全提供程序:

    ProviderInstaller.installIfNeeded(getApplicationContext());

这有效地使您的应用程序可以访问较新版本的OpenSSL和Java安全提供程序,其中包括对SSLEngine中的TLSv1.2的支持。一旦安装了新的提供程序,就可以创建SSLEngine,该SSLEngine以通常的方式支持SSLv3,TLSv1,TLSv1.1和TLSv1.2:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

或者,您可以使用来限制已启用的协议engine.setEnabledProtocols

不要忘记添加以下依赖项(在此处检查最新版本):

implementation 'com.google.android.gms:play-services-auth:17.0.0'

有关更多信息,请查看此链接


25
如果我在Lollipop设备上遇到此错误怎么办?
IgorGanapolsky

2
我在5.0.1上获得此信息
天网

连接到虚拟主机时,此方法似乎失败了,因为它通过不提供SSLCertificateSocketFactory.setHostname(Socket,String)方法而有效地禁用了SNI。我一直在403s那里openssl.exe和浏览器连接没有麻烦。原来是缺少的SNI。
JaroslavZáruba'16

2
怎样才能与凌空配合使用?
uniruddh

1
我在Android API 19中收到此错误javax.net.ssl.SSLProtocolException:读取错误:ssl = 0xb83d7120:SSL库失败,通常是协议错误
Rukmal Dias

117

情境

运行Android版本早于Android 5.0的设备上出现SSLHandshake异常。在我的用例中,我还想创建一个TrustManager来信任我的客户证书。

我实现了NoSSLv3SocketFactoryNoSSLv3Factory,以从客户端的受支持协议列表中删除SSLv3,但是我无法使这些解决方案都起作用。

我学到了一些东西:

  • 在低于Android 5.0的设备上,默认情况下未启用TLSv1.1和TLSv1.2协议。
  • 在低于Android 5.0的设备上,默认情况下不会禁用SSLv3协议。
  • SSLv3不是安全协议,因此最好在建立连接之前将其从客户端的受支持协议列表中删除。

什么对我有用

Provider启动应用程序时,允许Android的安全性更新。

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。如果客户端/连接(或更具体地说是SSLContext)在调用之前已初始化,ProviderInstaller.installIfNeeded(...)则需要重新创建它。

不要忘记添加以下依赖项(在此处找到最新版本):

compile 'com.google.android.gms:play-services-auth:16.0.1'

资料来源:

在旁边

我不需要显式设置客户端应使用的密码算法,但是我发现了一篇SO文章,推荐撰写本文时认为最安全的算法:哪些密码套件可启用SSL套接字?


1
我不知道运行Google Play服务的设备数量的任何公开统计信息。我能找到的最新数据是Sundar Pichai在IO 2014上的演讲,他说93%的Android设备具有最新版本的Play服务。关于手动方法,当所有手动方法都无法为我工作时,我得出了此解决方案。如果网络安全对您的用户而言并不重要,那么如果用户拒绝安装Play服务,则始终可以使用HTTP。对于我们自己的应用程序,我们考虑了兼容性和安全性之间的权衡。我们会强制执行Play服务的更新。
莫里斯·加文

3
Google Play服务在中国的支持非常有限。根据您的目标受众可能会很高兴知道。但是,如果您通过Google Play进行分发,则该受众群体已经失踪了。
jayeffkay

3
请添加此依赖项以使解决方案生效。编译'com.google.android.gms:play-services-auth:10.2.0',
Pradeep Chakravarti Gudipati

3
添加ProviderInstaller.installIfNeeded(this); Application的onCreate()部分对我有用!谢谢:-)
Kelevandos

1
绝对是这里最有用的答案。感谢您提供的宝贵帮助。
BurakKarakuş18年

50

另外,您还应该知道,可以为默认情况下未启用TLS v1.2的Android 4.0设备强制执行以下操作:

将此代码放在您的应用程序文件的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();
    }

7
非常感谢rxJava OkHttpClient.Builder的帮助。
Gweltaz Niquel '18年

2
适用于Android 7.0。
CoolMind

1
谢谢@mayur gangurde,这是我工作后添加播放服务auth gradle,实现'com.google.android.gms:play-services-auth:16.0.1'–
Basant

工作完美。非常感谢您为我节省了数小时的测试和尝试。
Ninad Desai

这很方便。非常感谢!
卡斯帕·杰林斯

15

以前,我还通过自定义SSLFactory实现解决了这个问题,但是根据OkHttp docs的说法,该解决方案要容易得多。

TLS为4.2+设备使用所需密码的最终解决方案如下所示:

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);
}

请注意,支持的协议集取决于服务器上的配置。


您的解决方案是否支持API 14?
CoolMind

不能肯定地说,仅在16个以上的API设备上进行了测试,效果很好。
片段

1
片段,一个可爱的昵称:)我同意,甚至无法启动API 14模拟器,但是你的解决方案适用于Android 7.0(也有问题)和API 19
CoolMind

1
谢谢,我测试了,它能正常工作。今天再次启动了API 19仿真器,不得不添加一个解决方案stackoverflow.com/a/51285550/2914140
CoolMind

12

我在此链接中找到了解决方案。

您只需要在Android应用程序类中的代码下方放置。这就足够了。无需在翻新设置中进行任何更改。它挽救了我的一天。

public class MyApplication extends Application {
@Override
public void onCreate() {
    super.onCreate();
    try {
      // Google Play will install latest OpenSSL 
      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();
        }
    }
}

希望这会有所帮助。谢谢。


4

这为我解决了:

用于SSLSocket的Android文档说,在android开始API级别16+(Android 4.1,Jelly Bean)中支持TLS 1.1和TLS 1.2。但默认情况下它是禁用的,但是从API级别20+开始(用于手表的Android 4.4,用于手机的Kitkat Watch和用于电话,Lollipop的Android 5.0),它们已启用。但是,很难找到有关如何为运行4.1的手机启用它的任何文档。要启用TLS 1.1和1.2,您需要创建一个自定义SSLSocketFactory,它将所有调用代理到默认的SSLSocketFactory实现。除此之外,我们还必须重写返回的SSLSocket上的所有createSocket方法和callsetEnabledProtocols,以启用TLS 1.1和TLS 1.2。有关示例实现,请点击以下链接。

android 4.1。启用tls1.1和tls 1.2


2

我也有此错误报告问题。我的代码在下面。

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获取信息。我犯下的严重错误是我在Android代码中使用了.url(“ https://10.0.2.2:8010 / getShopInfo / aaa”)。但是我的后端不允许https请求。在我使用.url(“ http://10.0.2.2:8010 / getShopInfo / aaa”)之后,我的代码运行良好。所以,我想说我的错误不是模拟器的版本,而是关于请求协议的版本。我说完之后我遇到了另一个问题,但这是另一个问题,我附上了解决新问题的方法
祝你好运!


1

仅当我在genymotion(<4.4)上使用代理时,它才可重现。

在设置->无线和网络-> WiFi->(长按WiredSSID)->修改网络中检查代理设置

选择“显示高级选项”:将“代理设置”设置为“无”。


1

当我收到此错误消息时,这是因为服务器支持的协议(TLS版本)和/或密码套件未在设备上启用(甚至可能不受设备支持)。对于API 16-19,支持TLSv1.1和TLSv1.2,但默认情况下未启用。为这些版本启用它们后,仍然会出现错误,因为这些版本不支持我们的AWS CloudFront实例上的任何密码。

由于无法在Android中添加密码,因此我们不得不将CloudFront版本从TLSv1.2_2018切换到TLSv1.1_2016(该版本仍支持TLSv1.2;只是不需要它),该版本支持以下四种密码:较早的Android版本,其中两个仍被认为是强大的。

那时,错误消失了,并且通过TLSv1.2进行了呼叫,因为设备和服务器共享了至少一个协议和至少一个密码。

请参阅此页面上的表格,以了解哪些版本的Android支持并启用了哪些协议和密码。

现在,Android是否真的尝试按照错误消息的“ sslv3警报握手失败”部分所暗示的那样使用SSLv3?我对此表示怀疑; 我怀疑这是SSL库中的旧蜘蛛网,尚未清除,但我不能确定。

为了启用TLSv1.2(和TLSv1.1),我能够使用SSLSocketFactory比其他地方(如NoSSLv3SocketFactory)更简单的方法。它只是确保已启用的协议包括所有受支持的协议,并确保已启用的密码包括所有受支持的密码(对于我来说,后者不是必需的,但对其他密码也可能是)-请参阅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
    }
}

你如何使用它?
CoolMind

0

我通过以下方法解决了问题: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());

0

我的答案与上述答案很接近,但您需要完全编写该类而不进行任何更改。

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();
                    }
                }
            }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.