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