1 /*
2  * Copyright (C) 2015 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 
17 package com.android.cts.deviceandprofileowner;
18 
19 import android.app.KeyguardManager;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.security.KeyChain;
26 import android.security.KeyChainException;
27 import android.test.AndroidTestCase;
28 import android.util.Base64;
29 import android.util.Base64InputStream;
30 
31 import java.io.ByteArrayInputStream;
32 import java.security.KeyFactory;
33 import java.security.NoSuchAlgorithmException;
34 import java.security.PrivateKey;
35 import java.security.cert.Certificate;
36 import java.security.cert.CertificateException;
37 import java.security.cert.CertificateFactory;
38 import java.security.spec.InvalidKeySpecException;
39 import java.security.spec.PKCS8EncodedKeySpec;
40 import java.util.concurrent.Semaphore;
41 import java.util.concurrent.TimeUnit;
42 
43 /**
44  * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app
45  * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various
46  * cert-related APIs. The expected certificate changes are validated both remotely and locally.
47  */
48 public class DelegatedCertInstallerTest extends BaseDeviceAdminTest {
49 
50     private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
51 
52     private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert";
53     private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert";
54     private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert";
55     private static final String ACTION_INSTALL_KEYPAIR =
56             "com.android.cts.certinstaller.install_keypair";
57     private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
58 
59     private static final String EXTRA_CERT_DATA = "extra_cert_data";
60     private static final String EXTRA_KEY_DATA = "extra_key_data";
61     private static final String EXTRA_KEY_ALIAS = "extra_key_alias";
62     private static final String EXTRA_RESULT_VALUE = "extra_result_value";
63     private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception";
64 
65     /*
66      * The CA and keypair below are generated with:
67      *
68      * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
69      * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
70      * mkdir -p demoCA/newcerts
71      * touch demoCA/index.txt
72      * echo "01" > demoCA/serial
73      * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
74      */
75 
76      // Content from cacert.pem
77     private static final String TEST_CA =
78             "-----BEGIN CERTIFICATE-----\n" +
79             "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
80             "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
81             "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
82             "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
83             "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
84             "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
85             "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
86             "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
87             "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
88             "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
89             "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
90             "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
91             "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
92             "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
93             "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
94             "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
95             "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
96             "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
97             "wQ==\n" +
98             "-----END CERTIFICATE-----";
99     // Content from userkey.pem without the private key header and footer.
100     private static final String TEST_KEY =
101             "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCYprGsTU+5L3KM\n" +
102             "fhkm0gXM2xjGUH+543YLiMPGVr3eVS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqU\n" +
103             "mhegxG4s3IvGYVB0KZoRIjDKmnnvlx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T\n" +
104             "9sV6zW2Wzri+f5mvzKjhnArbLktHAgMBAAECgYBlfVVPhtZnmuXJzzQpAEZzTugb\n" +
105             "tN1OimZO0RIocTQoqj4KT+HkiJOLGFQPwbtFpMre+q4SRqNpM/oZnI1yRtKcCmIc\n" +
106             "mZgkwJ2k6pdSxqO0ofxFFTdT9czJ3rCnqBHy1g6BqUQFXT4olcygkxUpKYUwzlz1\n" +
107             "oAl487CoPxyr4sVEAQJBANwiUOHcdGd2RoRILDzw5WOXWBoWPOKzX/K9wt0yL+mO\n" +
108             "wlFNFSymqo9eLheHcEq/VD9qK9rT700dCewJfWj6+bECQQDNXmWNYIxGii5NJilT\n" +
109             "OBOHiMD/F0NE178j+/kmacbhDJwpkbLYXaP8rW4+Iswrm4ORJ59lvjNuXaZ28+sx\n" +
110             "fFp3AkA6Z7Bl/IO135+eATgbgx6ZadIqObQ1wbm3Qbmtzl7/7KyJvZXcnuup1icM\n" +
111             "fxa//jtwB89S4+Ad6ZJ0WaA4dj5BAkEAuG7V9KmIULE388EZy8rIfyepa22Q0/qN\n" +
112             "hdt8XasRGHsio5Jdc0JlSz7ViqflhCQde/aBh/XQaoVgQeO8jKyI8QJBAJHekZDj\n" +
113             "WA0w1RsBVVReN1dVXgjm1CykeAT8Qx8TUmBUfiDX6w6+eGQjKtS7f4KC2IdRTV6+\n" +
114             "bDzDoHBChHNC9ms=\n";
115 
116     // Content from usercert.pem without the header and footer.
117     private static final String TEST_CERT =
118             "MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET\n" +
119             "MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ\n" +
120             "dHkgTHRkMB4XDTE1MDUwMTE2NTQwNVoXDTI1MDQyODE2NTQwNVowWzELMAkGA1UE\n" +
121             "BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp\n" +
122             "ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY2xpZW50IGNlcnQwgZ8wDQYJKoZIhvcN\n" +
123             "AQEBBQADgY0AMIGJAoGBALCYprGsTU+5L3KMfhkm0gXM2xjGUH+543YLiMPGVr3e\n" +
124             "VS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqUmhegxG4s3IvGYVB0KZoRIjDKmnnv\n" +
125             "lx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T9sV6zW2Wzri+f5mvzKjhnArbLktH\n" +
126             "AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu\n" +
127             "ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ8GL+jKSarvTn9fVNA2AzjY7qq\n" +
128             "gjAfBgNVHSMEGDAWgBRzBBA5sNWyT/fK8GrhN3tOqO5tgjANBgkqhkiG9w0BAQsF\n" +
129             "AAOCAQEAgwQEd2bktIDZZi/UOwU1jJUgGq7NiuBDPHcqgzjxhGFLQ8SQAAP3v3PR\n" +
130             "mLzcfxsxnzGynqN5iHQT4rYXxxaqrp1iIdj9xl9Wl5FxjZgXITxhlRscOd/UOBvG\n" +
131             "oMrazVczjjdoRIFFnjtU3Jf0Mich68HD1Z0S3o7X6sDYh6FTVR5KbLcxbk6RcoG4\n" +
132             "VCI5boR5LUXgb5Ed5UxczxvN12S71fyxHYVpuuI0z0HTIbAxKeRw43I6HWOmR1/0\n" +
133             "G6byGCNL/1Fz7Y+264fGqABSNTKdZwIU2K4ANEH7F+9scnhoO6OBp+gjBe5O+7jb\n" +
134             "wZmUCAoTka4hmoaOCj7cqt/IkmxozQ==\n";
135 
136     private DevicePolicyManager mDpm;
137     private volatile boolean mReceivedResult;
138     private volatile Exception mReceivedException;
139     private Semaphore mAvailableResultSemaphore;
140 
141     private final BroadcastReceiver receiver = new BroadcastReceiver() {
142         @Override
143         public void onReceive(Context context, Intent intent) {
144             if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) {
145                 synchronized (DelegatedCertInstallerTest.this) {
146                     mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false);
147                     mReceivedException =
148                             (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION);
149                     mAvailableResultSemaphore.release();
150                 }
151             }
152         }
153     };
154 
155     @Override
setUp()156     public void setUp() throws Exception {
157         super.setUp();
158         mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
159         mAvailableResultSemaphore = new Semaphore(0);
160         mReceivedResult = false;
161         mReceivedException = null;
162         IntentFilter filter = new IntentFilter();
163         filter.addAction(ACTION_CERT_OPERATION_DONE);
164         mContext.registerReceiver(receiver, filter);
165     }
166 
167     @Override
tearDown()168     public void tearDown() throws Exception {
169         mContext.unregisterReceiver(receiver);
170         mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
171         // Installed private key pair will be removed once the lockscreen password is cleared,
172         // which is done in the hostside test.
173         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
174         super.tearDown();
175     }
176 
testCaCertsOperations()177     public void testCaCertsOperations() throws InterruptedException {
178         byte[] cert = TEST_CA.getBytes();
179 
180         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
181         assertEquals(CERT_INSTALLER_PACKAGE,
182                 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
183 
184         // Exercise installCaCert()
185         installCaCert(cert);
186         assertResult("installCaCert", true);
187         assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled(
188                 ADMIN_RECEIVER_COMPONENT, cert));
189 
190         // Exercise getInstalledCaCerts()
191         verifyCaCert(cert);
192         assertResult("getInstalledCaCerts()", true);
193 
194         // Exercise uninstallCaCert()
195         removeCaCert(cert);
196         assertResult("uninstallCaCert()", true);
197         assertFalse("Certificate is not removed properly", mDpm.hasCaCertInstalled(
198                 ADMIN_RECEIVER_COMPONENT, cert));
199 
200         // Clear delegated cert installer.
201         // Tests after this are expected to fail.
202         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
203 
204         installCaCert(cert);
205         assertResult("installCaCert", false);
206     }
207 
testInstallKeyPair()208     public void testInstallKeyPair() throws InterruptedException, KeyChainException {
209         final String alias = "delegated-cert-installer-test-key";
210 
211         // Clear delegated cert installer.
212         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
213         // The app is not the cert installer , it shouldn't have have privilege to call
214         // installKeyPair().
215         installKeyPair(TEST_KEY, TEST_CERT, alias);
216         assertResult("installKeyPair", false);
217 
218         // Set the app to be cert installer.
219         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
220         assertEquals(CERT_INSTALLER_PACKAGE,
221                 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
222 
223         // Exercise installKeyPair()
224         checkKeyguardPrecondition();
225         installKeyPair(TEST_KEY, TEST_CERT, alias);
226         assertResult("installKeyPair", true);
227     }
228 
229     /**
230      * installKeyPair() requires the system to have a lockscreen password, which should have been
231      * set by the host side test.
232      */
checkKeyguardPrecondition()233     private void checkKeyguardPrecondition() throws InterruptedException {
234         KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
235         if (!km.isKeyguardSecure()) {
236             Thread.sleep(5000);
237           }
238           assertTrue("A lockscreen password is required before keypair can be installed",
239                           km.isKeyguardSecure());
240     }
241 
installCaCert(byte[] cert)242     private void installCaCert(byte[] cert) {
243         Intent intent = new Intent();
244         intent.setAction(ACTION_INSTALL_CERT);
245         intent.putExtra(EXTRA_CERT_DATA, cert);
246         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
247         mContext.startActivity(intent);
248     }
249 
removeCaCert(byte[] cert)250     private void removeCaCert(byte[] cert) {
251         Intent intent = new Intent();
252         intent.setAction(ACTION_REMOVE_CERT);
253         intent.putExtra(EXTRA_CERT_DATA, cert);
254         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
255         mContext.startActivity(intent);
256     }
257 
verifyCaCert(byte[] cert)258     private void verifyCaCert(byte[] cert) {
259         Intent intent = new Intent();
260         intent.setAction(ACTION_VERIFY_CERT);
261         intent.putExtra(EXTRA_CERT_DATA, cert);
262         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
263         mContext.startActivity(intent);
264     }
265 
assertResult(String testName, Boolean expectSuccess)266     private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
267         assertTrue("Cert installer did not respond in time.",
268                 mAvailableResultSemaphore.tryAcquire(5, TimeUnit.SECONDS));
269         synchronized (this) {
270             if (expectSuccess) {
271                 assertTrue(testName + " failed unexpectedly.", mReceivedResult);
272                 assertNull(testName + " raised exception", mReceivedException);
273             } else {
274                 assertFalse(testName + " succeeded unexpectedly.", mReceivedResult);
275                 assertTrue(testName + " did not raise SecurityException",
276                         mReceivedException != null &&
277                         mReceivedException instanceof SecurityException);
278             }
279         }
280     }
281 
installKeyPair(String key, String cert, String alias)282     private void installKeyPair(String key, String cert, String alias) {
283         Intent intent = new Intent();
284         intent.setAction(ACTION_INSTALL_KEYPAIR);
285         intent.putExtra(EXTRA_CERT_DATA, cert);
286         intent.putExtra(EXTRA_KEY_DATA, key);
287         intent.putExtra(EXTRA_KEY_ALIAS, alias);
288         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
289         mContext.startActivity(intent);
290     }
291 }
292