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