KeyChainServiceTest.java revision 3e6251dedc92654476c70bdc413f24a4b31ce6a4
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
17package com.android.keychain.tests;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.AccountManagerFuture;
22import android.app.Service;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.security.Credentials;
30import android.security.IKeyChainService;
31import android.security.KeyChain;
32import android.security.KeyStore;
33import android.util.Log;
34import com.android.keychain.tests.support.IKeyChainServiceTestSupport;
35import java.security.KeyStore.PrivateKeyEntry;
36import java.security.cert.Certificate;
37import java.util.Arrays;
38import junit.framework.Assert;
39import libcore.java.security.TestKeyStore;
40
41public class KeyChainServiceTest extends Service {
42
43    private static final String TAG = "KeyChainServiceTest";
44
45    private final Object mSupportLock = new Object();
46    private IKeyChainServiceTestSupport mSupport;
47    private boolean mIsBoundSupport;
48
49    private final Object mServiceLock = new Object();
50    private IKeyChainService mService;
51    private boolean mIsBoundService;
52
53    private ServiceConnection mSupportConnection = new ServiceConnection() {
54        @Override public void onServiceConnected(ComponentName name, IBinder service) {
55            synchronized (mSupportLock) {
56                mSupport = IKeyChainServiceTestSupport.Stub.asInterface(service);
57                mSupportLock.notifyAll();
58            }
59        }
60
61        @Override public void onServiceDisconnected(ComponentName name) {
62            synchronized (mSupportLock) {
63                mSupport = null;
64            }
65        }
66    };
67
68    private ServiceConnection mServiceConnection = new ServiceConnection() {
69        @Override public void onServiceConnected(ComponentName name, IBinder service) {
70            synchronized (mServiceLock) {
71                mService = IKeyChainService.Stub.asInterface(service);
72                mServiceLock.notifyAll();
73            }
74        }
75
76        @Override public void onServiceDisconnected(ComponentName name) {
77            synchronized (mServiceLock) {
78                mService = null;
79            }
80        }
81    };
82
83    private void bindSupport() {
84        mIsBoundSupport = bindService(new Intent(IKeyChainServiceTestSupport.class.getName()),
85                                      mSupportConnection,
86                                      Context.BIND_AUTO_CREATE);
87    }
88
89    private void bindService() {
90        mIsBoundService = bindService(new Intent(IKeyChainService.class.getName()),
91                                      mServiceConnection,
92                                      Context.BIND_AUTO_CREATE);
93    }
94
95    private void unbindServices() {
96        if (mIsBoundSupport) {
97            unbindService(mSupportConnection);
98            mIsBoundSupport = false;
99        }
100        if (mIsBoundService) {
101            unbindService(mServiceConnection);
102            mIsBoundService = false;
103        }
104    }
105
106    @Override public IBinder onBind(Intent intent) {
107        Log.d(TAG, "onBind");
108        return null;
109    }
110
111    @Override public int onStartCommand(Intent intent, int flags, int startId) {
112        Log.d(TAG, "onStartCommand");
113        new Thread(new Test(), TAG).start();
114        return START_STICKY;
115    }
116
117    @Override public void onDestroy () {
118        Log.d(TAG, "onDestroy");
119        unbindServices();
120    }
121
122    private final class Test extends Assert implements Runnable {
123
124        @Override public void run() {
125            try {
126                test_KeyChainService();
127            } catch (RuntimeException e) {
128                // rethrow RuntimeException without wrapping
129                throw e;
130            } catch (Exception e) {
131                throw new RuntimeException(e);
132            } finally {
133                stopSelf();
134            }
135        }
136
137        public void test_KeyChainService() throws Exception {
138            Log.d(TAG, "test_KeyChainService uid=" + getApplicationInfo().uid);
139
140            Log.d(TAG, "test_KeyChainService bind support");
141            bindSupport();
142            assertTrue(mIsBoundSupport);
143            synchronized (mSupportLock) {
144                if (mSupport == null) {
145                    mSupportLock.wait(10 * 1000);
146                }
147            }
148            assertNotNull(mSupport);
149
150            Log.d(TAG, "test_KeyChainService setup keystore and AccountManager");
151            KeyStore keyStore = KeyStore.getInstance();
152            assertTrue(mSupport.keystoreReset());
153            assertTrue(mSupport.keystorePassword("ignored", "newpasswd"));
154
155            String intermediate = "-intermediate";
156            String root = "-root";
157
158            String alias1 = "client";
159            String alias1Intermediate = alias1 + intermediate;
160            String alias1Root = alias1 + root;
161            byte[] alias1Pkey = (Credentials.USER_PRIVATE_KEY + alias1).getBytes();
162            byte[] alias1Cert = (Credentials.USER_CERTIFICATE + alias1).getBytes();
163            byte[] alias1ICert = (Credentials.CA_CERTIFICATE + alias1Intermediate).getBytes();
164            byte[] alias1RCert = (Credentials.CA_CERTIFICATE + alias1Root).getBytes();
165            PrivateKeyEntry pke1 = TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
166            Certificate intermediate1 = pke1.getCertificateChain()[1];
167            Certificate root1 = TestKeyStore.getClientCertificate().getRootCertificate("RSA");
168
169            final String alias2 = "server";
170            String alias2Intermediate = alias2 + intermediate;
171            String alias2Root = alias2 + root;
172            byte[] alias2Pkey = (Credentials.USER_PRIVATE_KEY + alias2).getBytes();
173            byte[] alias2Cert = (Credentials.USER_CERTIFICATE + alias2).getBytes();
174            byte[] alias2ICert = (Credentials.CA_CERTIFICATE + alias2Intermediate).getBytes();
175            byte[] alias2RCert = (Credentials.CA_CERTIFICATE + alias2Root).getBytes();
176            PrivateKeyEntry pke2 = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
177            Certificate intermediate2 = pke2.getCertificateChain()[1];
178            Certificate root2 = TestKeyStore.getServer().getRootCertificate("RSA");
179
180            assertTrue(mSupport.keystorePut(alias1Pkey, pke1.getPrivateKey().getEncoded()));
181            assertTrue(mSupport.keystorePut(alias1Cert, pke1.getCertificate().getEncoded()));
182            assertTrue(mSupport.keystorePut(alias1ICert, intermediate1.getEncoded()));
183            assertTrue(mSupport.keystorePut(alias1RCert, root1.getEncoded()));
184            assertTrue(mSupport.keystorePut(alias2Pkey, pke2.getPrivateKey().getEncoded()));
185            assertTrue(mSupport.keystorePut(alias2Cert, pke2.getCertificate().getEncoded()));
186            assertTrue(mSupport.keystorePut(alias2ICert, intermediate2.getEncoded()));
187            assertTrue(mSupport.keystorePut(alias2RCert, root2.getEncoded()));
188
189            assertEquals(KeyStore.NO_ERROR, keyStore.test());
190            AccountManager accountManager = AccountManager.get(KeyChainServiceTest.this);
191            assertNotNull(accountManager);
192            for (Account account : accountManager.getAccountsByType(KeyChain.ACCOUNT_TYPE)) {
193                mSupport.revokeAppPermission(account, alias1, getApplicationInfo().uid);
194                mSupport.revokeAppPermission(
195                        account, alias1Intermediate + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
196                mSupport.revokeAppPermission(
197                        account, alias1Root + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
198                mSupport.revokeAppPermission(account, alias2, getApplicationInfo().uid);
199                mSupport.revokeAppPermission(
200                        account, alias2Intermediate + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
201                mSupport.revokeAppPermission(
202                        account, alias2Root + KeyChain.CA_SUFFIX, getApplicationInfo().uid);
203            }
204
205            Log.d(TAG, "test_KeyChainService bind service");
206            bindService();
207            assertTrue(mIsBoundService);
208            synchronized (mServiceLock) {
209                if (mService == null) {
210                    mServiceLock.wait(10 * 1000);
211                }
212            }
213            assertNotNull(mService);
214
215            Account[] accounts = accountManager.getAccountsByType(KeyChain.ACCOUNT_TYPE);
216            assertNotNull(accounts);
217            assertEquals(1, accounts.length);
218            Account account = accounts[0];
219            Log.d(TAG, "test_KeyChainService getAuthTokenByFeatures for Intent");
220            AccountManagerFuture<Bundle> accountManagerFutureFail
221                    = accountManager.getAuthToken(account, alias1, false, null, null);
222            Bundle bundleFail = accountManagerFutureFail.getResult();
223            assertNotNull(bundleFail);
224            Object intentObject = bundleFail.get(AccountManager.KEY_INTENT);
225            assertNotNull(intentObject);
226            assertTrue(Intent.class.isAssignableFrom(intentObject.getClass()));
227            Intent intent = (Intent) intentObject;
228            assertEquals("android",
229                         intent.getComponent().getPackageName());
230            assertEquals("android.accounts.GrantCredentialsPermissionActivity",
231                         intent.getComponent().getClassName());
232
233            mSupport.grantAppPermission(account, alias1, getApplicationInfo().uid);
234            // don't grant alias2, so it can be done manually with KeyChainTestActivity
235            Log.d(TAG, "test_KeyChainService getAuthTokenByFeatures for authtoken");
236            AccountManagerFuture<Bundle> accountManagerFuture
237                    = accountManager.getAuthToken(account, alias1, false, null, null);
238            Bundle bundle = accountManagerFuture.getResult();
239            String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
240            assertNotNull(accountName);
241            String accountType = bundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
242            assertEquals(KeyChain.ACCOUNT_TYPE, accountType);
243            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
244            assertNotNull(authToken);
245            assertFalse(authToken.isEmpty());
246
247            byte[] privateKey = mService.getPrivate(alias1, authToken);
248            assertNotNull(privateKey);
249            assertEquals(Arrays.toString(pke1.getPrivateKey().getEncoded()),
250                         Arrays.toString(privateKey));
251
252            byte[] certificate = mService.getCertificate(alias1, authToken);
253            assertNotNull(certificate);
254            assertEquals(Arrays.toString(pke1.getCertificate().getEncoded()),
255                         Arrays.toString(certificate));
256
257            String aliasI = mService.findIssuer(KeyChain.fromCertificate(pke1.getCertificate()));
258            assertNotNull(aliasI);
259            assertEquals(alias1Intermediate, aliasI);
260
261            String aliasR = mService.findIssuer(KeyChain.fromCertificate(intermediate1));
262            assertNotNull(aliasR);
263            assertEquals(alias1Root, aliasR);
264
265            String aliasRR = mService.findIssuer(KeyChain.fromCertificate(intermediate1));
266            assertNotNull(aliasRR);
267            assertEquals(alias1Root, aliasRR);
268
269            try {
270                mService.findIssuer(new Bundle());
271                fail();
272            } catch (IllegalArgumentException expected) {
273            }
274            try {
275                mService.findIssuer(null);
276                fail();
277            } catch (NullPointerException expected) {
278            }
279
280            Log.d(TAG, "test_KeyChainService unbind");
281            unbindServices();
282            assertFalse(mIsBoundSupport);
283            assertFalse(mIsBoundService);
284
285            Log.d(TAG, "test_KeyChainService end");
286        }
287    }
288}
289