1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts.deviceowner; 17 18 import static com.android.cts.deviceowner.BaseDeviceOwnerTest.getWho; 19 import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1; 20 21 import android.app.Activity; 22 import android.app.admin.DevicePolicyManager; 23 import android.net.Uri; 24 import android.security.KeyChain; 25 import android.security.KeyChainAliasCallback; 26 import android.security.KeyChainException; 27 import android.test.ActivityInstrumentationTestCase2; 28 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.DataInputStream; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.UnsupportedEncodingException; 35 import java.net.URLEncoder; 36 import java.security.cert.CertificateException; 37 import java.security.cert.CertificateFactory; 38 import java.security.cert.X509Certificate; 39 import java.security.cert.Certificate; 40 import java.security.KeyFactory; 41 import java.security.NoSuchAlgorithmException; 42 import java.security.PrivateKey; 43 import java.security.spec.InvalidKeySpecException; 44 import java.security.spec.PKCS8EncodedKeySpec; 45 import java.util.Arrays; 46 import java.util.Collection; 47 import java.util.concurrent.CountDownLatch; 48 import java.util.concurrent.TimeUnit; 49 50 import android.content.ComponentName; 51 import android.content.Context; 52 import android.content.res.AssetManager; 53 54 public class KeyManagementTest extends ActivityInstrumentationTestCase2<KeyManagementActivity> { 55 56 private static final long KEYCHAIN_TIMEOUT_MINS = 6; 57 private DevicePolicyManager mDevicePolicyManager; 58 KeyManagementTest()59 public KeyManagementTest() { 60 super(KeyManagementActivity.class); 61 } 62 63 @Override setUp()64 protected void setUp() throws Exception { 65 super.setUp(); 66 67 // Confirm our DeviceOwner is set up 68 mDevicePolicyManager = (DevicePolicyManager) 69 getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); 70 BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager); 71 72 // Enable credential storage by setting a nonempty password. 73 assertTrue(mDevicePolicyManager.resetPassword("test", 0)); 74 } 75 76 @Override tearDown()77 protected void tearDown() throws Exception { 78 // Delete all keys by resetting our password to null, which clears the keystore. 79 mDevicePolicyManager.setPasswordQuality(getWho(), 80 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); 81 mDevicePolicyManager.setPasswordMinimumLength(getWho(), 0); 82 assertTrue(mDevicePolicyManager.resetPassword("", 0)); 83 super.tearDown(); 84 } 85 testCanInstallAndRemoveValidRsaKeypair()86 public void testCanInstallAndRemoveValidRsaKeypair() throws Exception { 87 final String alias = "com.android.test.valid-rsa-key-1"; 88 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA"); 89 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 90 91 // Install keypair. 92 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias)); 93 try { 94 // Request and retrieve using the alias. 95 assertGranted(alias, false); 96 assertEquals(alias, new KeyChainAliasFuture(alias).get()); 97 assertGranted(alias, true); 98 99 // Verify key is at least something like the one we put in. 100 assertEquals(KeyChain.getPrivateKey(getActivity(), alias).getAlgorithm(), "RSA"); 101 } finally { 102 // Delete regardless of whether the test succeeded. 103 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias)); 104 } 105 // Verify alias is actually deleted. 106 assertGranted(alias, false); 107 } 108 testCanInstallWithAutomaticAccess()109 public void testCanInstallWithAutomaticAccess() throws Exception { 110 final String grant = "com.android.test.autogrant-key-1"; 111 final String withhold = "com.android.test.nongrant-key-1"; 112 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA"); 113 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 114 115 // Install keypairs. 116 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert}, 117 grant, true)); 118 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert}, 119 withhold, false)); 120 try { 121 // Verify only the requested key was actually granted. 122 assertGranted(grant, true); 123 assertGranted(withhold, false); 124 125 // Verify the granted key is actually obtainable in PrivateKey form. 126 assertEquals(KeyChain.getPrivateKey(getActivity(), grant).getAlgorithm(), "RSA"); 127 } finally { 128 // Delete both keypairs. 129 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), grant)); 130 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), withhold)); 131 } 132 // Verify they're actually gone. 133 assertGranted(grant, false); 134 assertGranted(withhold, false); 135 } 136 testCanInstallCertChain()137 public void testCanInstallCertChain() throws Exception { 138 // Use assets/generate-client-cert-chain.sh to regenerate the client cert chain. 139 final PrivateKey privKey = loadPrivateKeyFromAsset("user-cert-chain.key"); 140 final Collection<Certificate> certs = loadCertificatesFromAsset("user-cert-chain.crt"); 141 final Certificate[] certChain = certs.toArray(new Certificate[certs.size()]); 142 final String alias = "com.android.test.clientkeychain"; 143 // Some sanity check on the cert chain 144 assertTrue(certs.size() > 1); 145 for (int i = 1; i < certs.size(); i++) { 146 certChain[i - 1].verify(certChain[i].getPublicKey()); 147 } 148 149 // Install keypairs. 150 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, certChain, alias, true)); 151 try { 152 // Verify only the requested key was actually granted. 153 assertGranted(alias, true); 154 155 // Verify the granted key is actually obtainable in PrivateKey form. 156 assertEquals(KeyChain.getPrivateKey(getActivity(), alias).getAlgorithm(), "RSA"); 157 158 // Verify the certificate chain is correct 159 X509Certificate[] returnedCerts = KeyChain.getCertificateChain(getActivity(), alias); 160 assertTrue(Arrays.equals(certChain, returnedCerts)); 161 } finally { 162 // Delete both keypairs. 163 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias)); 164 } 165 // Verify they're actually gone. 166 assertGranted(alias, false); 167 } 168 testGrantsDoNotPersistBetweenInstallations()169 public void testGrantsDoNotPersistBetweenInstallations() throws Exception { 170 final String alias = "com.android.test.persistent-key-1"; 171 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA"); 172 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 173 174 // Install keypair. 175 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert}, 176 alias, true)); 177 try { 178 assertGranted(alias, true); 179 } finally { 180 // Delete and verify. 181 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias)); 182 } 183 assertGranted(alias, false); 184 185 // Install again. 186 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert}, 187 alias, false)); 188 try { 189 assertGranted(alias, false); 190 } finally { 191 // Delete. 192 assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias)); 193 } 194 } 195 testNullKeyParamsFailPredictably()196 public void testNullKeyParamsFailPredictably() throws Exception { 197 final String alias = "com.android.test.null-key-1"; 198 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA"); 199 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 200 try { 201 mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias); 202 fail("Exception should have been thrown for null PrivateKey"); 203 } catch (NullPointerException expected) { 204 } 205 try { 206 mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias); 207 fail("Exception should have been thrown for null Certificate"); 208 } catch (NullPointerException expected) { 209 } 210 } 211 testNullAdminComponentIsDenied()212 public void testNullAdminComponentIsDenied() throws Exception { 213 final String alias = "com.android.test.null-admin-1"; 214 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA"); 215 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 216 try { 217 mDevicePolicyManager.installKeyPair(null, privKey, cert, alias); 218 fail("Exception should have been thrown for null ComponentName"); 219 } catch (SecurityException expected) { 220 } 221 } 222 assertGranted(String alias, boolean expected)223 private void assertGranted(String alias, boolean expected) throws InterruptedException { 224 boolean granted = false; 225 try { 226 granted = (KeyChain.getPrivateKey(getActivity(), alias) != null); 227 } catch (KeyChainException e) { 228 } 229 assertEquals("Grant for alias: \"" + alias + "\"", expected, granted); 230 } 231 getPrivateKey(final byte[] key, String type)232 private static PrivateKey getPrivateKey(final byte[] key, String type) 233 throws NoSuchAlgorithmException, InvalidKeySpecException { 234 return KeyFactory.getInstance(type).generatePrivate( 235 new PKCS8EncodedKeySpec(key)); 236 } 237 getCertificate(byte[] cert)238 private static Certificate getCertificate(byte[] cert) throws CertificateException { 239 return CertificateFactory.getInstance("X.509").generateCertificate( 240 new ByteArrayInputStream(cert)); 241 } 242 loadCertificatesFromAsset(String assetName)243 private Collection<Certificate> loadCertificatesFromAsset(String assetName) { 244 try { 245 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 246 AssetManager am = getActivity().getAssets(); 247 InputStream is = am.open(assetName); 248 return (Collection<Certificate>) certFactory.generateCertificates(is); 249 } catch (IOException | CertificateException e) { 250 e.printStackTrace(); 251 } 252 return null; 253 } 254 loadPrivateKeyFromAsset(String assetName)255 private PrivateKey loadPrivateKeyFromAsset(String assetName) { 256 try { 257 AssetManager am = getActivity().getAssets(); 258 InputStream is = am.open(assetName); 259 ByteArrayOutputStream output = new ByteArrayOutputStream(); 260 int length; 261 byte[] buffer = new byte[4096]; 262 while ((length = is.read(buffer, 0, buffer.length)) != -1) { 263 output.write(buffer, 0, length); 264 } 265 return getPrivateKey(output.toByteArray(), "RSA"); 266 } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { 267 e.printStackTrace(); 268 } 269 return null; 270 } 271 272 private class KeyChainAliasFuture implements KeyChainAliasCallback { 273 private final CountDownLatch mLatch = new CountDownLatch(1); 274 private String mChosenAlias = null; 275 276 @Override alias(final String chosenAlias)277 public void alias(final String chosenAlias) { 278 mChosenAlias = chosenAlias; 279 mLatch.countDown(); 280 } 281 KeyChainAliasFuture(String alias)282 public KeyChainAliasFuture(String alias) throws UnsupportedEncodingException { 283 /* Pass the alias as a GET to an imaginary server instead of explicitly asking for it, 284 * to make sure the DPC actually has to do some work to grant the cert. 285 */ 286 final Uri uri = 287 Uri.parse("https://example.org/?alias=" + URLEncoder.encode(alias, "UTF-8")); 288 KeyChain.choosePrivateKeyAlias(getActivity(), this, 289 null /* keyTypes */, null /* issuers */, uri, null /* alias */); 290 } 291 get()292 public String get() throws InterruptedException { 293 assertTrue("Chooser timeout", mLatch.await(KEYCHAIN_TIMEOUT_MINS, TimeUnit.MINUTES)); 294 return mChosenAlias; 295 } 296 } 297 } 298