java - 安卓java.security.cert.CertPathValidatorException : Trust anchor for certification path not found

标签 java android security ssl okhttp

Android 应用程序进行身份验证和授权的三个主机。最终宿主是 REST API。第一次使用 Oauth 身份验证和授权过程,它可以正常工作。

但如果用户在登录并访问 REST API 提供的服务后关闭应用程序,然后再次打开应用程序,就会出现此问题。 此时身份验证和授权过程没有发生,只有 REST API。它导致 java.security.cert.CertPathValidatorException 但它在第一个期间工作使用(登录然后使用该应用程序)。

有人可以解释此异常背后的情况以及该应用程序有什么问题。如果根据 this SO answer 如下忽略认证异常,则此方法有效.

SSLSocketFactory sslSocketFactory = null;

        try {
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            // Initialise the TMF as you normally would, for example:
            try {
                tmf.init((KeyStore)null);
            } catch(KeyStoreException e) {
                e.printStackTrace();
            }
            TrustManager[] trustManagers = tmf.getTrustManagers();

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

            // Create a trust manager that does not validate certificate chains
            TrustManager[] wrappedTrustManagers = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return origTrustmanager.getAcceptedIssuers();
                        }

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

                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            try {
                                origTrustmanager.checkServerTrusted(certs, authType);
                            } catch(CertificateException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            };
            //TrustManager[] trustAllCerts = TrustManagerFactory.getInstance("SSL").getTrustManagers();

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, wrappedTrustManagers, new java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return sslSocketFactory;

我正在为 http 请求使用 Okhttp 3。任何建议都有助于解决问题。如果我使用上面的代码 fragment ,请告诉我,这是否违反安全规定?会影响应用的安全吗?

最佳答案

我回答这个问题是为了根据 android 开发者网站给出关于场景和解决方案的想法,以便其他人受益。我已经使用自定义信任管理器解决了这个问题。

问题出在服务器证书上,它缺少中间证书颁发机构。然而,第一个流程证书路径以某种方式完成,结果是证书路径验证成功。

android developer site 中有一个解决方案.它建议使用信任此服务器证书的自定义信任管理器,或者它建议服务器将中间 CA 包含在服务器链中。

自定义信任管理器。来源:https://developer.android.com/training/articles/security-ssl.html#UnknownCa

// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
    ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
    caInput.close();
}

// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the okhttp to use a SocketFactory from our SSLContext
OkHttpClient okHttpClient client = new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory()).build();

更新:在从服务器端将中间证书颁发机构添加到证书链后,我的问题得到解决。这是最好的解决方案,将证书与应用程序 bundle 在一起需要在证书过期或与证书管理相关的任何其他问题上更新应用程序。

更新:03/09/2017 我发现加载证书文件的最简单方法是使用原始资源。

InputStream caInput = new BufferedInputStream(context
                .getResources().openRawResource(R.raw.certfilename));

其中 certfilename 是放置在 resources/raw 文件夹中的证书文件。也可以 http 的 sslSocketFactory(SSLSocketFactory sslSocketFactory)已被弃用,可以使用 okhttp api 文档中建议的方法。

另外,从服务器获取证书时最好使用 openssl。

openssl s_client -connect {server-address}:{port} -showcerts

因为我以前是从 firefox 抓取的,遇到被病毒 guard 修改的情况。

关于java - 安卓java.security.cert.CertPathValidatorException : Trust anchor for certification path not found,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39264056/

相关文章:

java - 如何以多线程或任何其他更快的方式处理ResultSet中的250K条记录?

android - 从Eclipse迁移到Android Studio导入库

android - 数据库调用使 Android 应用程序崩溃

php - 将 if/else PHP 重定向添加到 Wordpress 中的页面模板

java - java 的 session 安全性足够吗?

javascript - JSON 解析和安全

java - Android 使用 Intent 来控制媒体播放器?

java - 在 Web 上下文根之外动态编译和包含 JSP(X)

java - pkcs7 文件验证问题

android - 如何在android中的ConstraintLayout中重叠元素?