summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2011-05-11 23:02:10 -0700
committerBrian Carlstrom <bdc@google.com>2011-05-16 16:05:13 -0700
commit59f9a2721b1917c74e53f28f9d24e26e29fe0221 (patch)
treea8f8d6541c4745b25a49aa8defb130ff53046784 /src/com/android
parentd54f61431155eb39d39e7b0b5f5fe6a02f746c85 (diff)
downloadandroid_packages_apps_CertInstaller-59f9a2721b1917c74e53f28f9d24e26e29fe0221.tar.gz
android_packages_apps_CertInstaller-59f9a2721b1917c74e53f28f9d24e26e29fe0221.tar.bz2
android_packages_apps_CertInstaller-59f9a2721b1917c74e53f28f9d24e26e29fe0221.zip
Make CertInstaller installed CA certs trusted by applications via default TrustManager (3 of 6)
frameworks/base Adding IKeyChainService APIs for CertInstaller and Settings use keystore/java/android/security/IKeyChainService.aidl libcore Improve exceptions to include more information luni/src/main/java/javax/security/auth/x500/X500Principal.java Move guts of RootKeyStoreSpi to TrustedCertificateStore, leaving only KeyStoreSpi methods. Added support for adding user CAs in a separate directory for system. Added support for removing system CAs by placing a copy in a sytem directory luni/src/main/java/org/apache/harmony/xnet/provider/jsse/RootKeyStoreSpi.java luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java Formerly static methods on RootKeyStoreSpi are now instance methods on TrustedCertificateStore luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java Added test for NativeCrypto.X509_NAME_hash_old and X509_NAME_hash to make sure the implementing algorithms doe not change since TrustedCertificateStore depend on X509_NAME_hash_old (OpenSSL changed the algorithm from MD5 to SHA1 when moving from 0.9.8 to 1.0.0) luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java Extensive test of new TrustedCertificateStore behavior luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStoreTest.java TestKeyStore improvements - Refactored TestKeyStore to provide simpler createCA method (and internal createCertificate) - Cleaned up to remove use of BouncyCastle specific X509Principal in the TestKeyStore API when the public X500Principal would do. - Cleaned up TestKeyStore support methods to not throw Exception to remove need for static blocks for catch clauses in tests. support/src/test/java/libcore/java/security/TestKeyStore.java luni/src/test/java/libcore/java/security/KeyStoreTest.java luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java Added private PKIXParameters contructor for use by IndexedPKIXParameters to avoid wart of having to lookup and pass a TrustAnchor to satisfy the super-class sanity check. luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java luni/src/main/java/org/apache/harmony/xnet/provider/jsse/IndexedPKIXParameters.java luni/src/main/java/java/security/cert/PKIXParameters.java packages/apps/CertInstaller Change CertInstaller to call IKeyChainService.installCertificate for CA certs to pass them to the KeyChainServiceTest which will make them available to all apps through the TrustedCertificateStore. Change PKCS12 extraction to use AsyncTask. src/com/android/certinstaller/CertInstaller.java Added installCaCertsToKeyChain and hasCaCerts accessor for use by CertInstaller. Use hasUserCertificate() internally. Cleanup coding style. src/com/android/certinstaller/CredentialHelper.java packages/apps/KeyChain Added MANAGE_ACCOUNTS so that IKeyChainService.reset implementation can remove KeyChain accounts. AndroidManifest.xml Implement new IKeyChainService methods: - Added IKeyChainService.installCaCertificate to install certs provided by CertInstaller using the TrustedCertificateStore. - Added IKeyChainService.reset to allow Settings to remove the KeyChain accounts so that any app granted access to keystore credentials are revoked when the keystore is reset. src/com/android/keychain/KeyChainService.java packages/apps/Settings Changed com.android.credentials.RESET credential reset action to also call IKeyChainService.reset to remove any installed user CAs and remove KeyChain accounts to have AccountManager revoke credential granted to private keys removed during the RESET. src/com/android/settings/CredentialStorage.java Added toast text value for failure case res/values/strings.xml system/core Have init create world readable /data/misc/keychain to allow apps to access user added CA certificates installed by the CertInstaller. rootdir/init.rc Change-Id: Idc4e6dd927cf829268a684061e14412623f89d80
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/certinstaller/CertInstaller.java84
-rw-r--r--src/com/android/certinstaller/CredentialHelper.java132
2 files changed, 150 insertions, 66 deletions
diff --git a/src/com/android/certinstaller/CertInstaller.java b/src/com/android/certinstaller/CertInstaller.java
index 3d71337..737dc47 100644
--- a/src/com/android/certinstaller/CertInstaller.java
+++ b/src/com/android/certinstaller/CertInstaller.java
@@ -21,11 +21,17 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.IBinder;
import android.security.Credentials;
import android.security.KeyStore;
+import android.security.IKeyChainService;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -34,6 +40,8 @@ import android.widget.Toast;
import java.io.Serializable;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.Map;
/**
@@ -173,6 +181,12 @@ public class CertInstaller extends Activity
Log.d(TAG, "credential is added: " + mCredentials.getName());
Toast.makeText(this, getString(R.string.cert_is_added,
mCredentials.getName()), Toast.LENGTH_LONG).show();
+
+ if (mCredentials.hasCaCerts()) {
+ // more work to do, dont't finish just yet
+ new InstallCaCertsToKeyChainTask().execute();
+ return;
+ }
setResult(RESULT_OK);
} else {
Log.d(TAG, "credential not saved, err: " + resultCode);
@@ -184,6 +198,47 @@ public class CertInstaller extends Activity
finish();
}
+ private class InstallCaCertsToKeyChainTask extends AsyncTask<Void, Void, Boolean> {
+
+ @Override protected Boolean doInBackground(Void... unused) {
+ final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1);
+ ServiceConnection keyChainServiceConnection = new ServiceConnection() {
+ @Override public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ q.put(IKeyChainService.Stub.asInterface(service));
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+ @Override public void onServiceDisconnected(ComponentName name) {}
+ };
+ boolean isBound = bindService(new Intent(IKeyChainService.class.getName()),
+ keyChainServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ if (!isBound) {
+ Log.w(TAG, "could not bind to KeyChainService");
+ return false;
+ }
+ IKeyChainService keyChainService;
+ try {
+ keyChainService = q.take();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return false;
+ }
+ boolean success = mCredentials.installCaCertsToKeyChain(keyChainService);
+ unbindService(keyChainServiceConnection);
+ return success;
+ }
+
+ @Override protected void onPostExecute(Boolean success) {
+ if (success) {
+ setResult(RESULT_OK);
+ }
+ finish();
+ }
+ }
+
void installOthers() {
if (mCredentials.hasKeyPair()) {
saveKeyPair();
@@ -251,23 +306,20 @@ public class CertInstaller extends Activity
// show progress bar and extract certs in a background thread
showDialog(PROGRESS_BAR_DIALOG);
- new Thread(new Runnable() {
- public void run() {
- final boolean success = mCredentials.extractPkcs12(password);
-
- runOnUiThread(new Runnable() {
- public void run() {
- MyAction action = new OnExtractionDoneAction(success);
- if (mState == STATE_PAUSED) {
- // activity is paused; run it in next onResume()
- mNextAction = action;
- } else {
- action.run(CertInstaller.this);
- }
- }
- });
+ new AsyncTask<Void,Void,Boolean>() {
+ @Override protected Boolean doInBackground(Void... unused) {
+ return mCredentials.extractPkcs12(password);
+ }
+ @Override protected void onPostExecute(Boolean success) {
+ MyAction action = new OnExtractionDoneAction(success);
+ if (mState == STATE_PAUSED) {
+ // activity is paused; run it in next onResume()
+ mNextAction = action;
+ } else {
+ action.run(CertInstaller.this);
+ }
}
- }).start();
+ }.execute();
}
void onExtractionDone(boolean success) {
diff --git a/src/com/android/certinstaller/CredentialHelper.java b/src/com/android/certinstaller/CredentialHelper.java
index ae55e7d..a2e0736 100644
--- a/src/com/android/certinstaller/CredentialHelper.java
+++ b/src/com/android/certinstaller/CredentialHelper.java
@@ -19,7 +19,9 @@ package com.android.certinstaller;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.RemoteException;
import android.security.Credentials;
+import android.security.IKeyChainService;
import android.text.Html;
import android.util.Log;
@@ -33,15 +35,19 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.security.KeyStore;
+import java.security.KeyFactory;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
-import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -72,11 +78,15 @@ class CredentialHelper {
CredentialHelper(Intent intent) {
Bundle bundle = intent.getExtras();
- if (bundle == null) return;
+ if (bundle == null) {
+ return;
+ }
String name = bundle.getString(CERT_NAME_KEY);
bundle.remove(CERT_NAME_KEY);
- if (name != null) mName = name;
+ if (name != null) {
+ mName = name;
+ }
Log.d(TAG, "# extras: " + bundle.size());
for (String key : bundle.keySet()) {
@@ -95,15 +105,16 @@ class CredentialHelper {
outStates.putByteArray(Credentials.USER_PRIVATE_KEY,
mUserKey.getEncoded());
}
- ArrayList<byte[]> certs =
- new ArrayList<byte[]>(mCaCerts.size() + 1);
- if (mUserCert != null) certs.add(mUserCert.getEncoded());
+ ArrayList<byte[]> certs = new ArrayList<byte[]>(mCaCerts.size() + 1);
+ if (mUserCert != null) {
+ certs.add(mUserCert.getEncoded());
+ }
for (X509Certificate cert : mCaCerts) {
certs.add(cert.getEncoded());
}
outStates.putByteArray(CERTS_KEY, Util.toBytes(certs));
- } catch (Exception e) {
- // should not occur
+ } catch (CertificateEncodingException e) {
+ throw new AssertionError(e);
}
}
@@ -111,11 +122,14 @@ class CredentialHelper {
mBundle = (HashMap) savedStates.getSerializable(DATA_KEY);
mName = savedStates.getString(CERT_NAME_KEY);
byte[] bytes = savedStates.getByteArray(Credentials.USER_PRIVATE_KEY);
- if (bytes != null) setPrivateKey(bytes);
+ if (bytes != null) {
+ setPrivateKey(bytes);
+ }
- ArrayList<byte[]> certs =
- Util.fromBytes(savedStates.getByteArray(CERTS_KEY));
- for (byte[] cert : certs) parseCert(cert);
+ ArrayList<byte[]> certs = Util.fromBytes(savedStates.getByteArray(CERTS_KEY));
+ for (byte[] cert : certs) {
+ parseCert(cert);
+ }
}
X509Certificate getUserCertificate() {
@@ -123,11 +137,12 @@ class CredentialHelper {
}
private void parseCert(byte[] bytes) {
- if (bytes == null) return;
+ if (bytes == null) {
+ return;
+ }
try {
- CertificateFactory certFactory =
- CertificateFactory.getInstance("X.509");
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)
certFactory.generateCertificate(
new ByteArrayInputStream(bytes));
@@ -151,7 +166,7 @@ class CredentialHelper {
basicConstraints = ((DEROctetString) obj).getOctets();
obj = new ASN1InputStream(basicConstraints).readObject();
return new BasicConstraints((ASN1Sequence) obj).isCA();
- } catch (Exception e) {
+ } catch (IOException e) {
return false;
}
}
@@ -169,20 +184,22 @@ class CredentialHelper {
return (mUserCert != null);
}
+ boolean hasCaCerts() {
+ return !mCaCerts.isEmpty();
+ }
+
boolean hasAnyForSystemInstall() {
- return ((mUserKey != null) || (mUserCert != null)
- || !mCaCerts.isEmpty());
+ return (mUserKey != null) || hasUserCertificate() || hasCaCerts();
}
void setPrivateKey(byte[] bytes) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- mUserKey = keyFactory.generatePrivate(
- new PKCS8EncodedKeySpec(bytes));
- } catch (Exception e) {
- // should not occur
- Log.w(TAG, "setPrivateKey(): " + e);
- throw new RuntimeException(e);
+ mUserKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ } catch (InvalidKeySpecException e) {
+ throw new AssertionError(e);
}
}
@@ -231,25 +248,40 @@ class CredentialHelper {
Intent intent = new Intent("com.android.credentials.INSTALL");
// To prevent the private key from being sniffed, we explicitly spell
// out the intent receiver class.
- intent.setClassName("com.android.settings",
- "com.android.settings.CredentialStorage");
+ intent.setClassName("com.android.settings", "com.android.settings.CredentialStorage");
if (mUserKey != null) {
- intent.putExtra(Credentials.USER_PRIVATE_KEY + mName,
- convertToPem(mUserKey));
+ intent.putExtra(Credentials.USER_PRIVATE_KEY + mName, convertToPem(mUserKey));
}
if (mUserCert != null) {
- intent.putExtra(Credentials.USER_CERTIFICATE + mName,
- convertToPem(mUserCert));
+ intent.putExtra(Credentials.USER_CERTIFICATE + mName, convertToPem(mUserCert));
}
if (!mCaCerts.isEmpty()) {
- Object[] caCerts = (Object[])
- mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
- intent.putExtra(Credentials.CA_CERTIFICATE + mName,
- convertToPem(caCerts));
+ Object[] caCerts = (Object[]) mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
+ intent.putExtra(Credentials.CA_CERTIFICATE + mName, convertToPem(caCerts));
}
return intent;
}
+ boolean installCaCertsToKeyChain(IKeyChainService keyChainService) {
+ for (X509Certificate caCert : mCaCerts) {
+ byte[] bytes = null;
+ try {
+ bytes = caCert.getEncoded();
+ } catch (CertificateEncodingException e) {
+ throw new AssertionError(e);
+ }
+ if (bytes != null) {
+ try {
+ keyChainService.installCaCertificate(bytes);
+ } catch (RemoteException e) {
+ Log.w(TAG, "installCaCertsToKeyChain(): " + e);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
boolean extractPkcs12(String password) {
try {
return extractPkcs12Internal(password);
@@ -262,21 +294,20 @@ class CredentialHelper {
private boolean extractPkcs12Internal(String password)
throws Exception {
// TODO: add test about this
- java.security.KeyStore keystore =
- java.security.KeyStore.getInstance("PKCS12");
- PasswordProtection passwordProtection =
- new PasswordProtection(password.toCharArray());
+ java.security.KeyStore keystore = java.security.KeyStore.getInstance("PKCS12");
+ PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray());
keystore.load(new ByteArrayInputStream(getData(Credentials.PKCS12)),
- passwordProtection.getPassword());
+ passwordProtection.getPassword());
Enumeration<String> aliases = keystore.aliases();
- if (!aliases.hasMoreElements()) return false;
+ if (!aliases.hasMoreElements()) {
+ return false;
+ }
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
KeyStore.Entry entry = keystore.getEntry(alias, passwordProtection);
- Log.d(TAG, "extracted alias = " + alias
- + ", entry=" + entry.getClass());
+ Log.d(TAG, "extracted alias = " + alias + ", entry=" + entry.getClass());
if (entry instanceof PrivateKeyEntry) {
mName = alias;
@@ -292,11 +323,12 @@ class CredentialHelper {
Certificate[] certs = entry.getCertificateChain();
Log.d(TAG, "# certs extracted = " + certs.length);
- List<X509Certificate> caCerts = mCaCerts =
- new ArrayList<X509Certificate>(certs.length);
+ List<X509Certificate> caCerts = mCaCerts = new ArrayList<X509Certificate>(certs.length);
for (Certificate c : certs) {
X509Certificate cert = (X509Certificate) c;
- if (isCa(cert)) caCerts.add(cert);
+ if (isCa(cert)) {
+ caCerts.add(cert);
+ }
}
Log.d(TAG, "# ca certs extracted = " + mCaCerts.size());
@@ -308,13 +340,13 @@ class CredentialHelper {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(bao);
PEMWriter pw = new PEMWriter(osw);
- for (Object o : objects) pw.writeObject(o);
+ for (Object o : objects) {
+ pw.writeObject(o);
+ }
pw.close();
return bao.toByteArray();
} catch (IOException e) {
- // should not occur
- Log.w(TAG, "convertToPem(): " + e);
- throw new RuntimeException(e);
+ throw new AssertionError(e);
}
}
}