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.os.Handler; 25 import android.security.KeyChain; 26 import android.security.KeyChainAliasCallback; 27 import android.security.KeyChainException; 28 import android.test.ActivityInstrumentationTestCase2; 29 30 import java.io.ByteArrayInputStream; 31 import java.io.UnsupportedEncodingException; 32 import java.net.URLEncoder; 33 import java.security.cert.CertificateException; 34 import java.security.cert.CertificateFactory; 35 import java.security.cert.Certificate; 36 import java.security.KeyFactory; 37 import java.security.NoSuchAlgorithmException; 38 import java.security.PrivateKey; 39 import java.security.spec.InvalidKeySpecException; 40 import java.security.spec.PKCS8EncodedKeySpec; 41 import java.util.concurrent.CountDownLatch; 42 import java.util.concurrent.TimeUnit; 43 44 import android.content.ComponentName; 45 import android.content.Context; 46 47 public class KeyManagementTest extends 48 ActivityInstrumentationTestCase2<KeyManagementActivity> { 49 50 private static final int KEYCHAIN_TIMEOUT_MS = 8000; 51 private DevicePolicyManager mDevicePolicyManager; 52 KeyManagementTest()53 public KeyManagementTest() { 54 super(KeyManagementActivity.class); 55 } 56 57 @Override setUp()58 protected void setUp() throws Exception { 59 super.setUp(); 60 61 // Confirm our DeviceOwner is set up 62 mDevicePolicyManager = (DevicePolicyManager) 63 getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); 64 BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager); 65 66 // Enable credential storage by setting a nonempty password. 67 assertTrue(mDevicePolicyManager.resetPassword("test", 0)); 68 } 69 70 @Override tearDown()71 protected void tearDown() throws Exception { 72 // Delete all keys by resetting our password to null, which clears the keystore. 73 mDevicePolicyManager.setPasswordQuality(getWho(), 74 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); 75 mDevicePolicyManager.setPasswordMinimumLength(getWho(), 0); 76 assertTrue(mDevicePolicyManager.resetPassword("", 0)); 77 super.tearDown(); 78 } 79 testCanInstallValidRsaKeypair()80 public void testCanInstallValidRsaKeypair() 81 throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException, 82 KeyChainException, InterruptedException, UnsupportedEncodingException { 83 final String alias = "com.android.test.valid-rsa-key-1"; 84 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA"); 85 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 86 assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias)); 87 88 assertEquals(alias, new KeyChainAliasFuture(alias).get()); 89 final PrivateKey retrievedKey = KeyChain.getPrivateKey(getActivity(), alias); 90 assertEquals(retrievedKey.getAlgorithm(), "RSA"); 91 } 92 testNullKeyParamsFailPredictably()93 public void testNullKeyParamsFailPredictably() 94 throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException { 95 final String alias = "com.android.test.null-key-1"; 96 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA"); 97 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 98 try { 99 mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias); 100 fail("Exception should have been thrown for null PrivateKey"); 101 } catch (NullPointerException expected) { 102 } 103 try { 104 mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias); 105 fail("Exception should have been thrown for null Certificate"); 106 } catch (NullPointerException expected) { 107 } 108 } 109 testNullAdminComponentIsDenied()110 public void testNullAdminComponentIsDenied() 111 throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException { 112 final String alias = "com.android.test.null-admin-1"; 113 final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA"); 114 final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate); 115 try { 116 mDevicePolicyManager.installKeyPair(null, privKey, cert, alias); 117 fail("Exception should have been thrown for null ComponentName"); 118 } catch (SecurityException expected) { 119 } 120 } 121 getPrivateKey(final byte[] key, String type)122 private static PrivateKey getPrivateKey(final byte[] key, String type) 123 throws NoSuchAlgorithmException, InvalidKeySpecException { 124 return KeyFactory.getInstance(type).generatePrivate( 125 new PKCS8EncodedKeySpec(key)); 126 } 127 getCertificate(byte[] cert)128 private static Certificate getCertificate(byte[] cert) throws CertificateException { 129 return CertificateFactory.getInstance("X.509").generateCertificate( 130 new ByteArrayInputStream(cert)); 131 } 132 133 private class KeyChainAliasFuture implements KeyChainAliasCallback { 134 private final CountDownLatch mLatch = new CountDownLatch(1); 135 private String mChosenAlias = null; 136 137 @Override alias(final String chosenAlias)138 public void alias(final String chosenAlias) { 139 mChosenAlias = chosenAlias; 140 mLatch.countDown(); 141 } 142 KeyChainAliasFuture(String alias)143 public KeyChainAliasFuture(String alias) throws UnsupportedEncodingException { 144 /* Pass the alias as a GET to an imaginary server instead of explicitly asking for it, 145 * to make sure the DPC actually has to do some work to grant the cert. 146 */ 147 final Uri uri = 148 Uri.parse("https://example.org/?alias=" + URLEncoder.encode(alias, "UTF-8")); 149 KeyChain.choosePrivateKeyAlias(getActivity(), this, 150 null /* keyTypes */, null /* issuers */, uri, null /* alias */); 151 } 152 get()153 public String get() throws InterruptedException { 154 assertTrue("Chooser timeout", mLatch.await(KEYCHAIN_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 155 return mChosenAlias; 156 } 157 }; 158 } 159