diff options
author | Brian Carlstrom <bdc@google.com> | 2011-05-11 23:02:10 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2011-05-16 16:05:13 -0700 |
commit | 59f9a2721b1917c74e53f28f9d24e26e29fe0221 (patch) | |
tree | a8f8d6541c4745b25a49aa8defb130ff53046784 /src/com/android | |
parent | d54f61431155eb39d39e7b0b5f5fe6a02f746c85 (diff) | |
download | android_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.java | 84 | ||||
-rw-r--r-- | src/com/android/certinstaller/CredentialHelper.java | 132 |
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); } } } |