我尝试使用此代码向具有客户端证书身份验证的服务器发出请求:
try {
/*** CA Certificate ***/
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.caserver);
Certificate ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
System.out.println(keyStoreType);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
/*** Client Certificate ***/
KeyStore keyStore12 = KeyStore.getInstance("PKCS12");
InputStream certInput12 = getResources().openRawResource(R.raw.p12client);
keyStore12.load(certInput12, "123456key".toCharArray());
// Create a KeyManager that uses our client cert
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore12, null);
/*** SSL Connection ***/
// Create an SSLContext that uses our TrustManager and our KeyManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
URL url = new URL("https://myurl/test.json");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
System.out.println("Weeeeeeeeeee");
InputStream in = urlConnection.getInputStream(); // this throw exception
}
catch (Exception e) {
e.printStackTrace();
}
当执行到达最后一行InputStream in = urlConnection.getInputStream();
时,我获得了下一个异常。
System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
我花了很多时间尝试修复此错误但我无法找到任何信息。当我使用带有客户端证书的Web浏览器发出相同的请求时,一切正常。
有任何帮助吗?提前谢谢。
修改
我按照以下步骤生成证书:
> openssl req -config openssl.cnf -new -x509 -extensions v3_ca -days 3650 -keyout private/caserver.key -out certs/caserver.crt
> openssl req -config openssl.cnf -new -nodes -keyout private/client.key -out client.csr -days 1095
> openssl ca -config openssl.cnf -cert certs/caserver.crt -policy policy_anything -out certs/client.crt -infiles csr/client.csr
> openssl pkcs12 -export -clcerts -in certs/client.crt -inkey private/client.key -out p12client.p12
在我的代码中,我使用caserver.crt和p12client.p12。
答案 0 :(得分:0)
我不知道为什么输入流无法从Assets
文件夹中读取证书。我有同样的问题。为了克服,我已将证书放在raw
文件夹中并通过
InputStream caInput = getResources().openRawResource(R.raw.mycertificate);
并且运作良好!
答案 1 :(得分:0)
您似乎将重点放在客户端证书和可能存在的问题上,但是我认为该错误与服务器证书有关。
您有InputStream caInput = getResources().openRawResource(R.raw.caserver);
作为输入,可以输入可以验证服务器证书有效的CA的CA证书(例如,caserver可以是DER文件)。正如您的代码所说,您想信任该CA。
因此,问题可能出在此文件不是该CA的正确证书。
或者,它实际上可能是该CA的证书。但是该CA可能尚未直接签署您的服务器证书。通常,会有一个信任链,其中一个CA可能会签名,然后该CA被另一个CA信任,依此类推,一直到根CA或您信任的其他CA。
那么,为什么具有相同服务器证书的相同网站可以通过浏览器工作?您的浏览器可能会信任更多的CA,因此可以对服务器进行身份验证。而您的android应用可能不信任信任链中的一个或多个中间CA。因此,“找不到证书路径的信任锚。”
您能做什么?有关如何处理未知CA或缺少中间CA等的情况,请参见google's guide。