• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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