diff options
author | Hung-ying Tyan <tyanh@google.com> | 2009-09-28 10:26:15 +0800 |
---|---|---|
committer | Hung-ying Tyan <tyanh@google.com> | 2009-09-28 18:06:30 +0800 |
commit | c87a48ac1c22403b690330f0cf7a1890f9a0c4eb (patch) | |
tree | 613d3c6dd5c4683c25b872951099b6c6f0032361 /src | |
parent | bedff945a7dffd019035154f78018b350e47ee66 (diff) | |
download | android_packages_apps_CertInstaller-c87a48ac1c22403b690330f0cf7a1890f9a0c4eb.tar.gz android_packages_apps_CertInstaller-c87a48ac1c22403b690330f0cf7a1890f9a0c4eb.tar.bz2 android_packages_apps_CertInstaller-c87a48ac1c22403b690330f0cf7a1890f9a0c4eb.zip |
Fix the issue where cert comes later than keypair.
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/certinstaller/CertInstaller.java | 849 |
1 files changed, 841 insertions, 8 deletions
diff --git a/src/com/android/certinstaller/CertInstaller.java b/src/com/android/certinstaller/CertInstaller.java index fbc82ff..ca1d252 100644 --- a/src/com/android/certinstaller/CertInstaller.java +++ b/src/com/android/certinstaller/CertInstaller.java @@ -19,10 +19,18 @@ package com.android.certinstaller; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Environment; +import android.os.FileObserver; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.security.Credentials; +import android.security.KeyStore; import android.text.Html; import android.text.TextUtils; import android.util.Log; @@ -30,26 +38,851 @@ import android.view.View; import android.widget.TextView; import android.widget.Toast; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.openssl.PEMWriter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStreamWriter; +import java.io.Serializable; +import java.security.KeyStore.PasswordProtection; +import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** - * The app that installs certificates to keystore. It reacts to two intents: - * {@link CertTool#ACTION_ADD_CREDENTIAL} and - * {@link CertTool#ACTION_INSTALL_CERT_FROM_SDCARD}. + * Installs certificates to the system keystore. It reacts to the + * {@link Credentials#INSTALL_ACTION} intent. */ -public class CertInstaller extends Activity - implements DialogInterface.OnClickListener, - DialogInterface.OnDismissListener, +public class CertInstaller extends PreferenceActivity + implements Preference.OnPreferenceClickListener, FileFilter, DialogInterface.OnCancelListener { + private static final String TAG = "CertInstaller"; + + private static final int NAME_CREDENTIAL_DIALOG = 1; + private static final int PKCS12_PASSWORD_DIALOG = 2; + private static final int PROGRESS_BAR_DIALOG = 3; + private static final int REQUEST_SYSTEM_INSTALL_CODE = 1; + + private static final String DOWNLOAD = "download"; + + // dialog result + private static final int REOPEN = 1; // re-open the dialog + private static final int DONE = 2; + private static final int CANCELLED = 3; + + private static final byte[] PKEY_MAP_KEY = "PKEY_MAP".getBytes(); + + private KeyStore mKeyStore = KeyStore.getInstance(); + private View mView; + private int mDialogResult = REOPEN; + + private boolean mIsBrowsingSdcard; + private SdcardMonitor mSdcardMonitor; + private File mCertFile; + + private CredentialHelper mCredentials; + private Runnable mBottomHalf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + handleIntents(getIntent()); + } + + @Override + protected void onResume() { + super.onResume(); + if (mBottomHalf != null) { + Runnable r = mBottomHalf; + mBottomHalf = null; + r.run(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + stopSdcardMonitor(); + } + + @Override + protected Dialog onCreateDialog (int id) { + switch (id) { + case PKCS12_PASSWORD_DIALOG: + return createPkcs12PasswordDialog(); + + case NAME_CREDENTIAL_DIALOG: + return createNameCredentialDialog(); + + case PROGRESS_BAR_DIALOG: + ProgressDialog dialog = new ProgressDialog(this); + dialog.setMessage(getString(R.string.extracting_pkcs12)); + dialog.setIndeterminate(true); + dialog.setCancelable(false); + return dialog; + + default: + return null; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_SYSTEM_INSTALL_CODE) { + if (resultCode == RESULT_OK) { + Log.d(TAG, "credential is added: " + mCredentials.getName()); + Toast.makeText(this, + getString(R.string.cert_is_added, + mCredentials.getName()), + Toast.LENGTH_LONG).show(); + deleteCert(mCertFile); + if (!mIsBrowsingSdcard) finish(); + } else { + Log.d(TAG, "credential not saved, err: " + resultCode); + toastErrorAndFinish(R.string.cert_not_saved); + } + } else { + Log.w(TAG, "unknown request code: " + requestCode); + return; + } + } + + private void handleIntents(Intent intent) { + if (intent == null) return; + String action = intent.getAction(); + + if (Credentials.INSTALL_ACTION.equals(action)) { + mCredentials = new CredentialHelper(intent); + + if (!mCredentials.containsAny()) { + mIsBrowsingSdcard = true; + addPreferencesFromResource(R.xml.pick_file_pref); + startSdcardMonitor(); + createFileList(); + } else if (mCredentials.isPkcs12KeyStore()) { + showDialog(PKCS12_PASSWORD_DIALOG); + } else { + installAskingKeyStoreAccess(); + } + } + } + + private void installAskingKeyStoreAccess() { + if (mCredentials.isKeyPair()) { + if (isKeyStoreLocked()) { + unlockKeyStore(); + return; + } + saveKeyPair(); + finish(); + } else { + X509Certificate crt = mCredentials.getUserCertificate(); + if (crt != null) { + // find matched private key + String key = toMd5(crt.getPublicKey().getEncoded()); + Map<String, byte[]> map = getPkeyMap(); + byte[] privatekey = map.get(key); + if (privatekey != null) { + if (isKeyStoreLocked()) { + unlockKeyStore(); + return; + } + Log.d(TAG, "found matched key: " + privatekey); + map.remove(key); + setPkeyMap(map); + + mCredentials.setPrivateKey(privatekey); + } else { + Log.d(TAG, "didn't find matched private key: " + key); + } + } + nameCredential(); + } + } + + private void unlockKeyStore() { + mBottomHalf = new Runnable() { + public void run() { + if (!isKeyStoreLocked()) { + installAskingKeyStoreAccess(); + } else { + toastErrorAndFinish(R.string.cert_not_saved); + } + } + }; + Credentials.getInstance().unlock(this); + } + + private void nameCredential() { + if (!mCredentials.readyForSystemInstall()) { + toastErrorAndFinish(R.string.no_cert_to_saved); + } else { + showDialog(NAME_CREDENTIAL_DIALOG); + } + } + + private void saveKeyPair() { + byte[] privatekey = mCredentials.getData(Credentials.PRIVATE_KEY); + String key = toMd5(mCredentials.getData(Credentials.PUBLIC_KEY)); + Map<String, byte[]> map = getPkeyMap(); + map.put(key, privatekey); + setPkeyMap(map); + Log.d(TAG, "privatekey key: " + key + " --> #keys:" + map.size()); + } + + private void setPkeyMap(Map<String, byte[]> map) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream os = new ObjectOutputStream(baos); + os.writeObject(map); + os.close(); + if (!mKeyStore.put(PKEY_MAP_KEY, baos.toByteArray())) { + Log.w(TAG, "setPkeyMap(): failed to write pkey map"); + } + } catch (Exception e) { + // if anything wrong, we lost the private key + Log.w(TAG, "setPkeyMap(): " + e); + } + } + + private Map<String, byte[]> getPkeyMap() { + byte[] bb = mKeyStore.get(PKEY_MAP_KEY); + if (bb != null) { + try { + ObjectInputStream is = + new ObjectInputStream(new ByteArrayInputStream(bb)); + Map<String, byte[]> map = (Map<String, byte[]>) is.readObject(); + if (map != null) return map; + } catch (Exception e) { + // if anything wrong, create a new map + Log.w(TAG, "getPkeyMap(): " + e); + } + } + + return new MyMap(); + } public void onCancel(DialogInterface dialog) { + mDialogResult = CANCELLED; + } + + private Dialog createPkcs12PasswordDialog() { + mView = View.inflate(this, R.layout.password_dialog, null); + mDialogResult = REOPEN; + + DialogInterface.OnClickListener onClickHandler = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_NEGATIVE) { + onCancel(dialog); + return; + } + + String passwd = getViewText(R.id.credential_password); + + hideError(); + if (TextUtils.isEmpty(passwd)) { + showError(R.string.password_empty_error); + } else { + mDialogResult = DONE; + } + } + }; + + DialogInterface.OnDismissListener onDismissHandler = + new DialogInterface.OnDismissListener() { + public void onDismiss(DialogInterface dialog) { + Log.d(TAG, "onDismiss() called"); + // Original code without progress bar: + //if (mDialogResult == DONE) { + // String passwd = getViewText(R.id.credential_password); + // if (!mCredentials.extractFromPkcs12(passwd)) { + // mDialogResult = REOPEN; + // showError(R.string.password_error); + // } + //} + //enterPasswdDialogBottomHalf(); + + // show progress bar and extract in background thread + if (mDialogResult == DONE) { + showDialog(PROGRESS_BAR_DIALOG); + + final String passwd = getViewText(R.id.credential_password); + new Thread(new Runnable() { + public void run() { + if (!mCredentials.extractFromPkcs12(passwd)) { + mDialogResult = REOPEN; + showError(R.string.password_error); + } + removeDialog(PROGRESS_BAR_DIALOG); + runOnUiThread(new Runnable() { + public void run() { + enterPasswdDialogBottomHalf(); + } + }); + } + }).start(); + } else { + enterPasswdDialogBottomHalf(); + } + } + }; + + String title = (mCertFile == null) + ? getString(R.string.pkcs12_password_dialog_title) + : getString(R.string.pkcs12_file_password_dialog_title, + mCertFile.getName()); + Dialog d = new AlertDialog.Builder(this) + .setView(mView) + .setTitle(title) + .setPositiveButton(android.R.string.ok, onClickHandler) + .setNegativeButton(android.R.string.cancel, onClickHandler) + .setOnCancelListener(this) + .create(); + d.setOnDismissListener(onDismissHandler); + return d; + } + + private void enterPasswdDialogBottomHalf() { + if (mDialogResult == DONE) { + nameCredential(); + } else if (mDialogResult == REOPEN) { + showDialog(PKCS12_PASSWORD_DIALOG); + return; + } else { + toastErrorAndFinish(R.string.cert_not_saved); + } + removeDialog(PKCS12_PASSWORD_DIALOG); + } + + private Dialog createNameCredentialDialog() { + mView = View.inflate(this, R.layout.name_credential_dialog, null); + mDialogResult = REOPEN; + + setViewText(R.id.credential_name_title, R.string.credential_name); + setViewText(R.id.credential_info_title, R.string.credential_info); + setViewText(R.id.credential_info, + mCredentials.getDescription().toString()); + + DialogInterface.OnClickListener onClickHandler = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_NEGATIVE) { + onCancel(dialog); + return; + } + + hideError(); + + String name = getViewText(R.id.credential_name); + if (TextUtils.isEmpty(name)) { + showError(R.string.name_empty_error); + } else { + mCredentials.setName(name); + mDialogResult = DONE; + } + } + }; + + DialogInterface.OnDismissListener onDismissHandler = + new DialogInterface.OnDismissListener() { + public void onDismiss(DialogInterface dialog) { + if (mDialogResult == DONE) { + // install everything to system keystore + try { + startActivityForResult( + mCredentials.createSystemInstallIntent(), + REQUEST_SYSTEM_INSTALL_CODE); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "systemInstall(): " + e); + toastErrorAndFinish(R.string.cert_not_saved); + } + } else if (mDialogResult == REOPEN) { + showDialog(NAME_CREDENTIAL_DIALOG); + return; + } else { + toastErrorAndFinish(R.string.cert_not_saved); + } + removeDialog(NAME_CREDENTIAL_DIALOG); + } + }; + + Dialog d = new AlertDialog.Builder(this) + .setView(mView) + .setTitle(R.string.name_credential_dialog_title) + .setPositiveButton(android.R.string.ok, onClickHandler) + .setNegativeButton(android.R.string.cancel, onClickHandler) + .setOnCancelListener(this) + .create(); + d.setOnDismissListener(onDismissHandler); + return d; + } + + private void setAllFilesEnabled(boolean enabled) { + PreferenceScreen root = getPreferenceScreen(); + for (int i = 0, n = root.getPreferenceCount(); i < n; i++) { + root.getPreference(i).setEnabled(enabled); + } + } + + public boolean onPreferenceClick(Preference p) { + File f = new File(Environment.getExternalStorageDirectory(), + p.getTitle().toString()); + if (f.isDirectory()) { + Log.w(TAG, "impossible to pick a directory! " + f); + } else { + setAllFilesEnabled(false); + installFromSdCard(f); + } + return true; + } + + private synchronized void createFileList() { + try { + getPreferenceScreen().removeAll(); + File dir = Environment.getExternalStorageDirectory(); + createPreferencesFor(new File(dir, DOWNLOAD)); + createPreferencesFor(dir); + } catch (IOException e) { + // should not occur + Log.w(TAG, "createFileList(): " + e); + throw new RuntimeException(e); + } + } + + private void createPreferencesFor(File dir) throws IOException { + if ((dir == null) || !dir.isDirectory()) return; + + PreferenceScreen root = getPreferenceScreen(); + int prefixEnd = Environment.getExternalStorageDirectory() + .getCanonicalPath().length() + 1; + for (File f : dir.listFiles(this)) { + Preference p = new Preference(this); + p.setTitle(f.getCanonicalPath().substring(prefixEnd)); + root.addPreference(p); + p.setOnPreferenceClickListener(this); + } + } + + public boolean accept(File f) { + if (!f.isDirectory()) { + return f.getPath().endsWith(".p12"); + } else { + return false; + } + } + + private void toastErrorAndFinish(int msgId) { + if (msgId == R.string.cert_not_saved) { + toastErrorAndFinish(msgId, Toast.LENGTH_SHORT); + } else { + toastErrorAndFinish(msgId, Toast.LENGTH_LONG); + } + } + + private void toastErrorAndFinish(int msgId, int duration) { + toastErrorAndFinish(getString(msgId), duration); + } + + private void toastErrorAndFinish(String msg, int duration) { + Toast.makeText(this, msg, duration).show(); + + if (mIsBrowsingSdcard) { + setAllFilesEnabled(true); + } else { + finish(); + } + } + + private void installFromSdCard(File f) { + Log.d(TAG, "install cert from " + f); + + mCertFile = f; + if (f.exists()) { + long length = f.length(); + if (length < 1000000) { + byte[] data = readCert(f); + if (data == null) { + toastErrorAndFinish(R.string.cert_read_error); + return; + } + mCredentials.putData(Credentials.PKCS12, data); + showDialog(PKCS12_PASSWORD_DIALOG); + } else { + Log.w(TAG, "cert file is too large: " + length); + toastErrorAndFinish(R.string.cert_too_large_error); + } + } else { + Log.w(TAG, "cert file does not exist"); + toastErrorAndFinish(R.string.cert_missing_error); + } + } + + private byte[] readCert(File f) { + try { + byte[] data = new byte[(int) f.length()]; + FileInputStream fis = new FileInputStream(f); + fis.read(data); + fis.close(); + return data; + } catch (Exception e) { + Log.w(TAG, "cert file read error: " + e); + return null; + } + } + + private void deleteCert(File f) { + if ((f != null) && !f.delete()) { + Log.w(TAG, "cannot delete cert: " + f); + } + } + + private boolean isKeyStoreLocked() { + return (mKeyStore.test() != KeyStore.NO_ERROR); + } + + private TextView showError(int messageId) { + TextView v = (TextView) mView.findViewById(R.id.error); + v.setText(messageId); + if (v != null) v.setVisibility(View.VISIBLE); + return v; + } + + private void hide(int viewId) { + View v = mView.findViewById(viewId); + if (v != null) v.setVisibility(View.GONE); + } + + private void hideError() { + hide(R.id.error); + } + + private String getViewText(int viewId) { + return ((TextView) mView.findViewById(viewId)).getText().toString(); + } + + private void setViewText(int viewId, String text) { + TextView v = (TextView) mView.findViewById(viewId); + if (v != null) v.setText(text); + } + + private void setViewText(int viewId, int textId) { + TextView v = (TextView) mView.findViewById(viewId); + if (v != null) v.setText(textId); + } + + private void startSdcardMonitor() { + mSdcardMonitor = new SdcardMonitor(); + mSdcardMonitor.startWatching(); + } + + private void stopSdcardMonitor() { + if (mSdcardMonitor != null) mSdcardMonitor.stopWatching(); + } + + private static String toMd5(byte[] bb) { + try { + MessageDigest algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(bb); + return toHexString(algorithm.digest(), ""); + } catch(NoSuchAlgorithmException e){ + // should not occur + Log.w(TAG, "toMd5(): " + e); + throw new RuntimeException(e); + } } - public void onClick(DialogInterface dialog, int which) { + private static String toHexString(byte[] bb, String separator) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bb) { + hexString.append(Integer.toHexString(0xFF & b)).append(separator); + } + return hexString.toString(); } - public void onDismiss(DialogInterface dialog) { + private class CredentialHelper { + private Bundle mBundle; + private String mName; + + private PrivateKey mUserKey; + private X509Certificate mUserCert; + private List<X509Certificate> mCaCerts = + new ArrayList<X509Certificate>(); + + CredentialHelper(Intent intent) { + mBundle = intent.getExtras(); + if (mBundle == null) { + mBundle = new Bundle(); + return; + } + + // debug + Log.d(TAG, "# extras: " + mBundle.size()); + for (String key : mBundle.keySet()) { + Log.d(TAG, " " + key + ": " + mBundle.getByteArray(key)); + } + parseCert(getData(Credentials.CERTIFICATE)); + } + + X509Certificate getUserCertificate() { + return mUserCert; + } + + PrivateKey getUserKey() { + return mUserKey; + } + + private void parseCert(byte[] bb) { + if (bb == null) return; + try { + CertificateFactory crtFac = CertificateFactory.getInstance("X.509"); + X509Certificate crt = (X509Certificate) + crtFac.generateCertificate(new ByteArrayInputStream(bb)); + if (isCa(crt)) { + Log.d(TAG, "got a CA cert"); + mCaCerts.add(crt); + } else { + Log.d(TAG, "got a user cert"); + mUserCert = crt; + } + } catch (Exception e) { + Log.w(TAG, "parseCert(): " + e); + toastErrorAndFinish( + getString(R.string.intent_parse_error, e.toString()), + Toast.LENGTH_LONG); + } + } + + private boolean isCa(X509Certificate crt) { + try { + // TODO: add a test about this + byte[] bc = crt.getExtensionValue("2.5.29.19"); + Object o = new ASN1InputStream(bc).readObject(); + byte[] bc2 = ((DEROctetString) o).getOctets(); + Object o2 = new ASN1InputStream(bc2).readObject(); + return new BasicConstraints((ASN1Sequence) o2).isCA(); + } catch (Exception e) { + return false; + } + } + + boolean isPkcs12KeyStore() { + return mBundle.containsKey(Credentials.PKCS12); + } + + boolean isKeyPair() { + return mBundle.containsKey(Credentials.PUBLIC_KEY) + && mBundle.containsKey(Credentials.PRIVATE_KEY); + } + + boolean readyForSystemInstall() { + return ((mUserKey != null) || (mUserCert != null) + || !mCaCerts.isEmpty()); + } + + private 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); + } + } + + // notes: only this method checks if mBundle is null + boolean containsAny() { + if (mBundle == null) return false; + return !mBundle.isEmpty(); + } + + byte[] getData(String key) { + return mBundle.getByteArray(key); + } + + void putData(String key, byte[] data) { + mBundle.putByteArray(key, data); + } + + CharSequence getDescription() { + // TODO: create more descriptive string + StringBuilder sb = new StringBuilder(); + String nl = "<br>"; + if (mUserKey != null) { + sb.append(getString(R.string.one_userkey)).append(nl); + } + if (mUserCert != null) { + sb.append(getString(R.string.one_usercrt)).append(nl); + } + int n = mCaCerts.size(); + if (n > 0) { + if (n == 1) { + sb.append(getString(R.string.one_cacrt)); + } else { + sb.append(getString(R.string.n_cacrts, n)); + } + } + return Html.fromHtml(sb.toString()); + } + + void setName(String name) { + mName = name; + } + + String getName() { + return mName; + } + + Intent createSystemInstallIntent() { + Intent i = new Intent(Credentials.SYSTEM_INSTALL_ACTION); + if (mUserKey != null) { + i.putExtra(Credentials.USER_PRIVATE_KEY + mName, + convertToPem(mUserKey)); + } + if (mUserCert != null) { + i.putExtra(Credentials.USER_CERTIFICATE + mName, + convertToPem(mUserCert)); + } + if (!mCaCerts.isEmpty()) { + Object[] cacrts = (Object[]) + mCaCerts.toArray(new X509Certificate[mCaCerts.size()]); + i.putExtra(Credentials.CA_CERTIFICATE + mName, + convertToPem(cacrts)); + } + return i; + } + + boolean extractFromPkcs12(String passwd) { + try { + return extractFromPkcs12Internal(passwd); + } catch (Exception e) { + Log.w(TAG, "extractFromPkcs12(): " + e); + return false; + } + } + + private boolean extractFromPkcs12Internal(String passwd) + throws Exception { + // TODO: add test about this + java.security.KeyStore keystore = + java.security.KeyStore.getInstance("PKCS12"); + PasswordProtection pp = + new PasswordProtection(passwd.toCharArray()); + keystore.load(new ByteArrayInputStream(getData(Credentials.PKCS12)), + pp.getPassword()); + + Enumeration<String> aliases = keystore.aliases(); + if (!aliases.hasMoreElements()) return false; + + String alias = aliases.nextElement(); + Log.d(TAG, "extracted alias = " + alias); + PrivateKeyEntry entry = + (PrivateKeyEntry) keystore.getEntry(alias, pp); + mUserKey = entry.getPrivateKey(); + mUserCert = (X509Certificate) entry.getCertificate(); + + Certificate[] crts = entry.getCertificateChain(); + List<X509Certificate> caCerts = mCaCerts = + new ArrayList<X509Certificate>(crts.length); + for (Certificate c : crts) { + X509Certificate crt = (X509Certificate) c; + if (isCa(crt)) caCerts.add(crt); + } + Log.d(TAG, "caCerts.length = " + mCaCerts.size()); + return true; + } + + private byte[] convertToPem(Object... oo) { + try { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(bao); + PEMWriter pw = new PEMWriter(osw); + for (Object o : oo) pw.writeObject(o); + pw.close(); + return bao.toByteArray(); + } catch (IOException e) { + // should not occur + Log.w(TAG, "convertToPem(): " + e); + throw new RuntimeException(e); + } + } + } + + private static class MyMap extends LinkedHashMap<String, byte[]> + implements Serializable { + private static final long serialVersionUID = 1L; + + protected boolean removeEldestEntry(Map.Entry eldest) { + return (size() > 3); + } + } + + private class SdcardMonitor { + FileObserver mRootMonitor; + FileObserver mDownloadMonitor; + + SdcardMonitor() { + File root = Environment.getExternalStorageDirectory(); + mRootMonitor = new FileObserver(root.getPath()) { + @Override + public void onEvent(int evt, String path) { + commonHandler(evt, path); + } + }; + + File download = new File(root, DOWNLOAD); + mDownloadMonitor = new FileObserver(download.getPath()) { + @Override + public void onEvent(int evt, String path) { + commonHandler(evt, path); + } + }; + } + + private void commonHandler(int evt, String path) { + switch (evt) { + case FileObserver.CREATE: + case FileObserver.DELETE: + if (path.endsWith(".p12")) createFileList(); + break; + } + }; + + void startWatching() { + mRootMonitor.startWatching(); + mDownloadMonitor.startWatching(); + } + + void stopWatching() { + mRootMonitor.stopWatching(); + mDownloadMonitor.stopWatching(); + } } } |