Android:应用成功运行,但 logcat 具有 java.security.cert.CertPathValidatorException:未找到证书路径的信任锚

Android: app runs successfully, but logcat has java.security.cert.CertPathValidatorException: Trust anchor for certification path not found

我的 Android 应用成功向远程 Web 服务发出 HTTPS 请求(响应代码:200)。但是,在 Android Studio 的 logcat window 中,日志级别 Verbose 具有如下屏幕截图所示的消息(日志级别错误无消息)

这是我的代码。任何解释表示赞赏。感谢您的阅读。

package com.example.apiclient2;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class MainActivity extends AppCompatActivity {

private final Context mContext = this;
private TextView mTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextView = (TextView) findViewById(R.id.textView);

    new APIRequest().execute();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {

    final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];

    return new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return originalTrustManager.getAcceptedIssuers();
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    try {
                        originalTrustManager.checkClientTrusted(certs, authType);
                    } catch (CertificateException e) {
                        e.printStackTrace();
                    }
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    try {
                        originalTrustManager.checkServerTrusted(certs, authType);
                    } catch (CertificateException e) {
                        e.printStackTrace();
                    }
                }
            }
    };
}

private SSLSocketFactory getSSLSocketFactory(String keyStoreType, int keystoreResId)
        throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(keystoreResId);

    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();

    if (keyStoreType == null || keyStoreType.length() == 0) {
        keyStoreType = KeyStore.getDefaultType();
    }
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, wrappedTrustManagers, null);

    return sslContext.getSocketFactory();
}

private class APIRequest extends AsyncTask<Void, Void, String> {

    @Override
    protected String doInBackground(Void... params) {

        try {
            URL url = new URL("https://192.168.0.100/api/document");
            HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
            SSLSocketFactory sslSocketFactory = getSSLSocketFactory("BKS", R.raw.mybks_cert);

            urlConnection.setSSLSocketFactory(sslSocketFactory);
            urlConnection.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(false);
            urlConnection.setRequestMethod("GET");
            urlConnection.connect();

            return String.valueOf(urlConnection.getResponseCode());

        } catch (Exception e) {
            return e.toString();
        }
    }

    @Override
    protected void onPostExecute(String response) {
        super.onPostExecute(response);
        Toast.makeText(mContext, response, Toast.LENGTH_SHORT).show();
        mTextView.setText(response);
    }
}

}

问题可能是其中之一,请看这个

     1.The CA that issued the server certificate was unknown
     2.The server certificate wasn't signed by a CA, but was self signed
     3.The server configuration is missing an intermediate CA

我发现了我的错误。由于 getWrapped TrustManagers 中的 e.printStackTrace()。只需要忽略异常即可。