且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Android java.security.cert.CertPathValidatorException:找不到证书路径的信任锚

更新时间:2021-10-24 05:49:47

我正在回答这个问题,目的是根据android开发人员网站提供有关方案和解决方案的想法,以使他人受益.我已经使用自定义信任管理器解决了这个问题.

I am answering to this to give an idea about the scenario and solution as per the android developer site for others benefit. I have solved this using custom trust manager.

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

The problem was with the server certificate, it misses intermediate certificate authority. However with the first flow certificate path is completed somehow and result was successful certificate path validation.

Android开发者网站中,有一个解决方案.建议使用信任此服务器证书的自定义信任管理器,或者建议服务器在服务器链中包括中间CA.

There is a solution for this in android developer site. it suggest to use custom trust manager that trusts this server certificate or it suggest to server to include the intermediate CA in the server chain.

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

custom trust manager. source: 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();

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

UPDATE: My problem was solved after intermediate certificate authority added to the certificate chain from the server side. It is the best solution, Bundling the certificate with the app requires app to be updated on certificate expiring or any other issues related with certificate management.

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

UPDATE:03/09/2017 Easiest way to load certificate file I found is use of raw resource.

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

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

where certfilename is the certificate file placed in resources/raw folder. Also okhttp's sslSocketFactory(SSLSocketFactory sslSocketFactory) has been deprecated and suggested approach in the okhttp api doc can be used.

另外,从服务器获取证书时,***使用openssl.

Also when getting the certificate from the server it is better to use openssl.

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

因为我以前是从firefox那里获取它的,所以遇到了病毒防护人员对其进行更改的情况.

Because I used to grab that from firefox and faced situation where it was altered by the virus guard.