1 /* 2 * Copyright (C) 2011 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.keychain.tests; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.os.IBinder; 27 import android.security.Credentials; 28 import android.security.IKeyChainService; 29 import android.security.KeyStore; 30 import android.util.Log; 31 import com.android.keychain.tests.support.IKeyChainServiceTestSupport; 32 import java.security.KeyStore.PrivateKeyEntry; 33 import java.security.cert.Certificate; 34 import java.util.Arrays; 35 import junit.framework.Assert; 36 import libcore.java.security.TestKeyStore; 37 38 public class KeyChainServiceTest extends Service { 39 40 private static final String TAG = "KeyChainServiceTest"; 41 42 private final Object mSupportLock = new Object(); 43 private IKeyChainServiceTestSupport mSupport; 44 private boolean mIsBoundSupport; 45 46 private final Object mServiceLock = new Object(); 47 private IKeyChainService mService; 48 private boolean mIsBoundService; 49 50 private ServiceConnection mSupportConnection = new ServiceConnection() { 51 @Override public void onServiceConnected(ComponentName name, IBinder service) { 52 synchronized (mSupportLock) { 53 mSupport = IKeyChainServiceTestSupport.Stub.asInterface(service); 54 mSupportLock.notifyAll(); 55 } 56 } 57 58 @Override public void onServiceDisconnected(ComponentName name) { 59 synchronized (mSupportLock) { 60 mSupport = null; 61 } 62 } 63 }; 64 65 private ServiceConnection mServiceConnection = new ServiceConnection() { 66 @Override public void onServiceConnected(ComponentName name, IBinder service) { 67 synchronized (mServiceLock) { 68 mService = IKeyChainService.Stub.asInterface(service); 69 mServiceLock.notifyAll(); 70 } 71 } 72 73 @Override public void onServiceDisconnected(ComponentName name) { 74 synchronized (mServiceLock) { 75 mService = null; 76 } 77 } 78 }; 79 addComponentToIntent(PackageManager pm, Intent intent)80 private static void addComponentToIntent(PackageManager pm, Intent intent) { 81 ResolveInfo service = pm.resolveService(intent, 0); 82 if (service == null) { 83 Log.w(TAG, String.format("No service found for intent: %s", intent.getAction())); 84 } else { 85 Log.d(TAG, String.format("Found service: %s %s for action %s", 86 service.serviceInfo.packageName, service.serviceInfo.name, 87 intent.getAction())); 88 ComponentName comp = new ComponentName( 89 service.serviceInfo.packageName, service.serviceInfo.name); 90 intent.setComponent(comp); 91 } 92 } 93 bindSupport()94 private void bindSupport() { 95 Intent serviceIntent = new Intent(IKeyChainServiceTestSupport.class.getName()); 96 addComponentToIntent(getPackageManager(), serviceIntent); 97 mIsBoundSupport = bindService(serviceIntent, 98 mSupportConnection, 99 Context.BIND_AUTO_CREATE); 100 Log.d(TAG, String.format("Finished bindSupport with result: %b", mIsBoundSupport)); 101 } 102 bindService()103 private void bindService() { 104 Intent serviceIntent = new Intent(IKeyChainService.class.getName()); 105 addComponentToIntent(getPackageManager(), serviceIntent); 106 mIsBoundService = bindService(serviceIntent, 107 mServiceConnection, 108 Context.BIND_AUTO_CREATE); 109 Log.d(TAG, String.format("Finished bindService with result: %b", mIsBoundService)); 110 } 111 unbindServices()112 private void unbindServices() { 113 if (mIsBoundSupport) { 114 unbindService(mSupportConnection); 115 mIsBoundSupport = false; 116 } 117 if (mIsBoundService) { 118 unbindService(mServiceConnection); 119 mIsBoundService = false; 120 } 121 } 122 onBind(Intent intent)123 @Override public IBinder onBind(Intent intent) { 124 Log.d(TAG, "onBind"); 125 return null; 126 } 127 onStartCommand(Intent intent, int flags, int startId)128 @Override public int onStartCommand(Intent intent, int flags, int startId) { 129 Log.d(TAG, "onStartCommand"); 130 new Thread(new Test(), TAG).start(); 131 return START_STICKY; 132 } 133 onDestroy()134 @Override public void onDestroy () { 135 Log.d(TAG, "onDestroy"); 136 unbindServices(); 137 } 138 139 private final class Test extends Assert implements Runnable { 140 run()141 @Override public void run() { 142 try { 143 test_KeyChainService(); 144 } catch (RuntimeException e) { 145 // rethrow RuntimeException without wrapping 146 throw e; 147 } catch (Exception e) { 148 throw new RuntimeException(e); 149 } finally { 150 stopSelf(); 151 } 152 } 153 test_KeyChainService()154 public void test_KeyChainService() throws Exception { 155 Log.d(TAG, "test_KeyChainService uid=" + getApplicationInfo().uid); 156 157 Log.d(TAG, "test_KeyChainService bind support"); 158 bindSupport(); 159 assertTrue(mIsBoundSupport); 160 synchronized (mSupportLock) { 161 if (mSupport == null) { 162 mSupportLock.wait(10 * 1000); 163 } 164 } 165 assertNotNull(mSupport); 166 167 Log.d(TAG, "test_KeyChainService setup keystore"); 168 KeyStore keyStore = KeyStore.getInstance(); 169 assertTrue(mSupport.keystoreReset()); 170 assertTrue(mSupport.keystoreSetPassword("newpasswd")); 171 172 String intermediate = "-intermediate"; 173 String root = "-root"; 174 175 String alias1 = "client"; 176 String alias1Intermediate = alias1 + intermediate; 177 String alias1Root = alias1 + root; 178 String alias1Pkey = (Credentials.USER_PRIVATE_KEY + alias1); 179 String alias1Cert = (Credentials.USER_CERTIFICATE + alias1); 180 String alias1ICert = (Credentials.CA_CERTIFICATE + alias1Intermediate); 181 String alias1RCert = (Credentials.CA_CERTIFICATE + alias1Root); 182 PrivateKeyEntry pke1 = TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA"); 183 Certificate intermediate1 = pke1.getCertificateChain()[1]; 184 Certificate root1 = TestKeyStore.getClientCertificate().getRootCertificate("RSA"); 185 186 final String alias2 = "server"; 187 String alias2Intermediate = alias2 + intermediate; 188 String alias2Root = alias2 + root; 189 String alias2Pkey = (Credentials.USER_PRIVATE_KEY + alias2); 190 String alias2Cert = (Credentials.USER_CERTIFICATE + alias2); 191 String alias2ICert = (Credentials.CA_CERTIFICATE + alias2Intermediate); 192 String alias2RCert = (Credentials.CA_CERTIFICATE + alias2Root); 193 PrivateKeyEntry pke2 = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 194 Certificate intermediate2 = pke2.getCertificateChain()[1]; 195 Certificate root2 = TestKeyStore.getServer().getRootCertificate("RSA"); 196 197 assertTrue(mSupport.keystoreImportKey(alias1Pkey, 198 pke1.getPrivateKey().getEncoded())); 199 assertTrue(mSupport.keystorePut(alias1Cert, 200 Credentials.convertToPem(pke1.getCertificate()))); 201 assertTrue(mSupport.keystorePut(alias1ICert, 202 Credentials.convertToPem(intermediate1))); 203 assertTrue(mSupport.keystorePut(alias1RCert, 204 Credentials.convertToPem(root1))); 205 assertTrue(mSupport.keystoreImportKey(alias2Pkey, 206 pke2.getPrivateKey().getEncoded())); 207 assertTrue(mSupport.keystorePut(alias2Cert, 208 Credentials.convertToPem(pke2.getCertificate()))); 209 assertTrue(mSupport.keystorePut(alias2ICert, 210 Credentials.convertToPem(intermediate2))); 211 assertTrue(mSupport.keystorePut(alias2RCert, 212 Credentials.convertToPem(root2))); 213 214 assertEquals(KeyStore.State.UNLOCKED, keyStore.state()); 215 216 Log.d(TAG, "test_KeyChainService bind service"); 217 bindService(); 218 assertTrue(mIsBoundService); 219 synchronized (mServiceLock) { 220 if (mService == null) { 221 mServiceLock.wait(10 * 1000); 222 } 223 } 224 assertNotNull(mService); 225 226 mSupport.grantAppPermission(getApplicationInfo().uid, alias1); 227 // don't grant alias2, so it can be done manually with KeyChainTestActivity 228 Log.d(TAG, "test_KeyChainService positive testing"); 229 assertNotNull("Requesting private key should succeed", 230 mService.requestPrivateKey(alias1)); 231 232 byte[] certificate = mService.getCertificate(alias1); 233 assertNotNull(certificate); 234 assertEquals(Arrays.toString(Credentials.convertToPem(pke1.getCertificate())), 235 Arrays.toString(certificate)); 236 237 Log.d(TAG, "test_KeyChainService negative testing"); 238 mSupport.revokeAppPermission(getApplicationInfo().uid, alias2); 239 try { 240 mService.requestPrivateKey(alias2); 241 fail(); 242 } catch (IllegalStateException expected) { 243 } 244 245 try { 246 mService.getCertificate(alias2); 247 fail(); 248 } catch (IllegalStateException expected) { 249 } 250 251 Log.d(TAG, "test_KeyChainService unbind"); 252 unbindServices(); 253 assertFalse(mIsBoundSupport); 254 assertFalse(mIsBoundService); 255 256 Log.d(TAG, "test_KeyChainService end"); 257 } 258 } 259 } 260