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