summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2011-04-11 09:05:06 -0700
committerBrian Carlstrom <bdc@google.com>2011-04-22 17:15:31 -0700
commit3e6251dedc92654476c70bdc413f24a4b31ce6a4 (patch)
treeef8d4e42ebb95ab0e877a880f28d53e01a1a5d20 /tests
parent93eceb0295188d01d295da4687b09151a82c006e (diff)
downloadKeyChain-3e6251dedc92654476c70bdc413f24a4b31ce6a4.tar.gz
Adding KeyChainService and KeyChainActivity
Change-Id: I6c862d3e687cf80fb882966adb3245f2244244fe
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk22
-rw-r--r--tests/AndroidManifest.xml48
-rw-r--r--tests/src/com/android/keychain/tests/KeyChainServiceTest.java288
-rw-r--r--tests/src/com/android/keychain/tests/KeyChainTestActivity.java338
4 files changed, 696 insertions, 0 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..2d9afad
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,22 @@
+# Copyright 2011, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := KeyChainTests
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.keychain.tests.support core-tests
+include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..36b91d6
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.keychain.tests">
+
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+ <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+
+ <!--
+ To run service:
+ adb shell am startservice -n com.android.keychain.tests/.KeyChainServiceTest
+
+ To run activity:
+ adb shell am start -n com.android.keychain.tests/com.android.keychain.tests.KeyChainTestActivity
+ -->
+ <application>
+ <service android:name="com.android.keychain.tests.KeyChainServiceTest">
+ <intent-filter>
+ <action android:name="com.android.keychain.tests.KeyChainServiceTest"/>
+ </intent-filter>
+ </service>
+ <activity android:name="com.android.keychain.tests.KeyChainTestActivity">
+ <intent-filter>
+ <action android:name="com.android.keychain.tests.KeyChainTestActivity"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.keychain.tests.KeyChainSocketTestActivity">
+ <intent-filter>
+ <action android:name="com.android.keychain.tests.KeyChainSocketTestActivity"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/src/com/android/keychain/tests/KeyChainServiceTest.java b/tests/src/com/android/keychain/tests/KeyChainServiceTest.java
new file mode 100644
index 0000000..75883b2
--- /dev/null
+++ b/tests/src/com/android/keychain/tests/KeyChainServiceTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keychain.tests;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.security.Credentials;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.security.KeyStore;
+import android.util.Log;
+import com.android.keychain.tests.support.IKeyChainServiceTestSupport;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import junit.framework.Assert;
+import libcore.java.security.TestKeyStore;
+
+public class KeyChainServiceTest extends Service {
+
+ private static final String TAG = "KeyChainServiceTest";
+
+ private final Object mSupportLock = new Object();
+ private IKeyChainServiceTestSupport mSupport;
+ private boolean mIsBoundSupport;
+
+ private final Object mServiceLock = new Object();
+ private IKeyChainService mService;
+ private boolean mIsBoundService;
+
+ private ServiceConnection mSupportConnection = new ServiceConnection() {
+ @Override public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mSupportLock) {
+ mSupport = IKeyChainServiceTestSupport.Stub.asInterface(service);
+ mSupportLock.notifyAll();
+ }
+ }
+
+ @Override public void onServiceDisconnected(ComponentName name) {
+ synchronized (mSupportLock) {
+ mSupport = null;
+ }
+ }
+ };
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mServiceLock) {
+ mService = IKeyChainService.Stub.asInterface(service);
+ mServiceLock.notifyAll();
+ }
+ }
+
+ @Override public void onServiceDisconnected(ComponentName name) {
+ synchronized (mServiceLock) {
+ mService = null;
+ }
+ }
+ };
+
+ private void bindSupport() {
+ mIsBoundSupport = bindService(new Intent(IKeyChainServiceTestSupport.class.getName()),
+ mSupportConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ private void bindService() {
+ mIsBoundService = bindService(new Intent(IKeyChainService.class.getName()),
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ private void unbindServices() {
+ if (mIsBoundSupport) {
+ unbindService(mSupportConnection);
+ mIsBoundSupport = false;
+ }
+ if (mIsBoundService) {
+ unbindService(mServiceConnection);
+ mIsBoundService = false;
+ }
+ }
+
+ @Override public IBinder onBind(Intent intent) {
+ Log.d(TAG, "onBind");
+ return null;
+ }
+
+ @Override public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "onStartCommand");
+ new Thread(new Test(), TAG).start();
+ return START_STICKY;
+ }
+
+ @Override public void onDestroy () {
+ Log.d(TAG, "onDestroy");
+ unbindServices();
+ }
+
+ private final class Test extends Assert implements Runnable {
+
+ @Override public void run() {
+ try {
+ test_KeyChainService();
+ } catch (RuntimeException e) {
+ // rethrow RuntimeException without wrapping
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ stopSelf();
+ }
+ }
+
+ public void test_KeyChainService() throws Exception {
+ Log.d(TAG, "test_KeyChainService uid=" + getApplicationInfo().uid);
+
+ Log.d(TAG, "test_KeyChainService bind support");
+ bindSupport();
+ assertTrue(mIsBoundSupport);
+ synchronized (mSupportLock) {
+ if (mSupport == null) {
+ mSupportLock.wait(10 * 1000);
+ }
+ }
+ assertNotNull(mSupport);
+
+ Log.d(TAG, "test_KeyChainService setup keystore and AccountManager");
+ KeyStore keyStore = KeyStore.getInstance();
+ assertTrue(mSupport.keystoreReset());
+ assertTrue(mSupport.keystorePassword("ignored", "newpasswd"));
+
+ String intermediate = "-intermediate";
+ String root = "-root";
+
+ String alias1 = "client";
+ String alias1Intermediate = alias1 + intermediate;
+ String alias1Root = alias1 + root;
+ byte[] alias1Pkey = (Credentials.USER_PRIVATE_KEY + alias1).getBytes();
+ byte[] alias1Cert = (Credentials.USER_CERTIFICATE + alias1).getBytes();
+ byte[] alias1ICert = (Credentials.CA_CERTIFICATE + alias1Intermediate).getBytes();
+ byte[] alias1RCert = (Credentials.CA_CERTIFICATE + alias1Root).getBytes();
+ PrivateKeyEntry pke1 = TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
+ Certificate intermediate1 = pke1.getCertificateChain()[1];
+ Certificate root1 = TestKeyStore.getClientCertificate().getRootCertificate("RSA");
+
+ final String alias2 = "server";
+ String alias2Intermediate = alias2 + intermediate;
+ String alias2Root = alias2 + root;
+ byte[] alias2Pkey = (Credentials.USER_PRIVATE_KEY + alias2).getBytes();
+ byte[] alias2Cert = (Credentials.USER_CERTIFICATE + alias2).getBytes();
+ byte[] alias2ICert = (Credentials.CA_CERTIFICATE + alias2Intermediate).getBytes();
+ byte[] alias2RCert = (Credentials.CA_CERTIFICATE + alias2Root).getBytes();
+ PrivateKeyEntry pke2 = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+ Certificate intermediate2 = pke2.getCertificateChain()[1];
+ Certificate root2 = TestKeyStore.getServer().getRootCertificate("RSA");
+
+ assertTrue(mSupport.keystorePut(alias1Pkey, pke1.getPrivateKey().getEncoded()));
+ assertTrue(mSupport.keystorePut(alias1Cert, pke1.getCertificate().getEncoded()));
+ assertTrue(mSupport.keystorePut(alias1ICert, intermediate1.getEncoded()));
+ assertTrue(mSupport.keystorePut(alias1RCert, root1.getEncoded()));
+ assertTrue(mSupport.keystorePut(alias2Pkey, pke2.getPrivateKey().getEncoded()));
+ assertTrue(mSupport.keystorePut(alias2Cert, pke2.getCertificate().getEncoded()));
+ assertTrue(mSupport.keystorePut(alias2ICert, intermediate2.getEncoded()));
+ assertTrue(mSupport.keystorePut(alias2RCert, root2.getEncoded()));
+
+ assertEquals(KeyStore.NO_ERROR, keyStore.test());
+ AccountManager accountManager = AccountManager.get(KeyChainServiceTest.this);
+ assertNotNull(accountManager);
+ for (Account account : accountManager.getAccountsByType(KeyChain.ACCOUNT_TYPE)) {
+ mSupport.revokeAppPermission(account, alias1, getApplicationInfo().uid);
+ mSupport.revokeAppPermission(
+ account, alias1Intermediate + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
+ mSupport.revokeAppPermission(
+ account, alias1Root + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
+ mSupport.revokeAppPermission(account, alias2, getApplicationInfo().uid);
+ mSupport.revokeAppPermission(
+ account, alias2Intermediate + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
+ mSupport.revokeAppPermission(
+ account, alias2Root + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
+ }
+
+ Log.d(TAG, "test_KeyChainService bind service");
+ bindService();
+ assertTrue(mIsBoundService);
+ synchronized (mServiceLock) {
+ if (mService == null) {
+ mServiceLock.wait(10 * 1000);
+ }
+ }
+ assertNotNull(mService);
+
+ Account[] accounts = accountManager.getAccountsByType(KeyChain.ACCOUNT_TYPE);
+ assertNotNull(accounts);
+ assertEquals(1, accounts.length);
+ Account account = accounts[0];
+ Log.d(TAG, "test_KeyChainService getAuthTokenByFeatures for Intent");
+ AccountManagerFuture<Bundle> accountManagerFutureFail
+ = accountManager.getAuthToken(account, alias1, false, null, null);
+ Bundle bundleFail = accountManagerFutureFail.getResult();
+ assertNotNull(bundleFail);
+ Object intentObject = bundleFail.get(AccountManager.KEY_INTENT);
+ assertNotNull(intentObject);
+ assertTrue(Intent.class.isAssignableFrom(intentObject.getClass()));
+ Intent intent = (Intent) intentObject;
+ assertEquals("android",
+ intent.getComponent().getPackageName());
+ assertEquals("android.accounts.GrantCredentialsPermissionActivity",
+ intent.getComponent().getClassName());
+
+ mSupport.grantAppPermission(account, alias1, getApplicationInfo().uid);
+ // don't grant alias2, so it can be done manually with KeyChainTestActivity
+ Log.d(TAG, "test_KeyChainService getAuthTokenByFeatures for authtoken");
+ AccountManagerFuture<Bundle> accountManagerFuture
+ = accountManager.getAuthToken(account, alias1, false, null, null);
+ Bundle bundle = accountManagerFuture.getResult();
+ String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
+ assertNotNull(accountName);
+ String accountType = bundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
+ assertEquals(KeyChain.ACCOUNT_TYPE, accountType);
+ String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+ assertNotNull(authToken);
+ assertFalse(authToken.isEmpty());
+
+ byte[] privateKey = mService.getPrivate(alias1, authToken);
+ assertNotNull(privateKey);
+ assertEquals(Arrays.toString(pke1.getPrivateKey().getEncoded()),
+ Arrays.toString(privateKey));
+
+ byte[] certificate = mService.getCertificate(alias1, authToken);
+ assertNotNull(certificate);
+ assertEquals(Arrays.toString(pke1.getCertificate().getEncoded()),
+ Arrays.toString(certificate));
+
+ String aliasI = mService.findIssuer(KeyChain.fromCertificate(pke1.getCertificate()));
+ assertNotNull(aliasI);
+ assertEquals(alias1Intermediate, aliasI);
+
+ String aliasR = mService.findIssuer(KeyChain.fromCertificate(intermediate1));
+ assertNotNull(aliasR);
+ assertEquals(alias1Root, aliasR);
+
+ String aliasRR = mService.findIssuer(KeyChain.fromCertificate(intermediate1));
+ assertNotNull(aliasRR);
+ assertEquals(alias1Root, aliasRR);
+
+ try {
+ mService.findIssuer(new Bundle());
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ mService.findIssuer(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ Log.d(TAG, "test_KeyChainService unbind");
+ unbindServices();
+ assertFalse(mIsBoundSupport);
+ assertFalse(mIsBoundService);
+
+ Log.d(TAG, "test_KeyChainService end");
+ }
+ }
+}
diff --git a/tests/src/com/android/keychain/tests/KeyChainTestActivity.java b/tests/src/com/android/keychain/tests/KeyChainTestActivity.java
new file mode 100644
index 0000000..b7610e3
--- /dev/null
+++ b/tests/src/com/android/keychain/tests/KeyChainTestActivity.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keychain.tests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.security.KeyChain;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.widget.TextView;
+import java.net.Socket;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509TrustManager;
+import libcore.java.security.TestKeyStore;
+import libcore.javax.net.ssl.TestSSLContext;
+import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters;
+import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+
+/**
+ * Simple activity based test that exercises the KeyChain API
+ */
+public class KeyChainTestActivity extends Activity {
+
+ private static final String TAG = "KeyChainTestActivity";
+
+ private static final int REQUEST_ALIAS = 1;
+ private static final int REQUEST_GRANT = 2;
+
+ private TextView mTextView;
+
+ private KeyChain mKeyChain;
+
+ private final Object mAliasLock = new Object();
+ private String mAlias;
+
+ private final Object mGrantedLock = new Object();
+ private boolean mGranted;
+
+ private void log(final String message) {
+ Log.d(TAG, message);
+ runOnUiThread(new Runnable() {
+ @Override public void run() {
+ mTextView.append(message + "\n");
+ }
+ });
+ }
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mTextView = new TextView(this);
+ mTextView.setMovementMethod(new ScrollingMovementMethod());
+ setContentView(mTextView);
+
+ log("Starting test...");
+
+ try {
+ KeyChain.getInstance(this);
+ throw new AssertionError();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalStateException expected) {
+ log("KeyChain failed as expected on main thread.");
+ }
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ try {
+ mKeyChain = KeyChain.getInstance(KeyChainTestActivity.this);
+ log("Starting web server...");
+ URL url = startWebServer();
+ log("Making https request to " + url);
+ makeHttpsRequest(url);
+ log("Tests succeeded.");
+
+ return null;
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ private URL startWebServer() throws Exception {
+ KeyStore serverKeyStore = TestKeyStore.getServer().keyStore;
+ char[] serverKeyStorePassword = TestKeyStore.getServer().storePassword;
+ String kmfAlgoritm = KeyManagerFactory.getDefaultAlgorithm();
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgoritm);
+ kmf.init(serverKeyStore, serverKeyStorePassword);
+ SSLContext serverContext = SSLContext.getInstance("SSL");
+ serverContext.init(kmf.getKeyManagers(),
+ new TrustManager[] { new TrustAllTrustManager() },
+ null);
+ SSLSocketFactory sf = serverContext.getSocketFactory();
+ SSLSocketFactory needClientAuth = TestSSLContext.clientAuth(sf, false, true);
+ MockWebServer server = new MockWebServer();
+ server.useHttps(needClientAuth, false);
+ server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
+ server.play();
+ return server.getUrl("/");
+ }
+ private void makeHttpsRequest(URL url) throws Exception {
+ SSLContext clientContext = SSLContext.getInstance("SSL");
+ clientContext.init(new KeyManager[] { new KeyChainKeyManager() },
+ new TrustManager[] { new KeyChainTrustManager() },
+ null);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(clientContext.getSocketFactory());
+ if (connection.getResponseCode() != 200) {
+ throw new AssertionError();
+ }
+ }
+ }.execute();
+ }
+
+ /**
+ * Called when the user did not have access to requested
+ * alias. Ask the user for permission and wait for a result.
+ */
+ private void waitForGrant(Intent intent) {
+ mGranted = false;
+ log("Grant intent=" + intent);
+ startActivityForResult(intent, REQUEST_GRANT);
+ synchronized (mGrantedLock) {
+ while (!mGranted) {
+ try {
+ mGrantedLock.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+
+ private class KeyChainKeyManager extends X509ExtendedKeyManager {
+ @Override public String chooseClientAlias(String[] keyTypes,
+ Principal[] issuers,
+ Socket socket) {
+ log("KeyChainKeyManager chooseClientAlias...");
+
+ Intent intent = KeyChain.chooseAlias();
+ startActivityForResult(intent, REQUEST_ALIAS);
+ log("Starting chooser...");
+ String alias;
+ synchronized (mAliasLock) {
+ while (mAlias == null) {
+ try {
+ mAliasLock.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ alias = mAlias;
+ }
+ return alias;
+ }
+ @Override public String chooseServerAlias(String keyType,
+ Principal[] issuers,
+ Socket socket) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+ @Override public X509Certificate[] getCertificateChain(String alias) {
+ log("KeyChainKeyManager getCertificateChain...");
+ Bundle cert = mKeyChain.getCertificate(alias);
+ Intent intent = cert.getParcelable(KeyChain.KEY_INTENT);
+ if (intent != null) {
+ waitForGrant(intent);
+ cert = mKeyChain.getCertificate(alias);
+ }
+ X509Certificate certificate = KeyChain.toCertificate(cert);
+ log("certificate=" + certificate);
+ return new X509Certificate[] { certificate };
+ }
+ @Override public String[] getClientAliases(String keyType, Principal[] issuers) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+ @Override public String[] getServerAliases(String keyType, Principal[] issuers) {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+ @Override public PrivateKey getPrivateKey(String alias) {
+ log("KeyChainKeyManager getPrivateKey...");
+ Bundle pkey = mKeyChain.getPrivate(alias);
+ Intent intent = pkey.getParcelable(KeyChain.KEY_INTENT);
+ if (intent != null) {
+ waitForGrant(intent);
+ pkey = mKeyChain.getPrivate(alias);
+ }
+ PrivateKey privateKey = KeyChain.toPrivateKey(pkey);
+ log("privateKey=" + privateKey);
+ return privateKey;
+ }
+ }
+
+ private class KeyChainTrustManager implements X509TrustManager {
+ private final X509TrustManager trustManager = SSLParametersImpl.getDefaultTrustManager();
+ private final IndexedPKIXParameters index
+ = SSLParametersImpl.getDefaultIndexedPKIXParameters();
+
+ @Override public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ log("KeyChainTrustManager checkServerTrusted...");
+ // start at the end of the chain and make sure we have a trust anchor.
+ // if not, ask KeyChain for one.
+ X509Certificate end = chain[chain.length-1];
+ if (findTrustAnchor(end)) {
+ trustManager.checkServerTrusted(chain, authType);
+ return;
+ }
+
+ // try to extend the chain
+ List<X509Certificate> list = new ArrayList<X509Certificate>(Arrays.asList(chain));
+ do {
+ Bundle ca = mKeyChain.findIssuer(end);
+ if (ca == null) {
+ break;
+ }
+ Intent intent = ca.getParcelable(KeyChain.KEY_INTENT);
+ if (intent != null) {
+ waitForGrant(intent);
+ ca = mKeyChain.findIssuer(end);
+ }
+ end = KeyChain.toCertificate(ca);
+ list.add(end);
+ } while (!findTrustAnchor(end));
+
+ // convert extended chain back to array
+ if (list.size() != chain.length) {
+ chain = list.toArray(new X509Certificate[list.size()]);
+ }
+ trustManager.checkServerTrusted(chain, authType);
+ }
+
+ /**
+ * Returns true if we have found a trust anchor, with or
+ * without error, indicating that we should call the
+ * underlying TrustManager to verify the chain in its current
+ * state. Otherwise, returns false to indicate the chain
+ * should be extended.
+ */
+ private boolean findTrustAnchor(X509Certificate cert) {
+ try {
+ if (index.findTrustAnchor(cert) == null) {
+ return false;
+ }
+ } catch (CertPathValidatorException ignored) {
+ }
+ return true;
+ }
+
+ @Override public X509Certificate[] getAcceptedIssuers() {
+ // not a client SSLSocket callback
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static class TrustAllTrustManager implements X509TrustManager {
+ @Override public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ }
+ @Override public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ }
+ @Override public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+
+ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_ALIAS: {
+ log("onActivityResult REQUEST_ALIAS...");
+ if (resultCode != RESULT_OK) {
+ log("REQUEST_ALIAS failed!");
+ return;
+ }
+ String alias = data.getExtras().getString(Intent.EXTRA_TEXT);
+ log("Alias choosen '" + alias + "'");
+ synchronized (mAliasLock) {
+ mAlias = alias;
+ mAliasLock.notifyAll();
+ }
+ break;
+ }
+ case REQUEST_GRANT: {
+ log("onActivityResult REQUEST_GRANT...");
+ if (resultCode != RESULT_OK) {
+ log("REQUEST_GRANT failed!");
+ return;
+ }
+ synchronized (mGrantedLock) {
+ mGranted = true;
+ mGrantedLock.notifyAll();
+ }
+ break;
+ }
+ default:
+ throw new IllegalStateException("requestCode == " + requestCode);
+ }
+ }
+}