Merge "Pass all offload-exempt prefixes into OffloadController" into oc-dr1-dev
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 0b1569c..b56437e 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -20,7 +20,6 @@
 import android.util.Log;
 
 import com.android.internal.os.RoSystemProperties;
-import com.android.org.conscrypt.Conscrypt;
 import com.android.org.conscrypt.OpenSSLContextImpl;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.SSLClientSessionCache;
@@ -213,7 +212,7 @@
     private SSLSocketFactory makeSocketFactory(
             KeyManager[] keyManagers, TrustManager[] trustManagers) {
         try {
-            OpenSSLContextImpl sslContext =  (OpenSSLContextImpl) Conscrypt.newPreferredSSLContextSpi();
+            OpenSSLContextImpl sslContext = OpenSSLContextImpl.getPreferred();
             sslContext.engineInit(keyManagers, trustManagers, null);
             sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
             return sslContext.engineGetSocketFactory();
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 0122338..3dfd680 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -118,6 +118,18 @@
     }
 
     /**
+     * Show the second layer on top of the first layer immediately
+     *
+     * @hide
+     */
+    public void showSecondLayer() {
+        mAlpha = 255;
+        mReverse = false;
+        mTransitionState = TRANSITION_NONE;
+        invalidateSelf();
+    }
+
+    /**
      * Show only the first layer.
      */
     public void resetTransition() {
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..a740b13
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := KeyStoreTests
+
+include $(BUILD_PACKAGE)
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..415442f
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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="android.security.tests"
+          android:sharedUserId="android.uid.system">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="android.security.tests"
+        android:label="KeyStore Tests">
+    </instrumentation>
+</manifest>
diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
new file mode 100644
index 0000000..bc8dd13
--- /dev/null
+++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2012 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 android.security;
+
+import android.test.AndroidTestCase;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class KeyPairGeneratorSpecTest extends AndroidTestCase {
+    private static final String TEST_ALIAS_1 = "test1";
+
+    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+    private static final long NOW_MILLIS = System.currentTimeMillis();
+
+    private static final BigInteger SERIAL_1 = BigInteger.ONE;
+
+    /* We have to round this off because X509v3 doesn't store milliseconds. */
+    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+    @SuppressWarnings("deprecation")
+    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+    public void testConstructor_Success() throws Exception {
+        KeyPairGeneratorSpec spec =
+                new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1,
+                        SERIAL_1, NOW, NOW_PLUS_10_YEARS, 0);
+
+        assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+        assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+        assertEquals("Key algorithm should be the one specified", "RSA", spec.getKeyType());
+
+        assertEquals("Key size should be the one specified", 1024, spec.getKeySize());
+
+        assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+        assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+        assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+    }
+
+    public void testBuilder_Success() throws Exception {
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setKeyType("RSA")
+                .setKeySize(1024)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .setEncryptionRequired()
+                .build();
+
+        assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+        assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+        assertEquals("Key algorithm should be the one specified", "RSA", spec.getKeyType());
+
+        assertEquals("Key size should be the one specified", 1024, spec.getKeySize());
+
+        assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+        assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+        assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+
+        assertEquals("encryption flag should be on", KeyStore.FLAG_ENCRYPTED, spec.getFlags());
+    }
+
+    public void testConstructor_NullContext_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(null, TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS, 0);
+            fail("Should throw IllegalArgumentException when context is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullKeystoreAlias_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), null, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS, 0);
+            fail("Should throw IllegalArgumentException when keystoreAlias is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullSubjectDN_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS, 0);
+            fail("Should throw IllegalArgumentException when subjectDN is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullSerial_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
+                    NOW_PLUS_10_YEARS, 0);
+            fail("Should throw IllegalArgumentException when startDate is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullStartDate_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+                    null, NOW_PLUS_10_YEARS, 0);
+            fail("Should throw IllegalArgumentException when startDate is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullEndDate_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+                    NOW, null, 0);
+            fail("Should throw IllegalArgumentException when keystoreAlias is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_EndBeforeStart_Failure() throws Exception {
+        try {
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
+                    NOW_PLUS_10_YEARS, NOW, 0);
+            fail("Should throw IllegalArgumentException when end is before start");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+}
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
new file mode 100644
index 0000000..319cf32
--- /dev/null
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) 2009 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 android.security;
+
+import android.app.Activity;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+import android.test.ActivityUnitTestCase;
+import android.test.AssertionFailedError;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.MediumTest;
+import com.android.org.conscrypt.NativeConstants;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.security.spec.RSAKeyGenParameterSpec;
+
+/**
+ * Junit / Instrumentation test case for KeyStore class
+ *
+ * Running the test suite:
+ *
+ *  runtest keystore-unit
+ *
+ * Or this individual test case:
+ *
+ *  runtest --path frameworks/base/keystore/tests/src/android/security/KeyStoreTest.java
+ */
+@MediumTest
+public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
+    private static final String TEST_PASSWD = "12345678";
+    private static final String TEST_PASSWD2 = "87654321";
+    private static final String TEST_KEYNAME = "test-key";
+    private static final String TEST_KEYNAME1 = "test-key.1";
+    private static final String TEST_KEYNAME2 = "test-key\02";
+    private static final byte[] TEST_KEYVALUE = "test value".getBytes(StandardCharsets.UTF_8);
+
+    // "Hello, World" in Chinese
+    private static final String TEST_I18N_KEY = "\u4F60\u597D, \u4E16\u754C";
+    private static final byte[] TEST_I18N_VALUE = TEST_I18N_KEY.getBytes(StandardCharsets.UTF_8);
+
+    // Test vector data for signatures
+    private static final int RSA_KEY_SIZE = 1024;
+    private static final byte[] TEST_DATA =  new byte[RSA_KEY_SIZE / 8];
+    static {
+        for (int i = 0; i < TEST_DATA.length; i++) {
+            TEST_DATA[i] = (byte) i;
+        }
+    }
+
+    private KeyStore mKeyStore = null;
+
+    public KeyStoreTest() {
+        super(Activity.class);
+    }
+
+    private static final byte[] PRIVKEY_BYTES = hexToBytes(
+            "308204BE020100300D06092A864886F70D0101010500048204A8308204A4020100028201" +
+            "0100E0473E8AB8F2284FEB9E742FF9748FA118ED98633C92F52AEB7A2EBE0D3BE60329BE" +
+            "766AD10EB6A515D0D2CFD9BEA7930F0C306537899F7958CD3E85B01F8818524D312584A9" +
+            "4B251E3625B54141EDBFEE198808E1BB97FC7CB49B9EAAAF68E9C98D7D0EDC53BBC0FA00" +
+            "34356D6305FBBCC3C7001405386ABBC873CB0F3EF7425F3D33DF7B315AE036D2A0B66AFD" +
+            "47503B169BF36E3B5162515B715FDA83DEAF2C58AEB9ABFB3097C3CC9DD9DBE5EF296C17" +
+            "6139028E8A671E63056D45F40188D2C4133490845DE52C2534E9C6B2478C07BDAE928823" +
+            "B62D066C7770F9F63F3DBA247F530844747BE7AAA85D853B8BD244ACEC3DE3C89AB46453" +
+            "AB4D24C3AC6902030100010282010037784776A5F17698F5AC960DFB83A1B67564E648BD" +
+            "0597CF8AB8087186F2669C27A9ECBDD480F0197A80D07309E6C6A96F925331E57F8B4AC6" +
+            "F4D45EDA45A23269C09FC428C07A4E6EDF738A15DEC97FABD2F2BB47A14F20EA72FCFE4C" +
+            "36E01ADA77BD137CD8D4DA10BB162E94A4662971F175F985FA188F056CB97EE2816F43AB" +
+            "9D3747612486CDA8C16196C30818A995EC85D38467791267B3BF21F273710A6925862576" +
+            "841C5B6712C12D4BD20A2F3299ADB7C135DA5E9515ABDA76E7CAF2A3BE80551D073B78BF" +
+            "1162C48AD2B7F4743A0238EE4D252F7D5E7E6533CCAE64CCB39360075A2FD1E034EC3AE5" +
+            "CE9C408CCBF0E25E4114021687B3DD4754AE8102818100F541884BC3737B2922D4119EF4" +
+            "5E2DEE2CD4CBB75F45505A157AA5009F99C73A2DF0724AC46024306332EA898177634546" +
+            "5DC6DF1E0A6F140AFF3B7396E6A8994AC5DAA96873472FE37749D14EB3E075E629DBEB35" +
+            "83338A6F3649D0A2654A7A42FD9AB6BFA4AC4D481D390BB229B064BDC311CC1BE1B63189" +
+            "DA7C40CDECF2B102818100EA1A742DDB881CEDB7288C87E38D868DD7A409D15A43F445D5" +
+            "377A0B5731DDBFCA2DAF28A8E13CD5C0AFCEC3347D74A39E235A3CD9633F274DE2B94F92" +
+            "DF43833911D9E9F1CF58F27DE2E08FF45964C720D3EC2139DC7CAFC912953CDECB2F355A" +
+            "2E2C35A50FAD754CB3B23166424BA3B6E3112A2B898C38C5C15EDB238693390281805182" +
+            "8F1EC6FD996029901BAF1D7E337BA5F0AF27E984EAD895ACE62BD7DF4EE45A224089F2CC" +
+            "151AF3CD173FCE0474BCB04F386A2CDCC0E0036BA2419F54579262D47100BE931984A3EF" +
+            "A05BECF141574DC079B3A95C4A83E6C43F3214D6DF32D512DE198085E531E616B83FD7DD" +
+            "9D1F4E2607C3333D07C55D107D1D3893587102818100DB4FB50F50DE8EDB53FF34C80931" +
+            "88A0512867DA2CCA04897759E587C244010DAF8664D59E8083D16C164789301F67A9F078" +
+            "060D834A2ADBD367575B68A8A842C2B02A89B3F31FCCEC8A22FE395795C5C6C7422B4E5D" +
+            "74A1E9A8F30E7759B9FC2D639C1F15673E84E93A5EF1506F4315383C38D45CBD1B14048F" +
+            "4721DC82326102818100D8114593AF415FB612DBF1923710D54D07486205A76A3B431949" +
+            "68C0DFF1F11EF0F61A4A337D5FD3741BBC9640E447B8B6B6C47C3AC1204357D3B0C55BA9" +
+            "286BDA73F629296F5FA9146D8976357D3C751E75148696A40B74685C82CE30902D639D72" +
+            "4FF24D5E2E9407EE34EDED2E3B4DF65AA9BCFEB6DF28D07BA6903F165768");
+
+    private static final byte[] AES256_BYTES = hexToBytes(
+            "0CC175B9C0F1B6A831C399E269772661CEC520EA51EA0A47E87295FA3245A605");
+
+    private static byte[] hexToBytes(String s) {
+        int len = s.length();
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
+                    s.charAt(i + 1), 16));
+        }
+        return data;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mKeyStore = KeyStore.getInstance();
+        if (mKeyStore.state() != KeyStore.State.UNINITIALIZED) {
+            mKeyStore.reset();
+        }
+        assertEquals("KeyStore should be in an uninitialized state",
+                KeyStore.State.UNINITIALIZED, mKeyStore.state());
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mKeyStore.reset();
+        super.tearDown();
+    }
+
+    public void testState() throws Exception {
+        assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
+    }
+
+    public void testPassword() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
+    }
+
+    public void testGet() throws Exception {
+        assertNull(mKeyStore.get(TEST_KEYNAME));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertNull(mKeyStore.get(TEST_KEYNAME));
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+    }
+
+    public void testPut() throws Exception {
+        assertNull(mKeyStore.get(TEST_KEYNAME));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+    }
+
+    public void testPut_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testPut_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
+    public void testI18n() throws Exception {
+        assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_I18N_KEY));
+        mKeyStore.onUserPasswordChanged(TEST_I18N_KEY);
+        assertTrue(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_I18N_KEY));
+    }
+
+    public void testDelete() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+        assertTrue(mKeyStore.delete(TEST_KEYNAME));
+        assertNull(mKeyStore.get(TEST_KEYNAME));
+    }
+
+    public void testDelete_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testDelete_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
+    public void testContains() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testContains_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testContains_grantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
+    public void testList() throws Exception {
+        String[] emptyResult = mKeyStore.list(TEST_KEYNAME);
+        assertNotNull(emptyResult);
+        assertEquals(0, emptyResult.length);
+
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+
+        String[] results = mKeyStore.list(TEST_KEYNAME);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results)));
+    }
+
+    public void testList_ungrantedUid_Bluetooth() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertEquals(0, results1.length);
+
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertEquals(0, results2.length);
+    }
+
+    public void testList_grantedUid_Wifi() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.WIFI_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
+
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.WIFI_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
+    public void testList_grantedUid_Vpn() throws Exception {
+        String[] results1 = mKeyStore.list(TEST_KEYNAME, Process.VPN_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID, KeyStore.FLAG_ENCRYPTED);
+
+        String[] results2 = mKeyStore.list(TEST_KEYNAME, Process.VPN_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
+    public void testLock() throws Exception {
+        assertFalse(mKeyStore.lock());
+
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
+
+        assertTrue(mKeyStore.lock());
+        assertEquals(KeyStore.State.LOCKED, mKeyStore.state());
+    }
+
+    public void testUnlock() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
+        mKeyStore.lock();
+
+        assertFalse(mKeyStore.unlock(TEST_PASSWD2));
+        assertTrue(mKeyStore.unlock(TEST_PASSWD));
+    }
+
+    public void testIsEmpty() throws Exception {
+        assertTrue(mKeyStore.isEmpty());
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        assertTrue(mKeyStore.isEmpty());
+        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+        assertFalse(mKeyStore.isEmpty());
+        mKeyStore.reset();
+        assertTrue(mKeyStore.isEmpty());
+    }
+
+    public void testGenerate_NotInitialized_Fail() throws Exception {
+        assertFalse("Should fail when keystore is not initialized",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+    }
+
+    public void testGenerate_Locked_Fail() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        mKeyStore.lock();
+        assertFalse("Should fail when keystore is locked",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+    }
+
+    public void testGenerate_Success() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key when unlocked",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testGenerate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key when unlocked",
+                mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testGenerate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID,
+                    NativeConstants.EVP_PKEY_RSA, RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testImport_Success() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testImport_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testImport_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertFalse(mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.BLUETOOTH_UID,
+                KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testImport_Failure_BadEncoding() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+
+        assertFalse("Invalid DER-encoded key should not be imported", mKeyStore.importKey(
+                TEST_KEYNAME, TEST_DATA, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testSign_Success() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                    RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
+
+        assertNotNull("Signature should not be null", signature);
+    }
+
+    public void testVerify_Success() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                    RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
+
+        assertNotNull("Signature should not be null", signature);
+
+        assertTrue("Signature should verify with same data",
+                mKeyStore.verify(TEST_KEYNAME, TEST_DATA, signature));
+    }
+
+    public void testSign_NotInitialized_Failure() throws Exception {
+        assertNull("Should not be able to sign without first initializing the keystore",
+                mKeyStore.sign(TEST_KEYNAME, TEST_DATA));
+    }
+
+    public void testSign_NotGenerated_Failure() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+
+        assertNull("Should not be able to sign without first generating keys",
+                mKeyStore.sign(TEST_KEYNAME, TEST_DATA));
+    }
+
+    public void testGrant_Generated_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_Imported_Success() throws Exception {
+        assertTrue("Password should work for keystore", mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key for testcase", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should be able to grant key to other user", mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_NoKey_Failure() throws Exception {
+        assertTrue("Should be able to unlock keystore for test",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertFalse("Should not be able to grant without first initializing the keystore",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_NotInitialized_Failure() throws Exception {
+        assertFalse("Should not be able to grant without first initializing the keystore",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_Generated_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_Imported_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key for testcase", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_NotInitialized_Failure() throws Exception {
+        assertFalse("Should fail to ungrant key when keystore not initialized",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_NoGrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertFalse("Should not be able to revoke not existent grant",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_DoubleUngrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+
+        assertFalse("Should fail to ungrant key to other user second time",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_DoubleGrantUngrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                        RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to grant key to other user a second time",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+
+        assertFalse("Should fail to ungrant key to other user second time",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testDuplicate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                    RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        // source doesn't exist
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, -1, TEST_KEYNAME1, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+
+        // Copy from current UID to granted UID
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+
+        // Copy from granted UID to same granted UID
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+                Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME2, Process.WIFI_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+                Process.WIFI_UID));
+
+        assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME2));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+    }
+
+    public void testDuplicate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
+                    RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.duplicate(TEST_KEYNAME, Process.BLUETOOTH_UID, TEST_KEYNAME2,
+                Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
+    /**
+     * The amount of time to allow before and after expected time for variance
+     * in timing tests.
+     */
+    private static final long SLOP_TIME_MILLIS = 15000L;
+
+    public void testGetmtime_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        long now = System.currentTimeMillis();
+        long actual = mKeyStore.getmtime(TEST_KEYNAME);
+
+        long expectedAfter = now - SLOP_TIME_MILLIS;
+        long expectedBefore = now + SLOP_TIME_MILLIS;
+
+        assertLessThan("Time should be close to current time", expectedBefore, actual);
+        assertGreaterThan("Time should be close to current time", expectedAfter, actual);
+    }
+
+    private static void assertLessThan(String explanation, long expectedBefore, long actual) {
+        if (actual >= expectedBefore) {
+            throw new AssertionFailedError(explanation + ": actual=" + actual
+                    + ", expected before: " + expectedBefore);
+        }
+    }
+
+    private static void assertGreaterThan(String explanation, long expectedAfter, long actual) {
+        if (actual <= expectedAfter) {
+            throw new AssertionFailedError(explanation + ": actual=" + actual
+                    + ", expected after: " + expectedAfter);
+        }
+    }
+
+    public void testGetmtime_NonExist_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.onUserPasswordChanged(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked", mKeyStore.importKey(TEST_KEYNAME,
+                PRIVKEY_BYTES, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertEquals("-1 should be returned for non-existent key",
+                -1L, mKeyStore.getmtime(TEST_KEYNAME2));
+    }
+
+    private KeyCharacteristics generateRsaKey(String name) throws Exception {
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+        args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
+        return outCharacteristics;
+    }
+
+    public void testGenerateKey() throws Exception {
+        generateRsaKey("test");
+        mKeyStore.delete("test");
+    }
+
+    public void testGenerateRsaWithEntropy() throws Exception {
+        byte[] entropy = new byte[] {1,2,3,4,5};
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+        args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics);
+        assertEquals("generateKey should succeed", KeyStore.NO_ERROR, result);
+    }
+
+    public void testGenerateAndDelete() throws Exception {
+        generateRsaKey("test");
+        assertTrue("delete should succeed", mKeyStore.delete("test"));
+    }
+
+    public void testGetKeyCharacteristicsSuccess() throws Exception {
+        mKeyStore.onUserPasswordChanged(TEST_PASSWD);
+        String name = "test";
+        KeyCharacteristics gen = generateRsaKey(name);
+        KeyCharacteristics call = new KeyCharacteristics();
+        int result = mKeyStore.getKeyCharacteristics(name, null, null, call);
+        assertEquals("getKeyCharacteristics should succeed", KeyStore.NO_ERROR, result);
+        mKeyStore.delete("test");
+    }
+
+    public void testAppId() throws Exception {
+        String name = "test";
+        byte[] id = new byte[] {0x01, 0x02, 0x03};
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        args.addBytes(KeymasterDefs.KM_TAG_APPLICATION_ID, id);
+        args.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, RSAKeyGenParameterSpec.F4);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
+        assertEquals("getKeyCharacteristics should fail without application ID",
+                KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
+                mKeyStore.getKeyCharacteristics(name, null, null, outCharacteristics));
+        assertEquals("getKeyCharacteristics should succeed with application ID",
+                KeyStore.NO_ERROR,
+                mKeyStore.getKeyCharacteristics(name, new KeymasterBlob(id), null,
+                    outCharacteristics));
+    }
+
+
+    public void testExportRsa() throws Exception {
+        String name = "test";
+        generateRsaKey(name);
+        ExportResult result = mKeyStore.exportKey(name, KeymasterDefs.KM_KEY_FORMAT_X509, null,
+                null);
+        assertEquals("Export success", KeyStore.NO_ERROR, result.resultCode);
+        // TODO: Verify we have an RSA public key that's well formed.
+    }
+
+    public void testAesGcmEncryptSuccess() throws Exception {
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
+
+        args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 128);
+        OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
+                true, args, null);
+        IBinder token = result.token;
+        assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
+        result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
+        assertEquals("Update should succeed", KeyStore.NO_ERROR, result.resultCode);
+        assertEquals("Finish should succeed", KeyStore.NO_ERROR,
+                mKeyStore.finish(token, null, null).resultCode);
+        // TODO: Assert that an AEAD tag was returned by finish
+    }
+
+    public void testBadToken() throws Exception {
+        IBinder token = new Binder();
+        OperationResult result = mKeyStore.update(token, null, new byte[] {0x01});
+        assertEquals("Update with invalid token should fail",
+                KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, result.resultCode);
+    }
+
+    private int importAesKey(String name, byte[] key, int size, int mode) {
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mode);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, size);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        return mKeyStore.importKey(name, args, KeymasterDefs.KM_KEY_FORMAT_RAW, key, 0,
+                new KeyCharacteristics());
+    }
+    private byte[] doOperation(String name, int purpose, byte[] in, KeymasterArguments beginArgs) {
+        OperationResult result = mKeyStore.begin(name, purpose,
+                true, beginArgs, null);
+        assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
+        IBinder token = result.token;
+        result = mKeyStore.update(token, null, in);
+        assertEquals("Update should succeed", KeyStore.NO_ERROR, result.resultCode);
+        assertEquals("All data should be consumed", in.length, result.inputConsumed);
+        assertEquals("Finish should succeed", KeyStore.NO_ERROR,
+                mKeyStore.finish(token, null, null).resultCode);
+        return result.output;
+    }
+
+    public void testImportAes() throws Exception {
+        int result = importAesKey("aes", AES256_BYTES, 256, KeymasterDefs.KM_MODE_ECB);
+        assertEquals("import should succeed", KeyStore.NO_ERROR, result);
+        mKeyStore.delete("aes");
+    }
+
+    public void testAes256Ecb() throws Exception {
+        byte[] key =
+                hexToBytes("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
+        String name = "aes";
+        assertEquals(KeyStore.NO_ERROR, importAesKey(name, key, 256, KeymasterDefs.KM_MODE_ECB));
+        byte[][] testVectors = new byte[][] {
+            hexToBytes("6bc1bee22e409f96e93d7e117393172a"),
+            hexToBytes("ae2d8a571e03ac9c9eb76fac45af8e51"),
+            hexToBytes("30c81c46a35ce411e5fbc1191a0a52ef"),
+            hexToBytes("f69f2445df4f9b17ad2b417be66c3710")};
+        byte[][] cipherVectors = new byte[][] {
+            hexToBytes("f3eed1bdb5d2a03c064b5a7e3db181f8"),
+            hexToBytes("591ccb10d410ed26dc5ba74a31362870"),
+            hexToBytes("b6ed21b99ca6f4f9f153e7b1beafed1d"),
+            hexToBytes("23304b7a39f9f3ff067d8d8f9e24ecc7")};
+        KeymasterArguments beginArgs = new KeymasterArguments();
+        beginArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        beginArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+        beginArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        for (int i = 0; i < testVectors.length; i++) {
+            byte[] cipherText = doOperation(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, testVectors[i],
+                    beginArgs);
+            MoreAsserts.assertEquals(cipherVectors[i], cipherText);
+        }
+        for (int i = 0; i < testVectors.length; i++) {
+            byte[] plainText = doOperation(name, KeymasterDefs.KM_PURPOSE_DECRYPT,
+                    cipherVectors[i], beginArgs);
+            MoreAsserts.assertEquals(testVectors[i], plainText);
+        }
+    }
+
+    // This is a very implementation specific test and should be thrown out eventually, however it
+    // is nice for now to test that keystore is properly pruning operations.
+    public void testOperationPruning() throws Exception {
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
+        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
+
+        args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
+                true, args, null);
+        assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
+        IBinder first = result.token;
+        // Implementation detail: softkeymaster supports 16 concurrent operations
+        for (int i = 0; i < 16; i++) {
+            result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null);
+            assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
+        }
+        // At this point the first operation should be pruned.
+        assertEquals("Operation should be pruned", KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
+                mKeyStore.update(first, null, new byte[] {0x01}).resultCode);
+    }
+
+    public void testAuthNeeded() throws Exception {
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
+        args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+        args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
+        assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
+        OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
+                true, args, null);
+        assertEquals("Begin should expect authorization", KeyStore.OP_AUTH_NEEDED,
+                result.resultCode);
+        IBinder token = result.token;
+        result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
+        assertEquals("Update should require authorization",
+                KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED, result.resultCode);
+    }
+
+    public void testPasswordRemovalEncryptedEntry() throws Exception {
+        mKeyStore.onUserPasswordChanged("test");
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+        mKeyStore.onUserPasswordChanged("");
+        // Removing the password should have deleted all entries using FLAG_ENCRYPTED
+        assertNull(mKeyStore.get(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testPasswordRemovalUnencryptedEntry() throws Exception {
+        mKeyStore.onUserPasswordChanged("test");
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, KeyStore.UID_SELF,
+                KeyStore.FLAG_NONE));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+        mKeyStore.onUserPasswordChanged("");
+        // Removing the password should not delete unencrypted entries.
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
+    }
+}
diff --git a/keystore/tests/src/android/security/SystemKeyStoreTest.java b/keystore/tests/src/android/security/SystemKeyStoreTest.java
new file mode 100644
index 0000000..ecf7cbc
--- /dev/null
+++ b/keystore/tests/src/android/security/SystemKeyStoreTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 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 android.security;
+
+import android.app.Activity;
+import android.security.SystemKeyStore;
+import android.test.ActivityUnitTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/**
+ * Junit / Instrumentation test case for KeyStore class
+ *
+ * Running the test suite:
+ *
+ *  runtest keystore-unit
+ *
+ * Or this individual test case:
+ *
+ *  runtest --path frameworks/base/keystore/tests/src/android/security/SystemKeyStoreTest.java
+ */
+@MediumTest
+public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> {
+
+    private static final String keyName = "TestKey";
+    private static final String keyName2 = "TestKey2";
+    private SystemKeyStore mSysKeyStore = null;
+
+    public SystemKeyStoreTest() {
+        super(Activity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mSysKeyStore = SystemKeyStore.getInstance();
+        try {
+            mSysKeyStore.deleteKey(keyName);
+            mSysKeyStore.deleteKey(keyName2);
+        } catch (Exception e) { }
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            mSysKeyStore.deleteKey(keyName);
+            mSysKeyStore.deleteKey(keyName2);
+        } catch (Exception e) { }
+        super.tearDown();
+    }
+
+    public void testBasicAccess() throws Exception {
+        try {
+            byte[] newKey = mSysKeyStore.generateNewKey(128, "AES", keyName);
+            assertNotNull(newKey);
+            byte[] recKey = mSysKeyStore.retrieveKey(keyName);
+            assertEquals(newKey.length, recKey.length);
+            for (int i = 0; i < newKey.length; i++) {
+                assertEquals(newKey[i], recKey[i]);
+            }
+            mSysKeyStore.deleteKey(keyName);
+            byte[] nullKey = mSysKeyStore.retrieveKey(keyName);
+            assertNull(nullKey);
+
+            String newKeyStr = mSysKeyStore.generateNewKeyHexString(128, "AES", keyName2);
+            assertNotNull(newKeyStr);
+            String recKeyStr = mSysKeyStore.retrieveKeyHexString(keyName2);
+            assertEquals(newKeyStr, recKeyStr);
+
+            mSysKeyStore.deleteKey(keyName2);
+            String nullKey2 = mSysKeyStore.retrieveKeyHexString(keyName2);
+            assertNull(nullKey2);
+        } catch (Exception e) {
+            fail();
+        }
+    }
+}
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
new file mode 100644
index 0000000..1af0b7d
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyPairGeneratorTest.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2012 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 android.security.keystore;
+
+import android.security.Credentials;
+import android.security.KeyPairGeneratorSpec;
+import android.security.KeyStore;
+import android.security.keymaster.ExportResult;
+import android.security.keymaster.KeymasterDefs;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
+    private android.security.KeyStore mAndroidKeyStore;
+
+    private java.security.KeyPairGenerator mGenerator;
+
+    private static final String TEST_ALIAS_1 = "test1";
+
+    private static final String TEST_ALIAS_2 = "test2";
+
+    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+    private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
+
+    private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
+
+    private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
+
+    private static final long NOW_MILLIS = System.currentTimeMillis();
+
+    /* We have to round this off because X509v3 doesn't store milliseconds. */
+    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+    @SuppressWarnings("deprecation")
+    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+    @Override
+    protected void setUp() throws Exception {
+        mAndroidKeyStore = android.security.KeyStore.getInstance();
+
+        assertTrue(mAndroidKeyStore.reset());
+
+        assertFalse(mAndroidKeyStore.isUnlocked());
+
+        mGenerator = java.security.KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+    }
+
+    private void setupPassword() {
+        assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
+        assertTrue(mAndroidKeyStore.isUnlocked());
+
+        String[] aliases = mAndroidKeyStore.list("");
+        assertNotNull(aliases);
+        assertEquals(0, aliases.length);
+    }
+
+    public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .setEncryptionRequired()
+                .build());
+    }
+
+    public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        try {
+            mGenerator.initialize(1024);
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure()
+            throws Exception {
+        setupPassword();
+
+        try {
+            mGenerator.initialize(1024, new SecureRandom());
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure()
+            throws Exception {
+        setupPassword();
+
+        mGenerator.initialize(
+                new KeyPairGeneratorSpec.Builder(getContext())
+                        .setAlias(TEST_ALIAS_1)
+                        .setKeyType("RSA")
+                        .setKeySize(1024)
+                        .setSubject(TEST_DN_1)
+                        .setSerialNumber(TEST_SERIAL_1)
+                        .setStartDate(NOW)
+                        .setEndDate(NOW_PLUS_10_YEARS)
+                        .setEncryptionRequired()
+                        .build(),
+                new SecureRandom());
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .setEncryptionRequired()
+                .build());
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setCertificateSubject(TEST_DN_1)
+                .setCertificateSerialNumber(TEST_SERIAL_1)
+                .setCertificateNotBefore(NOW)
+                .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+                .setDigests(KeyProperties.DIGEST_SHA256)
+                .build());
+
+        final KeyPair pair = generator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_Legacy_GenerateKeyPair_EC_Unencrypted_Success()
+            throws Exception {
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setKeyType("EC")
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build());
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setKeyType("EC")
+                .setKeySize(521)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build());
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build());
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_RSA_WithParams_Unencrypted_Success()
+            throws Exception {
+        AlgorithmParameterSpec spec = new RSAKeyGenParameterSpec(1024, BigInteger.valueOf(3L));
+        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setKeySize(1024)
+                .setAlgorithmParameterSpec(spec)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build());
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 1024, spec, TEST_DN_1, TEST_SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
+        // Generate the first key
+        {
+            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                    .setAlias(TEST_ALIAS_1)
+                    .setSubject(TEST_DN_1)
+                    .setSerialNumber(TEST_SERIAL_1)
+                    .setStartDate(NOW)
+                    .setEndDate(NOW_PLUS_10_YEARS)
+                    .build());
+            final KeyPair pair1 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair1);
+            assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
+                    NOW, NOW_PLUS_10_YEARS);
+        }
+
+        // Replace the original key
+        {
+            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                    .setAlias(TEST_ALIAS_2)
+                    .setSubject(TEST_DN_2)
+                    .setSerialNumber(TEST_SERIAL_2)
+                    .setStartDate(NOW)
+                    .setEndDate(NOW_PLUS_10_YEARS)
+                    .build());
+            final KeyPair pair2 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair2);
+            assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
+                    NOW, NOW_PLUS_10_YEARS);
+        }
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success()
+            throws Exception {
+        // Generate the first key
+        {
+            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                    .setAlias(TEST_ALIAS_1)
+                    .setSubject(TEST_DN_1)
+                    .setSerialNumber(TEST_SERIAL_1)
+                    .setStartDate(NOW)
+                    .setEndDate(NOW_PLUS_10_YEARS)
+                    .build());
+            final KeyPair pair1 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair1);
+            assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
+                    NOW, NOW_PLUS_10_YEARS);
+        }
+
+        // Attempt to replace previous key
+        {
+            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                    .setAlias(TEST_ALIAS_1)
+                    .setSubject(TEST_DN_2)
+                    .setSerialNumber(TEST_SERIAL_2)
+                    .setStartDate(NOW)
+                    .setEndDate(NOW_PLUS_10_YEARS)
+                    .setEncryptionRequired()
+                    .build());
+            try {
+                mGenerator.generateKeyPair();
+                fail("Should not be able to generate encrypted key while not initialized");
+            } catch (IllegalStateException expected) {
+            }
+
+            assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
+            assertTrue(mAndroidKeyStore.isUnlocked());
+
+            final KeyPair pair2 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair2);
+            assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
+                    NOW, NOW_PLUS_10_YEARS);
+        }
+    }
+
+    private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
+            AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
+            throws Exception {
+        final PublicKey pubKey = pair.getPublic();
+        assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
+        assertEquals(keyType, pubKey.getAlgorithm());
+
+        if ("EC".equalsIgnoreCase(keyType)) {
+            assertEquals("Curve should be what was specified during initialization", keySize,
+                    ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
+        } else if ("RSA".equalsIgnoreCase(keyType)) {
+            RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
+            assertEquals("Modulus size should be what is specified during initialization",
+                    (keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
+            if (spec != null) {
+                RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
+                assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
+                assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
+            }
+        }
+
+        final PrivateKey privKey = pair.getPrivate();
+        assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
+        assertEquals(keyType, privKey.getAlgorithm());
+
+        if ("EC".equalsIgnoreCase(keyType)) {
+            assertTrue("EC private key must be instanceof ECKey: " + privKey.getClass().getName(),
+                    privKey instanceof ECKey);
+            assertEquals("Private and public key must have the same EC parameters",
+                    ((ECKey) pubKey).getParams(), ((ECKey) privKey).getParams());
+        } else if ("RSA".equalsIgnoreCase(keyType)) {
+            assertTrue("RSA private key must be instance of RSAKey: "
+                    + privKey.getClass().getName(),
+                    privKey instanceof RSAKey);
+            assertEquals("Private and public key must have the same RSA modulus",
+                    ((RSAKey) pubKey).getModulus(), ((RSAKey) privKey).getModulus());
+        }
+
+        final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+        assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
+
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final Certificate userCert =
+                cf.generateCertificate(new ByteArrayInputStream(userCertBytes));
+
+        assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
+
+        final X509Certificate x509userCert = (X509Certificate) userCert;
+
+        assertEquals(
+                "Public key used to sign certificate should have the same algorithm as in KeyPair",
+                pubKey.getAlgorithm(), x509userCert.getPublicKey().getAlgorithm());
+
+        assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
+                pubKey,
+                AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
+                        Credentials.USER_PRIVATE_KEY + alias,
+                        KeyStore.UID_SELF,
+                        x509userCert.getPublicKey().getAlgorithm(),
+                        x509userCert.getPublicKey().getEncoded()));
+
+        assertEquals("The Subject DN should be the one passed into the params", dn,
+                x509userCert.getSubjectDN());
+
+        assertEquals("The Issuer DN should be the same as the Subject DN", dn,
+                x509userCert.getIssuerDN());
+
+        assertEquals("The Serial should be the one passed into the params", serial,
+                x509userCert.getSerialNumber());
+
+        assertDateEquals("The notBefore date should be the one passed into the params", start,
+                x509userCert.getNotBefore());
+
+        assertDateEquals("The notAfter date should be the one passed into the params", end,
+                x509userCert.getNotAfter());
+
+        // Assert that the cert's signature verifies using the public key from generated KeyPair
+        x509userCert.verify(pubKey);
+        // Assert that the cert's signature verifies using the public key from the cert itself.
+        x509userCert.verify(x509userCert.getPublicKey());
+
+        final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+        assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
+
+        ExportResult exportResult = mAndroidKeyStore.exportKey(
+                Credentials.USER_PRIVATE_KEY + alias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null);
+        assertEquals(KeyStore.NO_ERROR, exportResult.resultCode);
+        final byte[] pubKeyBytes = exportResult.exportData;
+        assertNotNull("The keystore should return the public key for the generated key",
+                pubKeyBytes);
+        assertTrue("Public key X.509 format should be as expected",
+                Arrays.equals(pubKey.getEncoded(), pubKeyBytes));
+    }
+
+    private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
+        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+        String result1 = formatter.format(date1);
+        String result2 = formatter.format(date2);
+
+        assertEquals(message, result1, result2);
+    }
+}
diff --git a/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
new file mode 100644
index 0000000..aa718dc
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore/AndroidKeyStoreTest.java
@@ -0,0 +1,2210 @@
+/*
+ * Copyright (C) 2012 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 android.security.keystore;
+
+import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+
+import com.android.org.conscrypt.NativeConstants;
+
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.security.KeyStoreParameter;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.KeyStoreException;
+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.interfaces.ECKey;
+import java.security.interfaces.RSAKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.x500.X500Principal;
+
+public class AndroidKeyStoreTest extends AndroidTestCase {
+    private android.security.KeyStore mAndroidKeyStore;
+
+    private java.security.KeyStore mKeyStore;
+
+    private static final String TEST_ALIAS_1 = "test1";
+
+    private static final String TEST_ALIAS_2 = "test2";
+
+    private static final String TEST_ALIAS_3 = "test3";
+
+    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+    private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
+
+    private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
+
+    private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
+
+    private static final long NOW_MILLIS = System.currentTimeMillis();
+
+    /* We have to round this off because X509v3 doesn't store milliseconds. */
+    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+    @SuppressWarnings("deprecation")
+    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+    /*
+     * The keys and certificates below are generated with:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
+     * mkdir -p demoCA/newcerts
+     * touch demoCA/index.txt
+     * echo "01" > demoCA/serial
+     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+     */
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_CA_1 = {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
+            (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
+            (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
+            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
+            (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
+            (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
+            (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
+            (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+            (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
+            (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
+            (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
+            (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
+            (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
+            (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
+            (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
+            (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
+            (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
+            (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
+            (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
+            (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
+            (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
+            (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
+            (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
+            (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
+            (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
+            (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+            (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+            (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
+            (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
+            (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
+            (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
+            (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
+            (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
+            (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
+            (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
+            (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
+            (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
+            (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
+            (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
+            (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
+            (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
+            (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
+            (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
+            (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
+            (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
+            (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
+            (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
+            (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
+            (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
+            (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
+            (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+            (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
+            (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
+            (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
+            (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
+            (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
+            (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
+            (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
+            (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
+            (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
+            (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
+            (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
+            (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
+            (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+            (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+            (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+            (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+            (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+            (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+            (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
+            (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
+            (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
+            (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
+            (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+            (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+            (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
+            (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
+            (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
+            (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
+            (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
+            (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
+            (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
+            (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
+            (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
+            (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
+            (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
+            (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
+            (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
+            (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
+            (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
+            (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
+            (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
+            (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
+            (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
+            (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
+            (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
+            (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
+            (byte) 0xf1, (byte) 0x61
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+            (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+            (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+            (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+            (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+            (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+            (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+            (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+            (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+            (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+            (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+            (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+            (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+            (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+            (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+            (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+            (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+            (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+            (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+            (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+            (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+            (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+            (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+            (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+            (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+            (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+            (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+            (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+            (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+            (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+            (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+            (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+            (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+            (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+            (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+            (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+            (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+            (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+            (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+            (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+            (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+            (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+            (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+            (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+            (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+            (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+            (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+            (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+            (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+            (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+            (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+            (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+            (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+            (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+            (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+            (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+            (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+            (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+            (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+            (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+            (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+            (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+            (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+            (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+            (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+            (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+            (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+            (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+            (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+            (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+            (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+            (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+            (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+            (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+            (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+            (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+            (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+            (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+            (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+            (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+            (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+            (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+            (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+            (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+            (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+            (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+            (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+            (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+            (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+            (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+            (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+            (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+            (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+            (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+            (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+            (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+            (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+            (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+            (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+            (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+            (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+            (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+            (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+            (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_USER_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x95, (byte) 0x30, (byte) 0x82,
+            (byte) 0x01, (byte) 0xfe, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+            (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+            (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+            (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+            (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+            (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+            (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+            (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+            (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+            (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30, (byte) 0x1e,
+            (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32, (byte) 0x30, (byte) 0x38,
+            (byte) 0x31, (byte) 0x34, (byte) 0x32, (byte) 0x33, (byte) 0x32, (byte) 0x35,
+            (byte) 0x34, (byte) 0x38, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32,
+            (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x32,
+            (byte) 0x33, (byte) 0x32, (byte) 0x35, (byte) 0x34, (byte) 0x38, (byte) 0x5a,
+            (byte) 0x30, (byte) 0x55, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+            (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+            (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31, (byte) 0x1b,
+            (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e, (byte) 0x64,
+            (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x54,
+            (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x61,
+            (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x31, (byte) 0x1c, (byte) 0x30,
+            (byte) 0x1a, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+            (byte) 0x13, (byte) 0x13, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76,
+            (byte) 0x65, (byte) 0x72, (byte) 0x31, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
+            (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
+            (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+            (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+            (byte) 0x81, (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6,
+            (byte) 0x5b, (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c,
+            (byte) 0x66, (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86,
+            (byte) 0x8a, (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3,
+            (byte) 0x02, (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08,
+            (byte) 0xf3, (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04,
+            (byte) 0x6d, (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f,
+            (byte) 0x67, (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c,
+            (byte) 0xcb, (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30,
+            (byte) 0xe2, (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5,
+            (byte) 0x79, (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b,
+            (byte) 0xce, (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb,
+            (byte) 0x08, (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff,
+            (byte) 0x3b, (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9,
+            (byte) 0xc4, (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29,
+            (byte) 0x0d, (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b,
+            (byte) 0x23, (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78,
+            (byte) 0x08, (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5,
+            (byte) 0xf1, (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19,
+            (byte) 0xb4, (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03,
+            (byte) 0x16, (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce,
+            (byte) 0x9e, (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03,
+            (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x7b, (byte) 0x30,
+            (byte) 0x79, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+            (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86,
+            (byte) 0x48, (byte) 0x01, (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01,
+            (byte) 0x0d, (byte) 0x04, (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f,
+            (byte) 0x70, (byte) 0x65, (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c,
+            (byte) 0x20, (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72,
+            (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43,
+            (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+            (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04,
+            (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x32, (byte) 0xa1, (byte) 0x1e,
+            (byte) 0x6b, (byte) 0x69, (byte) 0x04, (byte) 0xfe, (byte) 0xb3, (byte) 0xcd,
+            (byte) 0xf8, (byte) 0xbb, (byte) 0x14, (byte) 0xcd, (byte) 0xff, (byte) 0xd4,
+            (byte) 0x16, (byte) 0xc3, (byte) 0xab, (byte) 0x44, (byte) 0x2f, (byte) 0x30,
+            (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23,
+            (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14,
+            (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60,
+            (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c,
+            (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e,
+            (byte) 0x5d, (byte) 0x51, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+            (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x46, (byte) 0x42, (byte) 0xef,
+            (byte) 0x56, (byte) 0x89, (byte) 0x78, (byte) 0x90, (byte) 0x38, (byte) 0x24,
+            (byte) 0x9f, (byte) 0x8c, (byte) 0x7a, (byte) 0xce, (byte) 0x7a, (byte) 0xa5,
+            (byte) 0xb5, (byte) 0x1e, (byte) 0x74, (byte) 0x96, (byte) 0x34, (byte) 0x49,
+            (byte) 0x8b, (byte) 0xed, (byte) 0x44, (byte) 0xb3, (byte) 0xc9, (byte) 0x05,
+            (byte) 0xd7, (byte) 0x48, (byte) 0x55, (byte) 0x52, (byte) 0x59, (byte) 0x15,
+            (byte) 0x0b, (byte) 0xaa, (byte) 0x16, (byte) 0x86, (byte) 0xd2, (byte) 0x8e,
+            (byte) 0x16, (byte) 0x99, (byte) 0xe8, (byte) 0x5f, (byte) 0x11, (byte) 0x71,
+            (byte) 0x42, (byte) 0x55, (byte) 0xd1, (byte) 0xc4, (byte) 0x6f, (byte) 0x2e,
+            (byte) 0xa9, (byte) 0x64, (byte) 0x6f, (byte) 0xd8, (byte) 0xfd, (byte) 0x43,
+            (byte) 0x13, (byte) 0x24, (byte) 0xaa, (byte) 0x67, (byte) 0xe6, (byte) 0xf5,
+            (byte) 0xca, (byte) 0x80, (byte) 0x5e, (byte) 0x3a, (byte) 0x3e, (byte) 0xcc,
+            (byte) 0x4f, (byte) 0xba, (byte) 0x87, (byte) 0xe6, (byte) 0xae, (byte) 0xbf,
+            (byte) 0x8f, (byte) 0xd5, (byte) 0x28, (byte) 0x38, (byte) 0x58, (byte) 0x30,
+            (byte) 0x24, (byte) 0xf6, (byte) 0x53, (byte) 0x5b, (byte) 0x41, (byte) 0x53,
+            (byte) 0xe6, (byte) 0x45, (byte) 0xbc, (byte) 0xbe, (byte) 0xe6, (byte) 0xbb,
+            (byte) 0x5d, (byte) 0xd8, (byte) 0xa7, (byte) 0xf9, (byte) 0x64, (byte) 0x99,
+            (byte) 0x04, (byte) 0x43, (byte) 0x75, (byte) 0xd7, (byte) 0x2d, (byte) 0x32,
+            (byte) 0x0a, (byte) 0x94, (byte) 0xaf, (byte) 0x06, (byte) 0x34, (byte) 0xae,
+            (byte) 0x46, (byte) 0xbd, (byte) 0xda, (byte) 0x00, (byte) 0x0e, (byte) 0x25,
+            (byte) 0xc2, (byte) 0xf7, (byte) 0xc9, (byte) 0xc3, (byte) 0x65, (byte) 0xd2,
+            (byte) 0x08, (byte) 0x41, (byte) 0x0a, (byte) 0xf3, (byte) 0x72
+    };
+
+    /*
+     * The keys and certificates below are generated with:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     * openssl ecparam -name prime256v1 -out ecparam.pem
+     * openssl req -newkey ec:ecparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+     * mkdir -p demoCA/newcerts
+     * touch demoCA/index.txt
+     * echo "01" > demoCA/serial
+     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+     */
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_EC_CA_1 = {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x58, (byte) 0x30, (byte) 0x82,
+            (byte) 0x01, (byte) 0xc1, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0xb2,
+            (byte) 0x8c, (byte) 0x04, (byte) 0x95, (byte) 0xeb, (byte) 0x10, (byte) 0xcb,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x45, (byte) 0x31,
+            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
+            (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
+            (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
+            (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+            (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
+            (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+            (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
+            (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
+            (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
+            (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d,
+            (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x37,
+            (byte) 0x31, (byte) 0x36, (byte) 0x32, (byte) 0x38, (byte) 0x32, (byte) 0x38,
+            (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33, (byte) 0x30,
+            (byte) 0x38, (byte) 0x32, (byte) 0x35, (byte) 0x31, (byte) 0x36, (byte) 0x32,
+            (byte) 0x38, (byte) 0x32, (byte) 0x38, (byte) 0x5a, (byte) 0x30, (byte) 0x45,
+            (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+            (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+            (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+            (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21,
+            (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x0a, (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74,
+            (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20,
+            (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74,
+            (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20,
+            (byte) 0x4c, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+            (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+            (byte) 0x81, (byte) 0x00, (byte) 0xb5, (byte) 0xf6, (byte) 0x08, (byte) 0x0f,
+            (byte) 0xc4, (byte) 0x4d, (byte) 0xe4, (byte) 0x0d, (byte) 0x34, (byte) 0x1d,
+            (byte) 0xe2, (byte) 0x23, (byte) 0x18, (byte) 0x63, (byte) 0x03, (byte) 0xf7,
+            (byte) 0x14, (byte) 0x0e, (byte) 0x98, (byte) 0xcd, (byte) 0x45, (byte) 0x1f,
+            (byte) 0xfe, (byte) 0xfb, (byte) 0x09, (byte) 0x3f, (byte) 0x5d, (byte) 0x36,
+            (byte) 0x3b, (byte) 0x0f, (byte) 0xf9, (byte) 0x5e, (byte) 0x86, (byte) 0x56,
+            (byte) 0x64, (byte) 0xd7, (byte) 0x3f, (byte) 0xae, (byte) 0x33, (byte) 0x09,
+            (byte) 0xd3, (byte) 0xdd, (byte) 0x06, (byte) 0x17, (byte) 0x26, (byte) 0xdc,
+            (byte) 0xa2, (byte) 0x8c, (byte) 0x3c, (byte) 0x65, (byte) 0xed, (byte) 0x03,
+            (byte) 0x82, (byte) 0x78, (byte) 0x9b, (byte) 0xee, (byte) 0xe3, (byte) 0x98,
+            (byte) 0x58, (byte) 0xe1, (byte) 0xf1, (byte) 0xa0, (byte) 0x85, (byte) 0xae,
+            (byte) 0x63, (byte) 0x84, (byte) 0x41, (byte) 0x46, (byte) 0xa7, (byte) 0x4f,
+            (byte) 0xdc, (byte) 0xbb, (byte) 0x1c, (byte) 0x6e, (byte) 0xec, (byte) 0x7b,
+            (byte) 0xd5, (byte) 0xab, (byte) 0x3d, (byte) 0x6a, (byte) 0x05, (byte) 0x58,
+            (byte) 0x0f, (byte) 0x9b, (byte) 0x6a, (byte) 0x67, (byte) 0x4b, (byte) 0xe9,
+            (byte) 0x2a, (byte) 0x6d, (byte) 0x96, (byte) 0x11, (byte) 0x53, (byte) 0x95,
+            (byte) 0x78, (byte) 0xaa, (byte) 0xd1, (byte) 0x91, (byte) 0x4a, (byte) 0xf8,
+            (byte) 0x54, (byte) 0x52, (byte) 0x6d, (byte) 0xb9, (byte) 0xca, (byte) 0x74,
+            (byte) 0x81, (byte) 0xf8, (byte) 0x99, (byte) 0x64, (byte) 0xd1, (byte) 0x4f,
+            (byte) 0x01, (byte) 0x38, (byte) 0x4f, (byte) 0x08, (byte) 0x5c, (byte) 0x31,
+            (byte) 0xcb, (byte) 0x7c, (byte) 0x5c, (byte) 0x78, (byte) 0x5d, (byte) 0x47,
+            (byte) 0xd9, (byte) 0xf0, (byte) 0x1a, (byte) 0xeb, (byte) 0x02, (byte) 0x03,
+            (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x50, (byte) 0x30,
+            (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+            (byte) 0x5f, (byte) 0x5b, (byte) 0x5e, (byte) 0xac, (byte) 0x29, (byte) 0xfa,
+            (byte) 0xa1, (byte) 0x9f, (byte) 0x9e, (byte) 0xad, (byte) 0x46, (byte) 0xe1,
+            (byte) 0xbc, (byte) 0x20, (byte) 0x72, (byte) 0xcf, (byte) 0x4a, (byte) 0xd4,
+            (byte) 0xfa, (byte) 0xe3, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+            (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x5f, (byte) 0x5b, (byte) 0x5e,
+            (byte) 0xac, (byte) 0x29, (byte) 0xfa, (byte) 0xa1, (byte) 0x9f, (byte) 0x9e,
+            (byte) 0xad, (byte) 0x46, (byte) 0xe1, (byte) 0xbc, (byte) 0x20, (byte) 0x72,
+            (byte) 0xcf, (byte) 0x4a, (byte) 0xd4, (byte) 0xfa, (byte) 0xe3, (byte) 0x30,
+            (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+            (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01,
+            (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+            (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81,
+            (byte) 0x81, (byte) 0x00, (byte) 0xa1, (byte) 0x4a, (byte) 0xe6, (byte) 0xfc,
+            (byte) 0x7f, (byte) 0x17, (byte) 0xaa, (byte) 0x65, (byte) 0x4a, (byte) 0x34,
+            (byte) 0xde, (byte) 0x69, (byte) 0x67, (byte) 0x54, (byte) 0x4d, (byte) 0xa2,
+            (byte) 0xc2, (byte) 0x98, (byte) 0x02, (byte) 0x43, (byte) 0x6a, (byte) 0x0e,
+            (byte) 0x0b, (byte) 0x7f, (byte) 0xa4, (byte) 0x46, (byte) 0xaf, (byte) 0xa4,
+            (byte) 0x65, (byte) 0xa0, (byte) 0xdb, (byte) 0xf1, (byte) 0x5b, (byte) 0xd5,
+            (byte) 0x09, (byte) 0xbc, (byte) 0xee, (byte) 0x37, (byte) 0x51, (byte) 0x19,
+            (byte) 0x36, (byte) 0xc0, (byte) 0x90, (byte) 0xd3, (byte) 0x5f, (byte) 0xf3,
+            (byte) 0x4f, (byte) 0xb9, (byte) 0x08, (byte) 0x45, (byte) 0x0e, (byte) 0x01,
+            (byte) 0x8a, (byte) 0x95, (byte) 0xef, (byte) 0x92, (byte) 0x95, (byte) 0x33,
+            (byte) 0x78, (byte) 0xdd, (byte) 0x90, (byte) 0xbb, (byte) 0xf3, (byte) 0x06,
+            (byte) 0x75, (byte) 0xd0, (byte) 0x66, (byte) 0xe6, (byte) 0xd0, (byte) 0x18,
+            (byte) 0x6e, (byte) 0xeb, (byte) 0x1c, (byte) 0x52, (byte) 0xc3, (byte) 0x2e,
+            (byte) 0x57, (byte) 0x7d, (byte) 0xa9, (byte) 0x03, (byte) 0xdb, (byte) 0xf4,
+            (byte) 0x57, (byte) 0x5f, (byte) 0x6c, (byte) 0x7e, (byte) 0x00, (byte) 0x0d,
+            (byte) 0x8f, (byte) 0xe8, (byte) 0x91, (byte) 0xf7, (byte) 0xae, (byte) 0x24,
+            (byte) 0x35, (byte) 0x07, (byte) 0xb5, (byte) 0x48, (byte) 0x2d, (byte) 0x36,
+            (byte) 0x30, (byte) 0x5d, (byte) 0xe9, (byte) 0x49, (byte) 0x2d, (byte) 0xd1,
+            (byte) 0x5d, (byte) 0xc5, (byte) 0xf4, (byte) 0x33, (byte) 0x77, (byte) 0x3c,
+            (byte) 0x71, (byte) 0xad, (byte) 0x90, (byte) 0x65, (byte) 0xa9, (byte) 0xc1,
+            (byte) 0x0b, (byte) 0x5c, (byte) 0x62, (byte) 0x55, (byte) 0x50, (byte) 0x6f,
+            (byte) 0x9b, (byte) 0xc9, (byte) 0x0d, (byte) 0xee
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_EC_KEY_1 = new byte[] {
+            (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x01, (byte) 0x00,
+            (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06,
+            (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d,
+            (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x04, (byte) 0x6d, (byte) 0x30,
+            (byte) 0x6b, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x20,
+            (byte) 0x3a, (byte) 0x8a, (byte) 0x02, (byte) 0xdc, (byte) 0xde, (byte) 0x70,
+            (byte) 0x84, (byte) 0x45, (byte) 0x34, (byte) 0xaf, (byte) 0xbd, (byte) 0xd5,
+            (byte) 0x02, (byte) 0x17, (byte) 0x69, (byte) 0x90, (byte) 0x65, (byte) 0x1e,
+            (byte) 0x87, (byte) 0xf1, (byte) 0x3d, (byte) 0x17, (byte) 0xb6, (byte) 0xf4,
+            (byte) 0x31, (byte) 0x94, (byte) 0x86, (byte) 0x76, (byte) 0x55, (byte) 0xf7,
+            (byte) 0xcc, (byte) 0xba, (byte) 0xa1, (byte) 0x44, (byte) 0x03, (byte) 0x42,
+            (byte) 0x00, (byte) 0x04, (byte) 0xd9, (byte) 0xcf, (byte) 0xe7, (byte) 0x9b,
+            (byte) 0x23, (byte) 0xc8, (byte) 0xa3, (byte) 0xb8, (byte) 0x33, (byte) 0x14,
+            (byte) 0xa4, (byte) 0x4d, (byte) 0x75, (byte) 0x90, (byte) 0xf3, (byte) 0xcd,
+            (byte) 0x43, (byte) 0xe5, (byte) 0x1b, (byte) 0x05, (byte) 0x1d, (byte) 0xf3,
+            (byte) 0xd0, (byte) 0xa3, (byte) 0xb7, (byte) 0x32, (byte) 0x5f, (byte) 0x79,
+            (byte) 0xdc, (byte) 0x88, (byte) 0xb8, (byte) 0x4d, (byte) 0xb3, (byte) 0xd1,
+            (byte) 0x6d, (byte) 0xf7, (byte) 0x75, (byte) 0xf3, (byte) 0xbf, (byte) 0x50,
+            (byte) 0xa1, (byte) 0xbc, (byte) 0x03, (byte) 0x64, (byte) 0x22, (byte) 0xe6,
+            (byte) 0x1a, (byte) 0xa1, (byte) 0xe1, (byte) 0x06, (byte) 0x68, (byte) 0x3b,
+            (byte) 0xbc, (byte) 0x9f, (byte) 0xd3, (byte) 0xae, (byte) 0x77, (byte) 0x5e,
+            (byte) 0x88, (byte) 0x0c, (byte) 0x5e, (byte) 0x0c, (byte) 0xb2, (byte) 0x38
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_EC_USER_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x51, (byte) 0x30, (byte) 0x82,
+            (byte) 0x01, (byte) 0xba, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+            (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+            (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+            (byte) 0x00, (byte) 0x30, (byte) 0x45, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13,
+            (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+            (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74,
+            (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18,
+            (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e,
+            (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64,
+            (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50,
+            (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64,
+            (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x33,
+            (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x37, (byte) 0x31, (byte) 0x36,
+            (byte) 0x33, (byte) 0x30, (byte) 0x30, (byte) 0x38, (byte) 0x5a, (byte) 0x17,
+            (byte) 0x0d, (byte) 0x32, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32,
+            (byte) 0x35, (byte) 0x31, (byte) 0x36, (byte) 0x33, (byte) 0x30, (byte) 0x30,
+            (byte) 0x38, (byte) 0x5a, (byte) 0x30, (byte) 0x62, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31,
+            (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f,
+            (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61,
+            (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c,
+            (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
+            (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57, (byte) 0x69,
+            (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73, (byte) 0x20,
+            (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c, (byte) 0x74,
+            (byte) 0x64, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x12,
+            (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72,
+            (byte) 0x2e, (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
+            (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
+            (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07,
+            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02,
+            (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+            (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x03,
+            (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0xd9, (byte) 0xcf, (byte) 0xe7,
+            (byte) 0x9b, (byte) 0x23, (byte) 0xc8, (byte) 0xa3, (byte) 0xb8, (byte) 0x33,
+            (byte) 0x14, (byte) 0xa4, (byte) 0x4d, (byte) 0x75, (byte) 0x90, (byte) 0xf3,
+            (byte) 0xcd, (byte) 0x43, (byte) 0xe5, (byte) 0x1b, (byte) 0x05, (byte) 0x1d,
+            (byte) 0xf3, (byte) 0xd0, (byte) 0xa3, (byte) 0xb7, (byte) 0x32, (byte) 0x5f,
+            (byte) 0x79, (byte) 0xdc, (byte) 0x88, (byte) 0xb8, (byte) 0x4d, (byte) 0xb3,
+            (byte) 0xd1, (byte) 0x6d, (byte) 0xf7, (byte) 0x75, (byte) 0xf3, (byte) 0xbf,
+            (byte) 0x50, (byte) 0xa1, (byte) 0xbc, (byte) 0x03, (byte) 0x64, (byte) 0x22,
+            (byte) 0xe6, (byte) 0x1a, (byte) 0xa1, (byte) 0xe1, (byte) 0x06, (byte) 0x68,
+            (byte) 0x3b, (byte) 0xbc, (byte) 0x9f, (byte) 0xd3, (byte) 0xae, (byte) 0x77,
+            (byte) 0x5e, (byte) 0x88, (byte) 0x0c, (byte) 0x5e, (byte) 0x0c, (byte) 0xb2,
+            (byte) 0x38, (byte) 0xa3, (byte) 0x7b, (byte) 0x30, (byte) 0x79, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+            (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00, (byte) 0x30, (byte) 0x2c,
+            (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01,
+            (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01, (byte) 0x0d, (byte) 0x04,
+            (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f, (byte) 0x70, (byte) 0x65,
+            (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c, (byte) 0x20, (byte) 0x47,
+            (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72, (byte) 0x61, (byte) 0x74,
+            (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72,
+            (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61,
+            (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04,
+            (byte) 0x14, (byte) 0xd5, (byte) 0xc4, (byte) 0x72, (byte) 0xbd, (byte) 0xd2,
+            (byte) 0x4e, (byte) 0x90, (byte) 0x1b, (byte) 0x14, (byte) 0x32, (byte) 0xdb,
+            (byte) 0x03, (byte) 0xae, (byte) 0xfa, (byte) 0x27, (byte) 0x7d, (byte) 0x8d,
+            (byte) 0xe4, (byte) 0x80, (byte) 0x58, (byte) 0x30, (byte) 0x1f, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18,
+            (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x5f, (byte) 0x5b,
+            (byte) 0x5e, (byte) 0xac, (byte) 0x29, (byte) 0xfa, (byte) 0xa1, (byte) 0x9f,
+            (byte) 0x9e, (byte) 0xad, (byte) 0x46, (byte) 0xe1, (byte) 0xbc, (byte) 0x20,
+            (byte) 0x72, (byte) 0xcf, (byte) 0x4a, (byte) 0xd4, (byte) 0xfa, (byte) 0xe3,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81,
+            (byte) 0x00, (byte) 0x43, (byte) 0x99, (byte) 0x9f, (byte) 0x67, (byte) 0x08,
+            (byte) 0x43, (byte) 0xd5, (byte) 0x6b, (byte) 0x6f, (byte) 0xd7, (byte) 0x05,
+            (byte) 0xd6, (byte) 0x75, (byte) 0x34, (byte) 0x30, (byte) 0xca, (byte) 0x20,
+            (byte) 0x47, (byte) 0x61, (byte) 0xa1, (byte) 0x89, (byte) 0xb6, (byte) 0xf1,
+            (byte) 0x49, (byte) 0x7b, (byte) 0xd9, (byte) 0xb9, (byte) 0xe8, (byte) 0x1e,
+            (byte) 0x29, (byte) 0x74, (byte) 0x0a, (byte) 0x67, (byte) 0xc0, (byte) 0x7d,
+            (byte) 0xb8, (byte) 0xe6, (byte) 0x39, (byte) 0xa8, (byte) 0x5e, (byte) 0xc3,
+            (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x6a, (byte) 0x1f, (byte) 0x1d,
+            (byte) 0xfc, (byte) 0x11, (byte) 0x59, (byte) 0x0b, (byte) 0xb9, (byte) 0xad,
+            (byte) 0x3a, (byte) 0x4e, (byte) 0x50, (byte) 0x0a, (byte) 0x61, (byte) 0xdb,
+            (byte) 0x75, (byte) 0x6b, (byte) 0xe5, (byte) 0x3f, (byte) 0x8d, (byte) 0xde,
+            (byte) 0x28, (byte) 0x68, (byte) 0xb1, (byte) 0x29, (byte) 0x9a, (byte) 0x18,
+            (byte) 0x8a, (byte) 0xfc, (byte) 0x3f, (byte) 0x13, (byte) 0x93, (byte) 0x29,
+            (byte) 0xed, (byte) 0x22, (byte) 0x7c, (byte) 0xb4, (byte) 0x50, (byte) 0xd5,
+            (byte) 0x4d, (byte) 0x32, (byte) 0x4d, (byte) 0x42, (byte) 0x2b, (byte) 0x29,
+            (byte) 0x97, (byte) 0x86, (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0x25,
+            (byte) 0xf6, (byte) 0xd3, (byte) 0x2a, (byte) 0xd8, (byte) 0xda, (byte) 0x13,
+            (byte) 0x94, (byte) 0x12, (byte) 0x78, (byte) 0x14, (byte) 0x0b, (byte) 0x51,
+            (byte) 0xc0, (byte) 0x45, (byte) 0xb4, (byte) 0x02, (byte) 0x37, (byte) 0x98,
+            (byte) 0x42, (byte) 0x3c, (byte) 0xcb, (byte) 0x2e, (byte) 0xe4, (byte) 0x38,
+            (byte) 0x69, (byte) 0x1b, (byte) 0x72, (byte) 0xf0, (byte) 0xaa, (byte) 0x89,
+            (byte) 0x7e, (byte) 0xde, (byte) 0xb2
+    };
+
+    /**
+     * The amount of time to allow before and after expected time for variance
+     * in timing tests.
+     */
+    private static final long SLOP_TIME_MILLIS = 15000L;
+
+    @Override
+    protected void setUp() throws Exception {
+        mAndroidKeyStore = android.security.KeyStore.getInstance();
+
+        assertTrue(mAndroidKeyStore.reset());
+        assertFalse(mAndroidKeyStore.isUnlocked());
+
+        mKeyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+    }
+
+    private void setupPassword() {
+        assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
+        assertTrue(mAndroidKeyStore.isUnlocked());
+
+        assertEquals(0, mAndroidKeyStore.list("").length);
+    }
+
+    private void assertAliases(final String[] expectedAliases) throws KeyStoreException {
+        final Enumeration<String> aliases = mKeyStore.aliases();
+        int count = 0;
+
+        final Set<String> expectedSet = new HashSet<String>();
+        expectedSet.addAll(Arrays.asList(expectedAliases));
+
+        while (aliases.hasMoreElements()) {
+            count++;
+            final String alias = aliases.nextElement();
+            assertTrue("The alias should be in the expected set", expectedSet.contains(alias));
+            expectedSet.remove(alias);
+        }
+        assertTrue("The expected set and actual set should be exactly equal", expectedSet.isEmpty());
+        assertEquals("There should be the correct number of keystore entries",
+                expectedAliases.length, count);
+    }
+
+    public void testKeyStore_Aliases_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertAliases(new String[] {});
+
+        assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                null));
+
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+    }
+
+    public void testKeyStore_Aliases_NotInitialized_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        try {
+            mKeyStore.aliases();
+            fail("KeyStore should throw exception when not initialized");
+        } catch (KeyStoreException success) {
+        }
+    }
+
+    public void testKeyStore_ContainsAliases_PrivateAndCA_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertAliases(new String[] {});
+
+        assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                null));
+
+        assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+
+        assertFalse("Should not contain unadded certificate alias",
+                mKeyStore.containsAlias(TEST_ALIAS_3));
+    }
+
+    public void testKeyStore_ContainsAliases_CAOnly_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2));
+    }
+
+    public void testKeyStore_ContainsAliases_NonExistent_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_DeleteEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        // TEST_ALIAS_1
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        // TEST_ALIAS_2
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        // TEST_ALIAS_3
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_3, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+        mKeyStore.deleteEntry(TEST_ALIAS_1);
+
+        assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+        mKeyStore.deleteEntry(TEST_ALIAS_3);
+
+        assertAliases(new String[] { TEST_ALIAS_2 });
+
+        mKeyStore.deleteEntry(TEST_ALIAS_2);
+
+        assertAliases(new String[] { });
+    }
+
+    public void testKeyStore_DeleteEntry_EmptyStore_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        // Should not throw when a non-existent entry is requested for delete.
+        mKeyStore.deleteEntry(TEST_ALIAS_1);
+    }
+
+    public void testKeyStore_DeleteEntry_NonExistent_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        // TEST_ALIAS_1
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        // Should not throw when a non-existent entry is requested for delete.
+        mKeyStore.deleteEntry(TEST_ALIAS_2);
+    }
+
+    public void testKeyStore_GetCertificate_Single_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        assertNull("Certificate should not exist in keystore",
+                mKeyStore.getCertificate(TEST_ALIAS_2));
+
+        Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+        assertNotNull("Retrieved certificate should not be null", retrieved);
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        assertEquals("Actual and retrieved certificates should be the same", actual, retrieved);
+    }
+
+    public void testKeyStore_GetCertificate_NonExist_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertNull("Certificate should not exist in keystore",
+                mKeyStore.getCertificate(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_GetCertificateAlias_CAEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+                mKeyStore.getCertificateAlias(actual));
+    }
+
+    public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+
+        assertEquals("Stored certificate alias should be found", TEST_ALIAS_1,
+                mKeyStore.getCertificateAlias(actual));
+    }
+
+    public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        // Insert TrustedCertificateEntry with CA name
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        // Insert PrivateKeyEntry that uses the same CA
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        assertEquals("Stored certificate alias should be found", TEST_ALIAS_2,
+                mKeyStore.getCertificateAlias(actual));
+    }
+
+    public void testKeyStore_GetCertificateAlias_NonExist_Empty_Encrypted_Failure()
+            throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        assertNull("Stored certificate alias should not be found",
+                mKeyStore.getCertificateAlias(actual));
+    }
+
+    public void testKeyStore_GetCertificateAlias_NonExist_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        CertificateFactory f = CertificateFactory.getInstance("X.509");
+        Certificate userCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+
+        assertNull("Stored certificate alias should be found",
+                mKeyStore.getCertificateAlias(userCert));
+    }
+
+    public void testKeyStore_GetCertificateChain_SingleLength_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        Certificate[] expected = new Certificate[2];
+        expected[0] = cf.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        expected[1] = cf.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        Certificate[] actual = mKeyStore.getCertificateChain(TEST_ALIAS_1);
+
+        assertNotNull("Returned certificate chain should not be null", actual);
+        assertEquals("Returned certificate chain should be correct size", expected.length,
+                actual.length);
+        assertEquals("First certificate should be user certificate", expected[0], actual[0]);
+        assertEquals("Second certificate should be CA certificate", expected[1], actual[1]);
+
+        // Negative test when keystore is populated.
+        assertNull("Stored certificate alias should not be found",
+                mKeyStore.getCertificateChain(TEST_ALIAS_2));
+    }
+
+    public void testKeyStore_GetCertificateChain_NonExist_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertNull("Stored certificate alias should not be found",
+                mKeyStore.getCertificateChain(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_GetCreationDate_PrivateKeyEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        Date now = new Date();
+        Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+        Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+        Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+        assertTrue("Time should be close to current time", actual.before(expectedBefore));
+        assertTrue("Time should be close to current time", actual.after(expectedAfter));
+    }
+
+    public void testKeyStore_GetCreationDate_PrivateKeyEntry_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+        Date now = new Date();
+        Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+
+        Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+        Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+        assertTrue("Time should be close to current time", actual.before(expectedBefore));
+        assertTrue("Time should be close to current time", actual.after(expectedAfter));
+    }
+
+    public void testKeyStore_GetCreationDate_CAEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        Date now = new Date();
+        Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1);
+        assertNotNull("Certificate should be found", actual);
+
+        Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS);
+        Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS);
+
+        assertTrue("Time should be close to current time", actual.before(expectedBefore));
+        assertTrue("Time should be close to current time", actual.after(expectedAfter));
+    }
+
+    public void testKeyStore_GetEntry_NullParams_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Entry should exist", entry);
+
+        assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+        assertPrivateKeyEntryEquals(keyEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                FAKE_RSA_CA_1);
+    }
+
+    public void testKeyStore_GetEntry_EC_NullParams_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_EC_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                FAKE_EC_USER_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_EC_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+        Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Entry should exist", entry);
+
+        assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+        assertPrivateKeyEntryEquals(keyEntry, "EC", FAKE_EC_KEY_1, FAKE_EC_USER_1, FAKE_EC_CA_1);
+    }
+
+    public void testKeyStore_GetEntry_RSA_NullParams_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                FAKE_RSA_USER_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+        Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Entry should exist", entry);
+
+        assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+        assertPrivateKeyEntryEquals(keyEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                FAKE_RSA_CA_1);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, String keyType, byte[] key,
+            byte[] cert, byte[] ca) throws Exception {
+        KeyFactory keyFact = KeyFactory.getInstance(keyType);
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(key));
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509");
+        Certificate expectedCert = certFact.generateCertificate(new ByteArrayInputStream(cert));
+
+        final Collection<Certificate> expectedChain;
+        if (ca != null) {
+            expectedChain = (Collection<Certificate>) certFact
+                    .generateCertificates(new ByteArrayInputStream(ca));
+        } else {
+            expectedChain = null;
+        }
+
+        assertPrivateKeyEntryEquals(keyEntry, expectedKey, expectedCert, expectedChain);
+    }
+
+    private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey,
+            Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception {
+        if (expectedKey instanceof ECKey) {
+            assertEquals("Returned PrivateKey should be what we inserted",
+                    ((ECKey) expectedKey).getParams().getCurve(),
+                    ((ECKey) keyEntry.getCertificate().getPublicKey()).getParams().getCurve());
+        } else if (expectedKey instanceof RSAKey) {
+            assertEquals("Returned PrivateKey should be what we inserted",
+                    ((RSAKey) expectedKey).getModulus(),
+                    ((RSAKey) keyEntry.getPrivateKey()).getModulus());
+        }
+
+        assertEquals("Returned Certificate should be what we inserted", expectedCert,
+                keyEntry.getCertificate());
+
+        Certificate[] actualChain = keyEntry.getCertificateChain();
+
+        assertEquals("First certificate in chain should be user cert", expectedCert, actualChain[0]);
+
+        if (expectedChain == null) {
+            assertEquals("Certificate chain should not include CAs", 1, actualChain.length);
+        } else {
+            int i = 1;
+            final Iterator<Certificate> it = expectedChain.iterator();
+            while (it.hasNext()) {
+                assertEquals("CA chain certificate should equal what we put in", it.next(),
+                        actualChain[i++]);
+            }
+        }
+    }
+
+    public void testKeyStore_GetEntry_Nonexistent_NullParams_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertNull("A non-existent entry should return null",
+                mKeyStore.getEntry(TEST_ALIAS_1, null));
+    }
+
+    public void testKeyStore_GetEntry_Nonexistent_NullParams_Unencrypted_Failure() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertNull("A non-existent entry should return null",
+                mKeyStore.getEntry(TEST_ALIAS_1, null));
+    }
+
+    public void testKeyStore_GetKey_NoPassword_Encrypted_Success() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+        assertNotNull("Key should exist", key);
+
+        assertTrue("Should be a PrivateKey", key instanceof PrivateKey);
+        assertTrue("Should be a RSAKey", key instanceof RSAKey);
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+        assertEquals("Inserted key should be same as retrieved key",
+                ((RSAKey) expectedKey).getModulus(), ((RSAKey) key).getModulus());
+    }
+
+    public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_NONE));
+
+        Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+        assertNotNull("Key should exist", key);
+
+        assertTrue("Should be a PrivateKey", key instanceof PrivateKey);
+        assertTrue("Should be a RSAKey", key instanceof RSAKey);
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+        assertEquals("Inserted key should be same as retrieved key",
+                ((RSAKey) expectedKey).getModulus(), ((RSAKey) key).getModulus());
+    }
+
+    public void testKeyStore_GetKey_Certificate_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+    }
+
+    public void testKeyStore_GetKey_NonExistent_Encrypted_Failure() throws Exception {
+        setupPassword();
+
+        mKeyStore.load(null, null);
+
+        assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null));
+    }
+
+    public void testKeyStore_GetProvider_Encrypted_Success() throws Exception {
+        assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
+        setupPassword();
+        assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName());
+    }
+
+    public void testKeyStore_GetType_Encrypted_Success() throws Exception {
+        assertEquals(AndroidKeyStoreSpi.NAME, mKeyStore.getType());
+        setupPassword();
+        assertEquals(AndroidKeyStoreSpi.NAME, mKeyStore.getType());
+    }
+
+    public void testKeyStore_IsCertificateEntry_CA_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should return true for CA certificate",
+                mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsCertificateEntry_PrivateKey_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertFalse("Should return false for PrivateKeyEntry",
+                mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsCertificateEntry_NonExist_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertFalse("Should return false for non-existent entry",
+                mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsCertificateEntry_NonExist_Unencrypted_Failure() throws Exception {
+        mKeyStore.load(null, null);
+
+        assertFalse("Should return false for non-existent entry",
+                mKeyStore.isCertificateEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsKeyEntry_PrivateKey_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsKeyEntry_CA_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_IsKeyEntry_NonExist_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertFalse("Should return false for non-existent entry",
+                mKeyStore.isKeyEntry(TEST_ALIAS_1));
+    }
+
+    public void testKeyStore_SetCertificate_CA_Encrypted_Success() throws Exception {
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+        final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual);
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        Certificate retrieved = mKeyStore.getCertificate(TEST_ALIAS_1);
+
+        assertEquals("Retrieved certificate should be the same as the one inserted", actual,
+                retrieved);
+    }
+
+    public void testKeyStore_SetCertificate_CAExists_Overwrite_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+        final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        // TODO have separate FAKE_CA for second test
+        mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+
+        assertAliases(new String[] { TEST_ALIAS_1 });
+    }
+
+    public void testKeyStore_SetCertificate_PrivateKeyExists_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
+                FAKE_RSA_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_USER_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+        final Certificate cert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        try {
+            mKeyStore.setCertificateEntry(TEST_ALIAS_1, cert);
+            fail("Should throw when trying to overwrite a PrivateKey entry with a Certificate");
+        } catch (KeyStoreException success) {
+        }
+    }
+
+    public void testKeyStore_SetEntry_PrivateKeyEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate[] expectedChain = new Certificate[2];
+        expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+        mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+        Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Retrieved entry should exist", actualEntry);
+
+        assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                actualEntry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+        assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_CA_1);
+    }
+
+    public void testKeyStore_SetEntry_PrivateKeyEntry_EC_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        KeyFactory keyFact = KeyFactory.getInstance("EC");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_EC_KEY_1));
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate[] expectedChain = new Certificate[2];
+        expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_EC_USER_1));
+        expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_EC_CA_1));
+
+        PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+        mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+        Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Retrieved entry should exist", actualEntry);
+
+        assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                actualEntry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+        assertPrivateKeyEntryEquals(actual, "EC", FAKE_EC_KEY_1, FAKE_EC_USER_1, FAKE_EC_CA_1);
+    }
+
+    public void testKeyStore_SetEntry_PrivateKeyEntry_RSA_Unencrypted_Success() throws Exception {
+        mKeyStore.load(null, null);
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate[] expectedChain = new Certificate[2];
+        expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+        mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+        Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Retrieved entry should exist", actualEntry);
+
+        assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                actualEntry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+        assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_CA_1);
+    }
+
+    public void testKeyStore_SetEntry_PrivateKeyEntry_Params_Unencrypted_Failure() throws Exception {
+        mKeyStore.load(null, null);
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate[] expectedChain = new Certificate[2];
+        expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+        try {
+            mKeyStore.setEntry(TEST_ALIAS_1, entry,
+                    new KeyStoreParameter.Builder(getContext())
+                    .setEncryptionRequired(true)
+                    .build());
+            fail("Shouldn't be able to insert encrypted entry when KeyStore uninitialized");
+        } catch (KeyStoreException expected) {
+        }
+
+        assertNull(mKeyStore.getEntry(TEST_ALIAS_1, null));
+    }
+
+    public void
+            testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        // Start with PrivateKeyEntry
+        {
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+            final Certificate[] expectedChain = new Certificate[2];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+            PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+            assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+
+        // TODO make entirely new test vector for the overwrite
+        // Replace with PrivateKeyEntry
+        {
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+
+            final Certificate[] expectedChain = new Certificate[2];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+            PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expected, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+            assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+    }
+
+    public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        // Start with TrustedCertificateEntry
+        {
+            final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+            TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+                    actualEntry instanceof TrustedCertificateEntry);
+            TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+            assertEquals("Stored and retrieved certificates should be the same",
+                    expectedCertEntry.getTrustedCertificate(),
+                    actualCertEntry.getTrustedCertificate());
+        }
+
+        // Replace with PrivateKeyEntry
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] expectedChain = new Certificate[2];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+            PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+            assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+    }
+
+    public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        // Start with PrivateKeyEntry
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] expectedChain = new Certificate[2];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            expectedChain[1] = caCert;
+
+            PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+            assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+
+        // Replace with TrustedCertificateEntry
+        {
+            TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+                    actualEntry instanceof TrustedCertificateEntry);
+            TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+            assertEquals("Stored and retrieved certificates should be the same",
+                    expectedCertEntry.getTrustedCertificate(),
+                    actualCertEntry.getTrustedCertificate());
+        }
+    }
+
+    public
+            void
+            testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        // Start with PrivateKeyEntry
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] expectedChain = new Certificate[2];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            expectedChain[1] = caCert;
+
+            PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+            assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+
+        // Replace with PrivateKeyEntry that has no chain
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] expectedChain = new Certificate[1];
+            expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+
+            PrivateKeyEntry expectedPrivEntry = new PrivateKeyEntry(expectedKey, expectedChain);
+
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedPrivEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actualPrivEntry = (PrivateKeyEntry) actualEntry;
+            assertPrivateKeyEntryEquals(actualPrivEntry, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    null);
+        }
+    }
+
+    public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Encrypted_Success()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        // Insert TrustedCertificateEntry
+        {
+            final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+            TrustedCertificateEntry expectedCertEntry = new TrustedCertificateEntry(caCert);
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedCertEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+                    actualEntry instanceof TrustedCertificateEntry);
+            TrustedCertificateEntry actualCertEntry = (TrustedCertificateEntry) actualEntry;
+            assertEquals("Stored and retrieved certificates should be the same",
+                    expectedCertEntry.getTrustedCertificate(),
+                    actualCertEntry.getTrustedCertificate());
+        }
+
+        // Replace with TrustedCertificateEntry of USER
+        {
+            final Certificate userCert = f
+                    .generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+
+            TrustedCertificateEntry expectedUserEntry = new TrustedCertificateEntry(userCert);
+            mKeyStore.setEntry(TEST_ALIAS_1, expectedUserEntry, null);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+            assertTrue("Retrieved entry should be of type TrustedCertificateEntry",
+                    actualEntry instanceof TrustedCertificateEntry);
+            TrustedCertificateEntry actualUserEntry = (TrustedCertificateEntry) actualEntry;
+            assertEquals("Stored and retrieved certificates should be the same",
+                    expectedUserEntry.getTrustedCertificate(),
+                    actualUserEntry.getTrustedCertificate());
+        }
+    }
+
+    public void testKeyStore_SetKeyEntry_ProtectedKey_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+        final Certificate[] chain = new Certificate[2];
+        chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        chain[1] = caCert;
+
+        try {
+            mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, "foo".toCharArray(), chain);
+            fail("Should fail when a password is specified");
+        } catch (KeyStoreException success) {
+        }
+    }
+
+    public void testKeyStore_SetKeyEntry_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        KeyFactory keyFact = KeyFactory.getInstance("RSA");
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+        final Certificate[] chain = new Certificate[2];
+        chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+        chain[1] = caCert;
+
+        mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+        Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull("Retrieved entry should exist", actualEntry);
+
+        assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                actualEntry instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+        assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1, FAKE_RSA_CA_1);
+    }
+
+    public void testKeyStore_SetKeyEntry_Replaced_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        final CertificateFactory f = CertificateFactory.getInstance("X.509");
+
+        final Certificate caCert = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_CA_1));
+
+        // Insert initial key
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] chain = new Certificate[2];
+            chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            chain[1] = caCert;
+
+            mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+            assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+
+        // TODO make a separate key
+        // Replace key
+        {
+            KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+            final Certificate[] chain = new Certificate[2];
+            chain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_RSA_USER_1));
+            chain[1] = caCert;
+
+            mKeyStore.setKeyEntry(TEST_ALIAS_1, privKey, null, chain);
+
+            Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+            assertNotNull("Retrieved entry should exist", actualEntry);
+
+            assertTrue("Retrieved entry should be of type PrivateKeyEntry",
+                    actualEntry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry;
+
+            assertPrivateKeyEntryEquals(actual, "RSA", FAKE_RSA_KEY_1, FAKE_RSA_USER_1,
+                    FAKE_RSA_CA_1);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static X509Certificate generateCertificate(android.security.KeyStore keyStore,
+            String alias, BigInteger serialNumber, X500Principal subjectDN, Date notBefore,
+            Date notAfter) throws Exception {
+        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+
+        KeyPair keyPair = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
+                keyStore, privateKeyAlias, KeyStore.UID_SELF);
+
+        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+        certGen.setPublicKey(keyPair.getPublic());
+        certGen.setSerialNumber(serialNumber);
+        certGen.setSubjectDN(subjectDN);
+        certGen.setIssuerDN(subjectDN);
+        certGen.setNotBefore(notBefore);
+        certGen.setNotAfter(notAfter);
+        certGen.setSignatureAlgorithm("sha1WithRSA");
+
+        final X509Certificate cert = certGen.generate(keyPair.getPrivate());
+
+        return cert;
+    }
+
+    public void testKeyStore_SetKeyEntry_ReplacedChain_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        // Create key #1
+        {
+            final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+            assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+
+            Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+
+            assertTrue(key instanceof PrivateKey);
+
+            PrivateKey expectedKey = (PrivateKey) key;
+
+            X509Certificate expectedCert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
+                    TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
+
+            assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                    expectedCert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+            Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+            assertTrue(entry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+            assertPrivateKeyEntryEquals(keyEntry, expectedKey, expectedCert, null);
+        }
+
+        // Replace key #1 with new chain
+        {
+            Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
+
+            assertTrue(key instanceof PrivateKey);
+
+            PrivateKey expectedKey = (PrivateKey) key;
+
+            X509Certificate expectedCert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
+                    TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
+
+            mKeyStore.setKeyEntry(TEST_ALIAS_1, expectedKey, null,
+                    new Certificate[] { expectedCert });
+
+            Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+            assertTrue(entry instanceof PrivateKeyEntry);
+
+            PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
+
+            assertPrivateKeyEntryEquals(keyEntry, expectedKey, expectedCert, null);
+        }
+    }
+
+    public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Encrypted_Failure()
+            throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        // Create key #1
+        {
+            final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+            assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+
+            X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
+                    TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
+
+            assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                    cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        }
+
+        // Create key #2
+        {
+            final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_2;
+            assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+
+            X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
+                    TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
+
+            assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_2,
+                    cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+        }
+
+        // Replace key #1 with key #2
+        {
+            Key key1 = mKeyStore.getKey(TEST_ALIAS_2, null);
+
+            X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
+                    TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
+
+            try {
+                mKeyStore.setKeyEntry(TEST_ALIAS_1, key1, null, new Certificate[] { cert });
+                fail("Should not allow setting of KeyEntry with wrong PrivaetKey");
+            } catch (KeyStoreException success) {
+            }
+        }
+    }
+
+    public void testKeyStore_SetKeyEntry_ReplacedChain_UnencryptedToEncrypted_Failure()
+            throws Exception {
+        mKeyStore.load(null, null);
+
+        // Create key #1
+        {
+            final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+            assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
+                    android.security.KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
+                    android.security.KeyStore.FLAG_NONE, null));
+
+            X509Certificate cert =
+                    generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1, TEST_DN_1,
+                            NOW, NOW_PLUS_10_YEARS);
+
+            assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                    cert.getEncoded(), android.security.KeyStore.UID_SELF,
+                    android.security.KeyStore.FLAG_NONE));
+        }
+
+        // Replace with one that requires encryption
+        {
+            Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null);
+
+            try {
+                mKeyStore.setEntry(TEST_ALIAS_1, entry,
+                        new KeyStoreParameter.Builder(getContext())
+                                .setEncryptionRequired(true)
+                                .build());
+                fail("Should not allow setting of Entry without unlocked keystore");
+            } catch (KeyStoreException success) {
+            }
+
+            assertTrue(mAndroidKeyStore.onUserPasswordChanged("1111"));
+            assertTrue(mAndroidKeyStore.isUnlocked());
+
+            mKeyStore.setEntry(TEST_ALIAS_1, entry,
+                    new KeyStoreParameter.Builder(getContext())
+                            .setEncryptionRequired(true)
+                            .build());
+        }
+    }
+
+    public void testKeyStore_Size_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+        assertAliases(new String[] { TEST_ALIAS_1 });
+
+        assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_RSA_CA_1,
+                KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+
+        assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+        assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
+
+        assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                null));
+
+        assertEquals("The keystore size should match expected", 3, mKeyStore.size());
+        assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2, TEST_ALIAS_3 });
+
+        assertTrue(mAndroidKeyStore.delete(Credentials.CA_CERTIFICATE + TEST_ALIAS_1));
+
+        assertEquals("The keystore size should match expected", 2, mKeyStore.size());
+        assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
+
+        assertTrue(mAndroidKeyStore.delete(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+
+        assertEquals("The keystore size should match expected", 1, mKeyStore.size());
+        assertAliases(new String[] { TEST_ALIAS_2 });
+    }
+
+    public void testKeyStore_Store_LoadStoreParam_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        try {
+            mKeyStore.store(null);
+            fail("Should throw UnsupportedOperationException when trying to store");
+        } catch (UnsupportedOperationException success) {
+        }
+    }
+
+    public void testKeyStore_Load_InputStreamSupplied_Encrypted_Failure() throws Exception {
+        byte[] buf = "FAKE KEYSTORE".getBytes();
+        ByteArrayInputStream is = new ByteArrayInputStream(buf);
+
+        try {
+            mKeyStore.load(is, null);
+            fail("Should throw IllegalArgumentException when InputStream is supplied");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyStore_Load_PasswordSupplied_Encrypted_Failure() throws Exception {
+        try {
+            mKeyStore.load(null, "password".toCharArray());
+            fail("Should throw IllegalArgumentException when password is supplied");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyStore_Store_OutputStream_Encrypted_Failure() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        OutputStream sink = new ByteArrayOutputStream();
+        try {
+            mKeyStore.store(sink, null);
+            fail("Should throw UnsupportedOperationException when trying to store");
+        } catch (UnsupportedOperationException success) {
+        }
+
+        try {
+            mKeyStore.store(sink, "blah".toCharArray());
+            fail("Should throw UnsupportedOperationException when trying to store");
+        } catch (UnsupportedOperationException success) {
+        }
+    }
+
+    private void setupKey() throws Exception {
+        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
+        assertTrue(mAndroidKeyStore
+                .generate(privateKeyAlias, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
+                        KeyStore.FLAG_ENCRYPTED, null));
+
+        X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1,
+                TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
+
+        assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1,
+                cert.getEncoded(), KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED));
+    }
+
+    public void testKeyStore_KeyOperations_Wrap_Encrypted_Success() throws Exception {
+        setupPassword();
+        mKeyStore.load(null, null);
+
+        setupKey();
+
+        // Test key usage
+        Entry e = mKeyStore.getEntry(TEST_ALIAS_1, null);
+        assertNotNull(e);
+        assertTrue(e instanceof PrivateKeyEntry);
+
+        PrivateKeyEntry privEntry = (PrivateKeyEntry) e;
+        PrivateKey privKey = privEntry.getPrivateKey();
+        assertNotNull(privKey);
+
+        PublicKey pubKey = privEntry.getCertificate().getPublicKey();
+
+        Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+        c.init(Cipher.WRAP_MODE, pubKey);
+
+        byte[] expectedKey = new byte[] {
+                0x00, 0x05, (byte) 0xAA, (byte) 0x0A5, (byte) 0xFF, 0x55, 0x0A
+        };
+
+        SecretKey expectedSecret = new SecretKeySpec(expectedKey, "AES");
+
+        byte[] wrappedExpected = c.wrap(expectedSecret);
+
+        c.init(Cipher.UNWRAP_MODE, privKey);
+        SecretKey actualSecret = (SecretKey) c.unwrap(wrappedExpected, "AES", Cipher.SECRET_KEY);
+
+        assertEquals(Arrays.toString(expectedSecret.getEncoded()),
+                Arrays.toString(actualSecret.getEncoded()));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 610eaff..9981972 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -191,10 +191,10 @@
             case DOZE:
             case DOZE_AOD:
                 mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
-                mDozeSensors.setListening(true);
                 if (oldState != DozeMachine.State.INITIALIZED) {
                     mDozeSensors.reregisterAllSensors();
                 }
+                mDozeSensors.setListening(true);
                 break;
             case DOZE_AOD_PAUSED:
             case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index a318efc..c454048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -100,4 +100,8 @@
             mAnimator = null;
         };
     };
+
+    public void showBackground() {
+        mBackground.showSecondLayer();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bb36725..dc81772 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -94,6 +94,13 @@
         if (savedInstanceState != null) {
             setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
             setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
+            int[] loc = new int[2];
+            View edit = view.findViewById(android.R.id.edit);
+            edit.getLocationInWindow(loc);
+            int x = loc[0] + edit.getWidth() / 2;
+            int y = loc[1] + edit.getHeight() / 2;
+            mQSCustomizer.setEditLocation(x, y);
+            mQSCustomizer.restoreInstanceState(savedInstanceState);
         }
     }
 
@@ -110,6 +117,7 @@
         super.onSaveInstanceState(outState);
         outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
         outState.putBoolean(EXTRA_LISTENING, mListening);
+        mQSCustomizer.saveInstanceState(outState);
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 30053e3..6c95a80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -20,6 +20,9 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.support.v7.widget.DefaultItemAnimator;
@@ -42,6 +45,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.QSContainerImpl;
 import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -60,6 +64,7 @@
 public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener {
 
     private static final int MENU_RESET = Menu.FIRST;
+    private static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
 
     private final QSDetailClipper mClipper;
 
@@ -109,11 +114,16 @@
         DefaultItemAnimator animator = new DefaultItemAnimator();
         animator.setMoveDuration(TileAdapter.MOVE_DURATION);
         mRecyclerView.setItemAnimator(animator);
+        updateNavBackDrop(getResources().getConfiguration());
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        updateNavBackDrop(newConfig);
+    }
+
+    private void updateNavBackDrop(Configuration newConfig) {
         View navBackdrop = findViewById(R.id.nav_bar_background);
         if (navBackdrop != null) {
             boolean shouldShow = newConfig.smallestScreenWidthDp >= 600
@@ -154,6 +164,21 @@
         }
     }
 
+
+    public void showImmediately() {
+        if (!isShown) {
+            setVisibility(VISIBLE);
+            mClipper.showBackground();
+            isShown = true;
+            setTileSpecs();
+            setCustomizing(true);
+            queryTiles();
+            mNotifQsContainer.setCustomizerAnimating(false);
+            mNotifQsContainer.setCustomizerShowing(true);
+            Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
+        }
+    }
+
     private void queryTiles() {
         mFinishedFetchingTiles = false;
         Runnable tileQueryFetchCompletion = () -> {
@@ -227,6 +252,35 @@
         }
     }
 
+
+    public void saveInstanceState(Bundle outState) {
+        if (isShown) {
+            Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
+        }
+        outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing);
+    }
+
+    public void restoreInstanceState(Bundle savedInstanceState) {
+        boolean customizing = savedInstanceState.getBoolean(EXTRA_QS_CUSTOMIZING);
+        if (customizing) {
+            setVisibility(VISIBLE);
+            addOnLayoutChangeListener(new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft,
+                        int oldTop, int oldRight, int oldBottom) {
+                    removeOnLayoutChangeListener(this);
+                    showImmediately();
+                }
+            });
+        }
+    }
+
+    public void setEditLocation(int x, int y) {
+        mX = x;
+        mY = y;
+    }
+
     private final Callback mKeyguardCallback = () -> {
         if (!isAttachedToWindow()) return;
         if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index c891418..92f26d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -116,7 +116,7 @@
     }
 
     @Override
-    protected boolean transformScale() {
+    protected boolean transformScale(TransformState otherState) {
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
index fb4e2ec..c4aabe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification;
 
 import android.text.Layout;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.Pools;
 import android.view.View;
@@ -50,12 +51,69 @@
                 int ownEllipsized = getEllipsisCount();
                 int otherEllipsized = otherTvs.getEllipsisCount();
                 return ownEllipsized == otherEllipsized
-                        && getInnerHeight(mText) == getInnerHeight(otherTvs.mText);
+                        && mText.getLineCount() == otherTvs.mText.getLineCount()
+                        && hasSameSpans(otherTvs);
             }
         }
         return false;
     }
 
+    private boolean hasSameSpans(TextViewTransformState otherTvs) {
+        boolean hasSpans = mText instanceof Spanned;
+        boolean otherHasSpans = otherTvs.mText instanceof Spanned;
+        if (hasSpans != otherHasSpans) {
+            return false;
+        } else if (!hasSpans) {
+            return true;
+        }
+        // Actually both have spans, let's try to compare them
+        Spanned ownSpanned = (Spanned) mText;
+        Object[] spans = ownSpanned.getSpans(0, ownSpanned.length(), Object.class);
+        Spanned otherSpanned = (Spanned) otherTvs.mText;
+        Object[] otherSpans = otherSpanned.getSpans(0, otherSpanned.length(), Object.class);
+        if (spans.length != otherSpans.length) {
+            return false;
+        }
+        for (int i = 0; i < spans.length; i++) {
+            Object span = spans[i];
+            Object otherSpan = otherSpans[i];
+            if (!span.getClass().equals(otherSpan.getClass())) {
+                return false;
+            }
+            if (ownSpanned.getSpanStart(span) != otherSpanned.getSpanStart(otherSpan)
+                    || ownSpanned.getSpanEnd(span) != otherSpanned.getSpanEnd(otherSpan)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean transformScale(TransformState otherState) {
+        if (!(otherState instanceof TextViewTransformState)) {
+            return false;
+        }
+        TextViewTransformState otherTvs = (TextViewTransformState) otherState;
+        int lineCount = mText.getLineCount();
+        return lineCount == 1 && lineCount == otherTvs.mText.getLineCount()
+                && getEllipsisCount() == otherTvs.getEllipsisCount()
+                && getViewHeight() != otherTvs.getViewHeight();
+    }
+
+    @Override
+    protected int getViewWidth() {
+        Layout l = mText.getLayout();
+        if (l != null) {
+            return (int) l.getLineWidth(0);
+        }
+        return super.getViewWidth();
+    }
+
+    @Override
+    protected int getViewHeight() {
+        return mText.getLineHeight();
+    }
+
     private int getInnerHeight(TextView text) {
         return text.getHeight() - text.getPaddingTop() - text.getPaddingBottom();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index a77dd4e..bafedff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -108,7 +108,7 @@
         final View transformedView = mTransformedView;
         boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
         boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
-        boolean transformScale = transformScale();
+        boolean transformScale = transformScale(otherState);
         // lets animate the positions correctly
         if (transformationAmount == 0.0f
                 || transformX && getTransformationStartX() == UNDEFINED
@@ -132,16 +132,16 @@
                 }
                 // we also want to animate the scale if we're the same
                 View otherView = otherState.getTransformedView();
-                if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
-                    setTransformationStartScaleX(otherView.getWidth() * otherView.getScaleX()
-                            / (float) transformedView.getWidth());
+                if (transformScale && otherState.getViewWidth() != getViewWidth()) {
+                    setTransformationStartScaleX(otherState.getViewWidth() * otherView.getScaleX()
+                            / (float) getViewWidth());
                     transformedView.setPivotX(0);
                 } else {
                     setTransformationStartScaleX(UNDEFINED);
                 }
-                if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
-                    setTransformationStartScaleY(otherView.getHeight() * otherView.getScaleY()
-                            / (float) transformedView.getHeight());
+                if (transformScale && otherState.getViewHeight() != getViewHeight()) {
+                    setTransformationStartScaleY(otherState.getViewHeight() * otherView.getScaleY()
+                            / (float) getViewHeight());
                     transformedView.setPivotY(0);
                 } else {
                     setTransformationStartScaleY(UNDEFINED);
@@ -205,7 +205,15 @@
         }
     }
 
-    protected boolean transformScale() {
+    protected int getViewWidth() {
+        return mTransformedView.getWidth();
+    }
+
+    protected int getViewHeight() {
+        return mTransformedView.getHeight();
+    }
+
+    protected boolean transformScale(TransformState otherState) {
         return false;
     }
 
@@ -259,7 +267,7 @@
         final View transformedView = mTransformedView;
         boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
         boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
-        boolean transformScale = transformScale();
+        boolean transformScale = transformScale(otherState);
         // lets animate the positions correctly
         if (transformationAmount == 0.0f) {
             if (transformX) {
@@ -275,13 +283,13 @@
                 setTransformationStartY(start);
             }
             View otherView = otherState.getTransformedView();
-            if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
+            if (transformScale && otherState.getViewWidth() != getViewWidth()) {
                 setTransformationStartScaleX(transformedView.getScaleX());
                 transformedView.setPivotX(0);
             } else {
                 setTransformationStartScaleX(UNDEFINED);
             }
-            if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
+            if (transformScale && otherState.getViewHeight() != getViewHeight()) {
                 setTransformationStartScaleY(transformedView.getScaleY());
                 transformedView.setPivotY(0);
             } else {
@@ -333,14 +341,14 @@
             if (transformationStartScaleX != UNDEFINED) {
                 transformedView.setScaleX(
                         NotificationUtils.interpolate(transformationStartScaleX,
-                                (otherView.getWidth() / (float) transformedView.getWidth()),
+                                (otherState.getViewWidth() / (float) getViewWidth()),
                                 interpolatedValue));
             }
             float transformationStartScaleY = getTransformationStartScaleY();
             if (transformationStartScaleY != UNDEFINED) {
                 transformedView.setScaleY(
                         NotificationUtils.interpolate(transformationStartScaleY,
-                                (otherView.getHeight() / (float) transformedView.getHeight()),
+                                (otherState.getViewHeight() / (float) getViewHeight()),
                                 interpolatedValue));
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 33d92fc..bcba789 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3487,6 +3487,11 @@
             pw.print  ("      ");
             mNotificationPanel.dump(fd, pw, args);
         }
+        pw.println("  mStackScroller: ");
+        if (mStackScroller != null) {
+            pw.print  ("      ");
+            mStackScroller.dump(fd, pw, args);
+        }
 
         DozeLog.dump(pw);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 41cde9c..2981b39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -91,6 +91,8 @@
 
 import android.support.v4.graphics.ColorUtils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -4257,6 +4259,19 @@
         mShelf.setDarkOffsetX(shelfOffsetX);
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+                        + " alpha:%f scrollY:%d]",
+                this.getClass().getSimpleName(),
+                mPulsing != null ?"T":"f",
+                mAmbientState.isQsCustomizerShowing() ? "T":"f",
+                getVisibility() == View.VISIBLE ? "visible"
+                        : getVisibility() == View.GONE ? "gone"
+                                : "invisible",
+                getAlpha(),
+                mAmbientState.getScrollY()));
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */