HTTPS RPC と通信するアプリがあります。
HTTP サーバーは、CAcert 署名付き証明書を使用しています。
証明書の検証にカスタム TrustManager を使用しています。
- 確信が持てないため、CAcert はすべてのデバイスの信頼できるキー ストアに含まれています。
- CAcert のみがこの接続の証明書に署名できるようにしたいためです。
ただし、Google のベスト プラクティスに従っています。私が変更した唯一のことは次のとおりです。
- ファイルの代わりに静的な byte[] から CAcert ルート証明書をロードします
- サンプルコードがファイルをロードする最後の部分を に置き換えます
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
。UrlConnection の上に JSONRPC2 API があります。
テスト済みのデバイス:
- Nexus 4 / mako running API18 / CM10.2 に取り組んでいます
- API18エミュレータで作業中
- API17エミュレータで作業中
- API14エミュレータで作業中
- API10 / CM7 を実行している HTC G2では動作しません。*
- API8 エミュレーターで動作しない
低 API デバイスでは、SSL ハンドシェイク中に証明書の検証に失敗します。API18でこれを読み込も
うとすると、トラスト アンカーが見つからないため、期待どおりに失敗します。
したがって、基本的に、このコードは機能するはずで、すべてのメソッドは API1 です...
私は、UrlConnection が一部の下位 API で壊れていることを知っています。https://google.com
TrustManager
これを修正するにはどうすればよいですか?
コード:
/**
* 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