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