使用HttpsURLConnection忽略SSL证书的方法

15

我使用了两种方法来尝试消费HTTPS URL:

旧的已弃用的方法会返回正确值的响应。

这里是代码,它不需要忽略SSL证书,它会自己忽略或者可能使用其他技术:

public String newApiPost(String url,String p1,String p2,String p3){

    HttpClient httpClient = new DefaultHttpClient();
    // replace with your url
    HttpPost httpPost = new HttpPost(url);

    //Post Data
    List<NameValuePair> nameValuePair = new ArrayList<NameValuePair>();
    nameValuePair.add(new BasicNameValuePair("cliend_id",p1));
    nameValuePair.add(new BasicNameValuePair("client_secret", p2));
    nameValuePair.add(new BasicNameValuePair("key",p3));

    //Encoding POST data
    try {
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePair));
    } catch (UnsupportedEncodingException e) {
        // log exception
        e.printStackTrace();
    }

    //making POST request.
    try {
        HttpResponse response = httpClient.execute(httpPost);
        HttpEntity httpEntity = response.getEntity();
        String   result = EntityUtils.toString(httpEntity,     
        HTTP.UTF_8);
        Log.d("response", result);

        // write response to log
        Log.d("zzuu", result.toString());
    } catch (ClientProtocolException e) {
        // Log exception
        e.printStackTrace();
    } catch (IOException e) {
        // Log exception
        e.printStackTrace();
        Log.d("dddfg", e.toString());
    }catch (Exception e){
        Log.d("dddfg", e.toString());
    }

    return "";
 }

然后我使用了未弃用的方法HttpsUrlConnections,该方法需要忽略SSL证书。我尝试了很多方式但都没有成功:

public void sendNew (String urls,String paramValue1,String paramValue2,String paramValue3){
    URL url = null;
    BufferedReader reader = null;
    StringBuilder stringBuilder;
    String data="";

    try {
        url = new URL(urls);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

        conn.setReadTimeout(10000);
        conn.setConnectTimeout(15000);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("charset", "utf-8");
        conn.setDoInput(true);
        conn.setDoOutput(true);
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("cliend_id", paramValue1)
                .appendQueryParameter("client_secret", paramValue2)
                .appendQueryParameter("apikey", paramValue3);
        String query = builder.build().getEncodedQuery();

        OutputStream os = conn.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
        writer.write(query);
        writer.flush();
        writer.close();
        os.close();

        conn.connect();

        reader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        stringBuilder = new StringBuilder();

        String line = null;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line + "\n");
        }
        data= stringBuilder.toString();
        Log.d("zzuu", data);

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (ProtocolException e) {
        e.printStackTrace();
        Log.d("dddfg", e.toString());

    } catch (IOException e) {
        e.printStackTrace();
        Log.d("dddfg", e.toString());

    }catch (Exception e){
        Log.d("dddfg", e.toString());

    }
}

它会以以下错误响应:
10-12 17:06:06.135  16052-16075/  W/System.err﹕ java.io.IOException: Hostname 'xxxxxxxx' was not verified
10-12 17:06:06.139  16052-16075/  W/System.err﹕ at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
10-12 17:06:06.139  16052-16075/  W/System.err﹕ at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
10-12 17:06:06.139  16052-16075/  W/System.err﹕ at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
10-12 17:06:06.170  16052-16075/  W/System.err﹕ at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
10-12 17:06:06.171  16052-16075/  W/System.err﹕ at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:292)
10-12 17:06:06.175  16052-16075/ W/System.err﹕ at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:185)
10-12 17:06:06.175  16052-16075/  W/System.err﹕ at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
10-12 17:06:06.176  16052-16075/  W/System.err﹕ at ubicall.sand.ubicall.helper.JSONParser.method(JSONParser.java:422)
10-12 17:06:06.177  16052-16075/  W/System.err﹕ at ubicall.sand.ubicall.activity.SplashActivity$Access.doInBackground(SplashActivity.java:111)
10-12 17:06:06.177  16052-16075/  W/System.err﹕ at ubicall.sand.ubicall.activity.SplashActivity$Access.doInBackground(SplashActivity.java:106)
10-12 17:06:06.177  16052-16075/  W/System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:287)

我不明白问题出在哪里,因为在restclient和postman中它都能正常工作。数据使用POST方法发送到URL的主体中,然后我发现当使用HttpsURLConnection时需要忽略SSL证书,但是我尝试了很多方法都没有成功。

我使用

private void trustEveryone() {
    try {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new X509TrustManager[]{new X509TrustManager(){
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(
        context.getSocketFactory());
    } catch (Exception e) { // should never happen
        e.printStackTrace();
    }
}

看起来是证书问题。请查看此处以获取更多信息:https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs - Damien O'Reilly
谢谢回复。在Postman中正常工作但在Android中无法工作,这是正常的吗? - androidAhmed
URLConnection比HttpClient更可取。请查看http://stackoverflow.com/questions/32591295/subsequent-https-post-request-in-java-with-cookies-retained/32592521#32592521。 - Ravindra babu
请使用HttpURLConnection而不是java.net.URLConnection来发送和处理HTTP请求。这将确保更好的性能和可靠性,并且可以避免一些已知的问题。如果您正在使用Android平台,则可以考虑使用https://github.com/loopj/android-async-http库,它提供了一个简单易用的接口来处理异步HTTP请求。 - Ravindra babu
请看一下我编辑过的答案。 - BNK
1个回答

13

根据您提供的logcat信息,我认为您应该阅读以下链接:

  1. HostnameVerifier

    如果URL的主机名与对等方的标识主机名不匹配,则应在握手期间使用它。

  2. 主机名验证常见问题

    发生这种情况的一个原因是服务器配置错误。服务器配置了一个证书,该证书不具有与您要到达的服务器相匹配的主题或主题备用名称字段...

然后,您可以参考以下问题的答案:

OkHttp trusting certificate


编辑: 关于您的“忽略ssl”的想法,您可以尝试以下操作(但据说不建议这样做):

public class HttpsTrustManager implements X509TrustManager {
    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

    @Override
    public void checkClientTrusted(
            X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    @Override
    public void checkServerTrusted(
            X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers;
    }

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[]{new HttpsTrustManager()};
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
    }
}

然后在您的活动中调用HttpsTrustManager.allowAllSSL();


你看过我在以下链接中的回答了吗?https://dev59.com/mo_ea4cB1Zd3GeqPVfuh。创建完HostnameVerifier之后,你可以为你的HttpsURLConnection设置setHostnameVerifier。 - BNK
如果错误仍在发生,请发布新的日志记录,因为我认为可能会抛出另一个异常,例如“找不到信任锚点”... - BNK
使用Java语言,返回异常信息:java.lang.NullPointerException: lock == null。 - androidAhmed
对于sslsocketfactory,您需要在项目中拥有证书文件/密钥库文件。 - BNK
请在以下链接中阅读我的答案:https://dev59.com/R47ea4cB1Zd3GeqPAFoU - BNK
显示剩余7条评论

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