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