【发布时间】:2013-09-13 20:53:54
【问题描述】:
我有一个与 HTTPS RPC 通信的应用。
HTTP 服务器正在使用 CAcert 签名证书。
我正在使用自定义 TrustManager 来验证证书。
- 因为我无法确定,CAcert 包含在所有设备的受信任密钥库中。
- 因为我只想允许 CAcert 为这个连接签署证书。
不过,我关注的是 Google 的 best practices。 我唯一改变的是:
- 从静态字节[]而不是文件加载 CAcert 根证书
- 将示例代码加载文件的最后一部分替换为
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());。 UrlConnection 之上有一个 JSONRPC2 API。
测试的设备:
- 在 Nexus 4 / mako 上运行 API18 / CM10.2
- 在 API18 模拟器上工作
- 在 API17 模拟器上工作
- 在 API14 模拟器上工作
- 无法在运行 API10 / CM7 的 HTC G2 上运行。*
- 无法在 API8 模拟器上运行
在低 API 设备上,它无法在 SSL 握手期间验证证书。
当尝试在 API18 上使用此 TrustManager 加载 https://google.com 时,它会按预期失败,因为找不到信任锚。
所以基本上,这段代码应该可以工作,所有的方法都是 API1...
我知道,UrlConnection 在一些较低的 API 上被破坏了。
我该如何解决这个问题?
代码:
/**
* Trust only CAcert's CA. CA cert is injected as byte[]. Following best practices from
* https://developer.android.com/training/articles/security-ssl.html#UnknownCa
*/
private static void trustCAcert() {
try {
// Load CAs from an InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream is = new ByteArrayInputStream(CACERTROOTDER);
Certificate ca;
try {
ca = cf.generateCertificate(is);
Log.d(TAG, "ca=", ((X509Certificate) ca).getSubjectDN());
} finally {
is.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 sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(
sslContext.getSocketFactory());
// added for testing only
URL u = new URL(
"https://myremoteapiurlsignedwiththesamecert.com/v1/doc.html");
HttpsURLConnection con = (HttpsURLConnection) u.openConnection();
con.setSSLSocketFactory(sslContext.getSocketFactory());
BufferedReader r = new BufferedReader(
new InputStreamReader(
con.getInputStream())); // the exception is thrown here
// because verification fails
String l;
while ((l = r.readLine()) != null) {
Log.d(TAG, "l: ", l);
}
} catch (IOException e) { // none of the exceptions is thrown during setup
Log.e(TAG, "IOException", e);
} catch (CertificateException e) {
Log.e(TAG, "CertificateException", e);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException", e);
} catch (KeyStoreException e) {
Log.e(TAG, "KeyStoreException", e);
} catch (KeyManagementException e) {
Log.e(TAG, "KeyManagementException", e);
}
}
日志:
APIUtils D ca=OID.1.2.840.113549.1.9.1=#1612737570706F7274406361636572742E6F7267, CN=CA Cert Signing Authority, OU=http://www.cacert.org, O=Root CA
E IOException
E javax.net.ssl.SSLException: Not trusted server certificate
E at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:371)
E at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.getSecureSocket(HttpConnection.java:168)
E at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:399)
E at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1152)
E at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:253)
E at de.terminbuddy.android.network.APIUtils.trustCAcert(APIUtils.java:294)
E at de.terminbuddy.android.network.APIUtils.initRpcSession(APIUtils.java:243)
E at de.terminbuddy.android.network.APIUtils.runRPC(APIUtils.java:323)
E at de.terminbuddy.android.network.AsyncJSONRPCTask.doInBackground(AsyncJSONRPCTask.java:55)
E at de.terminbuddy.android.network.AsyncJSONRPCTask.doInBackground(AsyncJSONRPCTask.java:17)
E at android.os.AsyncTask$2.call(AsyncTask.java:185)
E at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
E at java.util.concurrent.FutureTask.run(FutureTask.java:137)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
E at java.lang.Thread.run(Thread.java:1096)
E Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Could not validate certificate signature.
E at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:168)
E at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:366)
E ... 15 more
E Caused by: java.security.cert.CertPathValidatorException: Could not validate certificate signature.
E at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:342)
E at java.security.cert.CertPathValidator.validate(CertPathValidator.java:202)
E at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:164)
E ... 16 more
E Caused by: java.security.SignatureException: Signature was not verified.
E at org.apache.harmony.security.provider.cert.X509CertImpl.fastVerify(X509CertImpl.java:601)
E at org.apache.harmony.security.provider.cert.X509CertImpl.verify(X509CertImpl.java:544)
E at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:337)
E ... 18 more
【问题讨论】:
标签: android ssl httpsurlconnection