使用Volley时出现SSL异常

12

我在Android中使用Volley来执行我的应用程序请求。不幸的是,我遇到了以下错误:

com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x61e15f78: Failure in SSL library, usually a protocol error
    error:1407743E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert inappropriate fallback (external/openssl/ssl/s23_clnt.c:744 0x5b647c58:0x00000000)

我在一个 ViewPager 中使用两个 Fragments,它们在 onResume 时请求其内容。请求的 URL 基本相同,除了查询参数(用于设置内容类型,例如热门 vs 趋势)。

URL 的格式为 https://host/api/content?type={hot/trending}。授权是通过请求头完成的。

这个异常的奇怪之处在于只有其中一个请求失败,而且每次失败的都不同。在它们之间添加延迟后,异常停止发生了(奇怪的指向某些竞争条件?)。但这似乎是一个糟糕的解决方法,我想找到正确的解决方法。

您认为可能是什么原因导致了这个问题?

编辑:

请求是使用提供队列的单例创建的标准方式,如下所示:

final RequestQueue requestQueue = RequestQueueSingleton.getInstance(getActivity()).getRequestQueue();
final GsonRequestGet<SearchApiWrapper> gsonRequest = new GsonRequestGet<>(clazz, url,successListener, errorListener);
gsonRequest.setRetryPolicy(new DefaultRetryPolicy(3000, 3, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
gsonRequest.setTag(mTag);
requestQueue.add(gsonRequest);

这里是单例类:

public class RequestQueueSingleton {

    private static RequestQueueSingleton mInstance;
    private RequestQueue mRequestQueue;
    private Context mContext;

    public RequestQueueSingleton(Context context) {
        mContext = context;
        mRequestQueue = getRequestQueue();
    }

    /**
     * Returns a instance of this singleton
     */
    public static synchronized RequestQueueSingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new RequestQueueSingleton(context);
        }
        return mInstance;
    }

    /**
     * Returns instance of the request queue
     */
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext());
        }
        return mRequestQueue;
    }
}

看看这个:http://stackoverflow.com/questions/22564317/https-support-for-volley-android-networking-library - EE66
@GuilhE 我编辑了代码,添加了requestQueue创建逻辑以及我如何使用它的说明。 - Edson Menegatti
@EE66 在提问之前,我确实查看了那个答案(以及其他答案),但不幸的是它们似乎并不适用于这种情况,因为我的请求通常都能正常工作。唯一失败的时候是当这两个片段同时将它们的请求添加到队列中时。 - Edson Menegatti
我不知道这是否是问题的原因,或者是否能解决它,但我通常会像这样创建我的队列:static { requestQueue = Volley.newRequestQueue(Application.getContext(), new HurlStack(null, ClientSSLSocketFactory.getSocketFactory())); } - GuilhE
@GuilhE,你是如何创建ClientSSLSocketFactory的? - Edson Menegatti
显示剩余2条评论
3个回答

3
在我们的评论之后,也许这可以帮助您:
您的requestQueue
static {
    requestQueue = Volley.newRequestQueue(Application.getContext(), new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));
}

客户端SSL套接字工厂(ClientSSLSocketFactory):
public class ClientSSLSocketFactory extends SSLCertificateSocketFactory {
    private SSLContext sslContext;

    public static SSLSocketFactory getSocketFactory(){
        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 null;
                }
            };
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[] { tm }, null);

            SSLSocketFactory ssf = ClientSSLSocketFactory.getDefault(10000, new SSLSessionCache(Application.getInstance()));

            return ssf;
        } catch (Exception ex) {
            return null;
        }
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

我会说是的,因为当我们执行 sslContext.init(null, new TrustManager[] { tm }, null); 时,我们使用了没有实现的 tm。我还没有测试过,我会尽快测试一下。 - GuilhE
不幸的是,问题仍在发生,虽然频率较低但仍在发生。有什么想法是什么原因呢? - Edson Menegatti
@droid_dev 我在一些项目中使用这个类,在所有API中都能正常工作。你遇到了哪种类型的错误或者你的服务器报告有什么问题? - GuilhE
基本上这是一个证书问题。相同的错误显示为 javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. - moDev
请查看此链接:https://developer.android.com/training/articles/security-ssl.html#CommonProblems,其中可能列出了一些问题。 - GuilhE
显示剩余5条评论

3

添加以下导入语句。

import javax.net.ssl.TrustManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import java.util.ResourceBundle;

在进行网络调用之前,请添加以下代码。
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    } };
    SSLContext sc = null;
    try {
        sc = SSLContext.getInstance("SSL");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    try {
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

0
唯一对我有效的方法是这个:
首先将以下方法放入您的项目的“Application”类中:
private void updateAndroidSecurityProvider() {
    try { 
        ProviderInstaller.installIfNeeded(this); 
    } catch (Exception e) {
        e.getMessage(); 
    } 
}

然后在onCreate方法中调用它。

我还没有遇到任何问题。

参考link


那个方法在哪里被调用了? - Nanda R.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接